remove #[async_trait] from many many places

Compile time got improved by 77% (including last commit)
This commit is contained in:
Alexander Medvedev 2025-12-04 13:57:03 +01:00
parent 71392b4a38
commit ae751f3a83
205 changed files with 11331 additions and 9627 deletions

View File

@ -86,54 +86,39 @@ pub struct PropertyStruct {
impl ToTokens for PropertyStruct { impl ToTokens for PropertyStruct {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
if self.values == vec!["true".to_string(), "false".to_string()] { if self.values[0] == "true" && self.values[1] == "false" {
// For boolean properties, we'll use Rust's built-in bool type
return; return;
} }
let name = Ident::new(&self.name, Span::call_site()); let name = Ident::new(&self.name, Span::call_site());
let count = self.values.len();
let variant_count = count as u16;
let variant_count = self.values.clone().len() as u16; let is_number_values = !self.values.is_empty()
let values_index = (0..self.values.clone().len() as u16).collect::<Vec<_>>(); && self.values.iter().all(|v| v.starts_with('L'))
&& self.values.iter().any(|v| v == "L1");
let ident_values = self let mut variants = Vec::with_capacity(count);
.values let mut literals = Vec::with_capacity(count);
.iter() let mut indices = Vec::with_capacity(count);
.map(|value| Ident::new(&(value).to_upper_camel_case(), Span::call_site()));
let values_2 = ident_values.clone(); for (i, raw_value) in self.values.iter().enumerate() {
let values_3 = ident_values.clone(); let ident = Ident::new(&raw_value.to_upper_camel_case(), Span::call_site());
let is_number_values = let literal_str = if is_number_values {
self.values.iter().all(|v| v.starts_with("L")) && self.values.iter().any(|v| v == "L1"); raw_value.strip_prefix('L').unwrap_or(raw_value)
let from_values = self.values.iter().map(|value| {
let ident = Ident::new(&(value).to_upper_camel_case(), Span::call_site());
let value = if is_number_values {
value.strip_prefix("L").unwrap()
} else { } else {
value raw_value.as_str()
}; };
quote! {
#value => Self::#ident
}
});
let to_values = self.values.iter().map(|value| {
let ident = Ident::new(&(value).to_upper_camel_case(), Span::call_site());
let value = if is_number_values {
value.strip_prefix("L").unwrap()
} else {
value
};
quote! {
Self::#ident => #value
}
});
variants.push(ident);
literals.push(literal_str);
indices.push(i as u16);
}
tokens.extend(quote! { tokens.extend(quote! {
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum #name { pub enum #name {
#(#ident_values),* #(#variants),*
} }
impl EnumVariants for #name { impl EnumVariants for #name {
@ -143,30 +128,29 @@ impl ToTokens for PropertyStruct {
fn to_index(&self) -> u16 { fn to_index(&self) -> u16 {
match self { match self {
#(Self::#values_2 => #values_index),* #(Self::#variants => #indices),*
} }
} }
fn from_index(index: u16) -> Self { fn from_index(index: u16) -> Self {
match index { match index {
#(#values_index => Self::#values_3,)* #(#indices => Self::#variants,)*
_ => panic!("Invalid index: {index}"), _ => panic!("Invalid index: {index}"),
} }
} }
fn to_value(&self) -> &'static str { fn to_value(&self) -> &'static str {
match self { match self {
#(#to_values),* #(Self::#variants => #literals),*
} }
} }
fn from_value(value: &str) -> Self { fn from_value(value: &str) -> Self {
match value { match value {
#(#from_values),*, #(#literals => Self::#variants),*,
_ => panic!("Invalid value: {value}"), _ => panic!("Invalid value: {value}"),
} }
} }
} }
}); });
} }

View File

@ -1,5 +1,5 @@
use std::any::Any;
use std::sync::Arc; use std::sync::Arc;
use std::{any::Any, pin::Pin};
use async_trait::async_trait; use async_trait::async_trait;
use pumpkin_world::{inventory::split_stack, item::ItemStack}; use pumpkin_world::{inventory::split_stack, item::ItemStack};
@ -82,11 +82,12 @@ impl RecipeInputInventory for CraftingInventory {
} }
} }
#[async_trait]
impl Clearable for CraftingInventory { impl Clearable for CraftingInventory {
async fn clear(&self) { fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
for slot in self.items.iter() { Box::pin(async move {
*slot.lock().await = ItemStack::EMPTY.clone(); for item in self.items.iter() {
} *item.lock().await = ItemStack::EMPTY.clone();
}
})
} }
} }

View File

@ -1,4 +1,5 @@
use std::any::Any; use std::any::Any;
use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicU8, Ordering}; use std::sync::atomic::{AtomicU8, Ordering};
@ -6,7 +7,8 @@ use super::recipes::{RecipeFinderScreenHandler, RecipeInputInventory};
use crate::crafting::crafting_inventory::CraftingInventory; use crate::crafting::crafting_inventory::CraftingInventory;
use crate::player::player_inventory::PlayerInventory; use crate::player::player_inventory::PlayerInventory;
use crate::screen_handler::{ use crate::screen_handler::{
InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerListener, InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture,
ScreenHandlerListener,
}; };
use crate::slot::{NormalSlot, Slot}; use crate::slot::{NormalSlot, Slot};
use async_trait::async_trait; use async_trait::async_trait;
@ -406,48 +408,54 @@ impl Slot for ResultSlot {
} }
} }
#[async_trait]
impl ScreenHandlerListener for ResultSlot { impl ScreenHandlerListener for ResultSlot {
async fn on_slot_update( fn on_slot_update<'a>(
&self, &'a self,
screen_handler: &ScreenHandlerBehaviour, screen_handler: &'a ScreenHandlerBehaviour,
slot: u8, slot: u8,
_stack: ItemStack, _stack: ItemStack,
) { ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
if (0..=(self.inventory.get_width() * self.inventory.get_height())) Box::pin(async move {
.contains(&(slot as usize)) if (0..=(self.inventory.get_width() * self.inventory.get_height()))
{ .contains(&(slot as usize))
let result = self.refill_output().await; {
let result = self.refill_output().await;
let next_revision = screen_handler.next_revision(); let next_revision = screen_handler.next_revision();
if let Some(sync_handler) = screen_handler.sync_handler.as_ref() { if let Some(sync_handler) = screen_handler.sync_handler.as_ref() {
sync_handler sync_handler
.update_slot(screen_handler, 0, &result, next_revision) .update_slot(screen_handler, 0, &result, next_revision)
.await; .await;
}
} }
} })
} }
} }
// AbstractCraftingScreenHandler.java // AbstractCraftingScreenHandler.java
#[async_trait]
pub trait CraftingScreenHandler<I: RecipeInputInventory>: pub trait CraftingScreenHandler<I: RecipeInputInventory>:
RecipeFinderScreenHandler + ScreenHandler RecipeFinderScreenHandler + ScreenHandler
{ {
async fn add_recipe_slots(&mut self, crafing_inventory: Arc<dyn RecipeInputInventory>) { fn add_recipe_slots<'a>(
let result_slot = Arc::new(ResultSlot::new(crafing_inventory.clone())); &'a mut self,
self.add_slot(result_slot.clone()); crafing_inventory: Arc<dyn RecipeInputInventory>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
let result_slot = Arc::new(ResultSlot::new(crafing_inventory.clone()));
self.add_slot(result_slot.clone());
let width = crafing_inventory.get_width(); let width = crafing_inventory.get_width();
let height = crafing_inventory.get_height(); let height = crafing_inventory.get_height();
for i in 0..width { for i in 0..width {
for j in 0..height { for j in 0..height {
let input_slot = NormalSlot::new(crafing_inventory.clone(), j + i * width); // Assuming j + i * width is the correct slot index calculation
self.add_slot(Arc::new(input_slot)); let input_slot = NormalSlot::new(crafing_inventory.clone(), j + i * width);
self.add_slot(Arc::new(input_slot));
}
} }
}
self.add_listener(result_slot).await; self.add_listener(result_slot).await;
})
} }
} }
@ -481,13 +489,14 @@ impl CraftingTableScreenHandler {
impl RecipeFinderScreenHandler for CraftingTableScreenHandler {} impl RecipeFinderScreenHandler for CraftingTableScreenHandler {}
#[async_trait]
impl ScreenHandler for CraftingTableScreenHandler { impl ScreenHandler for CraftingTableScreenHandler {
async fn on_closed(&mut self, player: &dyn InventoryPlayer) { fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
self.default_on_closed(player).await; Box::pin(async move {
//TODO: this.craftingResultInventory.clear(); self.default_on_closed(player).await;
self.drop_inventory(player, self.crafting_inventory.clone()) //TODO: this.craftingResultInventory.clear();
.await; self.drop_inventory(player, self.crafting_inventory.clone())
.await;
})
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
@ -502,77 +511,83 @@ impl ScreenHandler for CraftingTableScreenHandler {
&mut self.behaviour &mut self.behaviour
} }
async fn quick_move(&mut self, player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { fn quick_move<'a>(
let slot = self.get_behaviour().slots[slot_index as usize].clone(); &'a mut self,
player: &'a dyn InventoryPlayer,
slot_index: i32,
) -> ItemStackFuture<'a> {
Box::pin(async move {
let slot = self.get_behaviour().slots[slot_index as usize].clone();
if slot.has_stack().await { if slot.has_stack().await {
let slot_stack = slot.get_stack().await; let slot_stack = slot.get_stack().await;
let mut slot_stack = slot_stack.lock().await; let mut slot_stack = slot_stack.lock().await;
let stack_prev = slot_stack.clone(); let stack_prev = slot_stack.clone();
if slot_index == 0 { if slot_index == 0 {
// From crafting result slot - move to player inventory (slots 10-46) // From crafting result slot - move to player inventory (slots 10-46)
if !self.insert_item(&mut slot_stack, 10, 46, true).await { if !self.insert_item(&mut slot_stack, 10, 46, true).await {
return ItemStack::EMPTY.clone(); return ItemStack::EMPTY.clone();
} }
} else if (1..=9).contains(&slot_index) { } else if (1..=9).contains(&slot_index) {
// From crafting input slots - try to move to player inventory (slots 10-46) // From crafting input slots - try to move to player inventory (slots 10-46)
if !self.insert_item(&mut slot_stack, 10, 46, false).await { if !self.insert_item(&mut slot_stack, 10, 46, false).await {
return ItemStack::EMPTY.clone(); return ItemStack::EMPTY.clone();
} }
} else if (10..46).contains(&slot_index) { } else if (10..46).contains(&slot_index) {
// From player inventory - try to move to crafting input slots first (1-9) // From player inventory - try to move to crafting input slots first (1-9)
if !self.insert_item(&mut slot_stack, 1, 10, false).await { if !self.insert_item(&mut slot_stack, 1, 10, false).await {
// If that fails, try moving within player inventory // If that fails, try moving within player inventory
if slot_index < 37 { if slot_index < 37 {
// From main inventory to hotbar // From main inventory to hotbar
if !self.insert_item(&mut slot_stack, 37, 46, false).await { if !self.insert_item(&mut slot_stack, 37, 46, false).await {
return ItemStack::EMPTY.clone(); return ItemStack::EMPTY.clone();
} }
} else { } else {
// From hotbar to main inventory // From hotbar to main inventory
if !self.insert_item(&mut slot_stack, 10, 37, false).await { if !self.insert_item(&mut slot_stack, 10, 37, false).await {
return ItemStack::EMPTY.clone(); return ItemStack::EMPTY.clone();
}
} }
} }
} else {
// Any other slot - try to move to player inventory
if !self.insert_item(&mut slot_stack, 10, 46, false).await {
return ItemStack::EMPTY.clone();
}
} }
} else {
// Any other slot - try to move to player inventory let stack = slot_stack.clone();
if !self.insert_item(&mut slot_stack, 10, 46, false).await { drop(slot_stack); // release the lock before calling other methods
if stack.is_empty() {
slot.set_stack_prev(ItemStack::EMPTY.clone(), stack_prev.clone())
.await;
} else {
slot.mark_dirty().await;
}
if stack.item_count == stack_prev.item_count {
// Nothing changed
return ItemStack::EMPTY.clone(); return ItemStack::EMPTY.clone();
} }
}
let stack = slot_stack.clone(); slot.on_take_item(player, &stack).await;
drop(slot_stack); // release the lock before calling other methods
if stack.is_empty() { if slot_index == 0 {
slot.set_stack_prev(ItemStack::EMPTY.clone(), stack_prev.clone()) slot.on_quick_move_crafted(stack.clone(), stack_prev.clone())
.await; .await;
} else { // For crafting result slot, drop any remaining items
slot.mark_dirty().await; if !stack.is_empty() {
} player.drop_item(stack, false).await;
}
if stack.item_count == stack_prev.item_count {
// Nothing changed
return ItemStack::EMPTY.clone();
}
slot.on_take_item(player, &stack).await;
if slot_index == 0 {
slot.on_quick_move_crafted(stack.clone(), stack_prev.clone())
.await;
// For crafting result slot, drop any remaining items
if !stack.is_empty() {
player.drop_item(stack, false).await;
} }
return stack_prev;
} }
return stack_prev; ItemStack::EMPTY.clone()
} })
ItemStack::EMPTY.clone()
} }
} }

View File

@ -1,4 +1,4 @@
use std::{any::Any, sync::Arc}; use std::{any::Any, pin::Pin, sync::Arc};
use async_trait::async_trait; use async_trait::async_trait;
use pumpkin_world::{ use pumpkin_world::{
@ -96,10 +96,11 @@ impl Inventory for DoubleInventory {
} }
} }
#[async_trait]
impl Clearable for DoubleInventory { impl Clearable for DoubleInventory {
async fn clear(&self) { fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
self.first.clear().await; Box::pin(async move {
self.second.clear().await; self.first.clear().await;
self.second.clear().await;
})
} }
} }

View File

@ -1,14 +1,13 @@
use std::{any::Any, sync::Arc}; use std::{any::Any, pin::Pin, sync::Arc};
use async_trait::async_trait;
use pumpkin_data::fuels::is_fuel; use pumpkin_data::fuels::is_fuel;
use pumpkin_world::{block::entities::BlockEntity, inventory::Inventory, item::ItemStack}; use pumpkin_world::{block::entities::BlockEntity, inventory::Inventory, item::ItemStack};
use crate::{ use crate::{
player::player_inventory::PlayerInventory, player::player_inventory::PlayerInventory,
screen_handler::{ screen_handler::{
InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerListener, InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour,
ScreenProperty, ScreenHandlerFuture, ScreenHandlerListener, ScreenProperty,
}, },
}; };
@ -36,19 +35,20 @@ impl FurnaceScreenHandler {
}; };
struct FurnaceScreenListener; struct FurnaceScreenListener;
#[async_trait]
impl ScreenHandlerListener for FurnaceScreenListener { impl ScreenHandlerListener for FurnaceScreenListener {
async fn on_property_update( fn on_property_update<'a>(
&self, &'a self,
screen_handler: &ScreenHandlerBehaviour, screen_handler: &'a ScreenHandlerBehaviour,
property: u8, property: u8,
value: i32, value: i32,
) { ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
if let Some(sync_handler) = screen_handler.sync_handler.as_ref() { Box::pin(async move {
sync_handler if let Some(sync_handler) = screen_handler.sync_handler.as_ref() {
.update_property(screen_handler, property as i32, value) sync_handler
.await; .update_property(screen_handler, property as i32, value)
} .await;
}
})
} }
} }
@ -84,11 +84,12 @@ impl FurnaceScreenHandler {
} }
} }
#[async_trait]
impl ScreenHandler for FurnaceScreenHandler { impl ScreenHandler for FurnaceScreenHandler {
async fn on_closed(&mut self, player: &dyn InventoryPlayer) { fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
self.default_on_closed(player).await; Box::pin(async move {
//TODO: self.inventory.on_closed(player).await; self.default_on_closed(player).await;
// TODO: self.inventory.on_closed(player).await;
})
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
@ -103,40 +104,55 @@ impl ScreenHandler for FurnaceScreenHandler {
&mut self.behaviour &mut self.behaviour
} }
async fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { fn quick_move<'a>(
const FUEL_SLOT: i32 = 1; &'a mut self,
_player: &'a dyn InventoryPlayer,
slot_index: i32,
) -> ItemStackFuture<'a> {
Box::pin(async move {
const FUEL_SLOT: i32 = 1; // Note: Slots 0, 1, 2 are Furnace slots.
let mut stack_left = ItemStack::EMPTY.clone(); let mut stack_left = ItemStack::EMPTY.clone();
let slot = self.get_behaviour().slots[slot_index as usize].clone();
if !slot.has_stack().await { let slot = self.get_behaviour().slots[slot_index as usize].clone();
return stack_left;
}
let slot_stack = slot.get_stack().await; if !slot.has_stack().await {
let mut stack = slot_stack.lock().await; return stack_left;
stack_left = stack.clone(); }
let success = if slot_index < 3 { let slot_stack_lock = slot.get_stack().await;
self.insert_item(&mut stack, 3, self.get_behaviour().slots.len() as i32, true)
.await
} else if is_fuel(stack.item.id) {
self.insert_item(&mut stack, FUEL_SLOT, 3, false).await
} else {
self.insert_item(&mut stack, 0, 3, false).await
};
if !success { // Acquire the lock to read/clone the stack
return ItemStack::EMPTY.clone(); let mut stack = slot_stack_lock.lock().await;
} stack_left = stack.clone();
if stack.is_empty() { let success = if slot_index < 3 {
drop(stack); // If clicked slot is one of the Furnace slots (0, 1, 2):
slot.set_stack(ItemStack::EMPTY.clone()).await; // Try to move to player inventory (slots 3 onwards, starting from the end)
} else { self.insert_item(&mut stack, 3, self.get_behaviour().slots.len() as i32, true)
slot.mark_dirty().await; .await
} } else if is_fuel(stack.item.id) {
// If clicked slot is in the player inventory (3+) and contains fuel:
// Try to move to the Furnace's Fuel slot (slot 1)
self.insert_item(&mut stack, FUEL_SLOT, 3, false).await
} else {
// If clicked slot is in the player inventory (3+) and NOT fuel (must be a smeltable item):
// Try to move to the Furnace's Input/Smelting slot (slot 0)
self.insert_item(&mut stack, 0, 3, false).await
};
stack_left if !success {
return ItemStack::EMPTY.clone();
}
if stack.is_empty() {
drop(stack); // Release lock before awaiting
slot.set_stack(ItemStack::EMPTY.clone()).await;
} else {
slot.mark_dirty().await;
}
stack_left
})
} }
} }

View File

@ -1,12 +1,14 @@
use std::{any::Any, sync::Arc}; use std::{any::Any, sync::Arc};
use async_trait::async_trait;
use pumpkin_data::screen::WindowType; use pumpkin_data::screen::WindowType;
use pumpkin_world::{inventory::Inventory, item::ItemStack}; use pumpkin_world::{inventory::Inventory, item::ItemStack};
use crate::{ use crate::{
player::player_inventory::PlayerInventory, player::player_inventory::PlayerInventory,
screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour}, screen_handler::{
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour,
ScreenHandlerFuture,
},
slot::NormalSlot, slot::NormalSlot,
}; };
@ -119,11 +121,12 @@ impl GenericContainerScreenHandler {
} }
} }
#[async_trait]
impl ScreenHandler for GenericContainerScreenHandler { impl ScreenHandler for GenericContainerScreenHandler {
async fn on_closed(&mut self, player: &dyn InventoryPlayer) { fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
self.default_on_closed(player).await; Box::pin(async move {
self.inventory.on_close().await; self.default_on_closed(player).await;
self.inventory.on_close().await;
})
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
@ -138,45 +141,58 @@ impl ScreenHandler for GenericContainerScreenHandler {
&mut self.behaviour &mut self.behaviour
} }
async fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { fn quick_move<'a>(
let mut stack_left = ItemStack::EMPTY.clone(); &'a mut self,
let slot = self.get_behaviour().slots[slot_index as usize].clone(); _player: &'a dyn InventoryPlayer,
slot_index: i32,
) -> ItemStackFuture<'a> {
Box::pin(async move {
let mut stack_left = ItemStack::EMPTY.clone();
// Assuming bounds check passed for slot_index by caller or within quick_move spec
let slot = self.get_behaviour().slots[slot_index as usize].clone();
if slot.has_stack().await { if slot.has_stack().await {
let slot_stack = slot.get_stack().await; let slot_stack_lock = slot.get_stack().await;
stack_left = slot_stack.lock().await.clone(); let slot_stack_guard = slot_stack_lock.lock().await;
stack_left = slot_stack_guard.clone();
// Release the guard before calling insert_item which needs its own lock
drop(slot_stack_guard);
if slot_index < (self.rows * 9) as i32 { // Re-acquire lock for insert_item (which expects &mut ItemStack)
if !self let mut slot_stack_mut = slot_stack_lock.lock().await;
.insert_item(
&mut *slot_stack.lock().await, if slot_index < (self.rows * 9) as i32 {
(self.rows * 9).into(), // Move from inventory to player area (end)
self.get_behaviour().slots.len() as i32, if !self
true, .insert_item(
) &mut slot_stack_mut,
(self.rows * 9).into(),
self.get_behaviour().slots.len() as i32,
true,
)
.await
{
return ItemStack::EMPTY.clone();
}
} else if !self
.insert_item(&mut slot_stack_mut, 0, (self.rows * 9).into(), false)
.await .await
{ {
// Move from player area to inventory (start)
return ItemStack::EMPTY.clone(); return ItemStack::EMPTY.clone();
} }
} else if !self
.insert_item( // Check the resulting state of the slot stack after insert_item
&mut *slot_stack.lock().await, if slot_stack_mut.is_empty() {
0, drop(slot_stack_mut); // Release lock
(self.rows * 9).into(), slot.set_stack(ItemStack::EMPTY.clone()).await;
false, } else {
) drop(slot_stack_mut); // Release lock
.await slot.mark_dirty().await;
{ }
return ItemStack::EMPTY.clone();
} }
if stack_left.is_empty() { stack_left
slot.set_stack(ItemStack::EMPTY.clone()).await; })
} else {
slot.mark_dirty().await;
}
}
return stack_left;
} }
} }

View File

@ -1,4 +1,4 @@
use std::{any::Any, array::from_fn, sync::Arc}; use std::{any::Any, array::from_fn, pin::Pin, sync::Arc};
use async_trait::async_trait; use async_trait::async_trait;
use pumpkin_world::{ use pumpkin_world::{
@ -100,11 +100,12 @@ impl Inventory for EnderChestInventory {
} }
} }
#[async_trait]
impl Clearable for EnderChestInventory { impl Clearable for EnderChestInventory {
async fn clear(&self) { fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
for slot in self.items.iter() { Box::pin(async move {
*slot.lock().await = ItemStack::EMPTY.clone(); for item in self.items.iter() {
} *item.lock().await = ItemStack::EMPTY.clone();
}
})
} }
} }

View File

@ -10,6 +10,7 @@ use pumpkin_world::item::ItemStack;
use std::any::Any; use std::any::Any;
use std::array::from_fn; use std::array::from_fn;
use std::collections::HashMap; use std::collections::HashMap;
use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicU8, Ordering}; use std::sync::atomic::{AtomicU8, Ordering};
use tokio::sync::Mutex; use tokio::sync::Mutex;
@ -307,14 +308,15 @@ impl PlayerInventory {
} }
} }
#[async_trait]
impl Clearable for PlayerInventory { impl Clearable for PlayerInventory {
async fn clear(&self) { fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
for item in self.main_inventory.iter() { Box::pin(async move {
*item.lock().await = ItemStack::EMPTY.clone(); for item in self.main_inventory.iter() {
} *item.lock().await = ItemStack::EMPTY.clone();
}
self.entity_equipment.lock().await.clear(); self.entity_equipment.lock().await.clear();
})
} }
} }

View File

@ -2,9 +2,10 @@ use super::player_inventory::PlayerInventory;
use crate::crafting::crafting_inventory::CraftingInventory; use crate::crafting::crafting_inventory::CraftingInventory;
use crate::crafting::crafting_screen_handler::CraftingScreenHandler; use crate::crafting::crafting_screen_handler::CraftingScreenHandler;
use crate::crafting::recipes::{RecipeFinderScreenHandler, RecipeInputInventory}; use crate::crafting::recipes::{RecipeFinderScreenHandler, RecipeInputInventory};
use crate::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour}; use crate::screen_handler::{
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture,
};
use crate::slot::{ArmorSlot, NormalSlot, Slot}; use crate::slot::{ArmorSlot, NormalSlot, Slot};
use async_trait::async_trait;
use pumpkin_data::data_component_impl::{EquipmentSlot, EquipmentType, EquippableImpl}; use pumpkin_data::data_component_impl::{EquipmentSlot, EquipmentType, EquippableImpl};
use pumpkin_data::screen::WindowType; use pumpkin_data::screen::WindowType;
use pumpkin_world::inventory::Inventory; use pumpkin_world::inventory::Inventory;
@ -75,13 +76,14 @@ impl PlayerScreenHandler {
} }
} }
#[async_trait]
impl ScreenHandler for PlayerScreenHandler { impl ScreenHandler for PlayerScreenHandler {
async fn on_closed(&mut self, player: &dyn InventoryPlayer) { fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
self.default_on_closed(player).await; Box::pin(async move {
//TODO: this.craftingResultInventory.clear(); self.default_on_closed(player).await;
self.drop_inventory(player, self.crafting_inventory.clone()) //TODO: this.craftingResultInventory.clear();
.await; self.drop_inventory(player, self.crafting_inventory.clone())
.await;
})
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
@ -99,114 +101,118 @@ impl ScreenHandler for PlayerScreenHandler {
/// Do quick move (Shift + Click) for the given slot index. /// Do quick move (Shift + Click) for the given slot index.
/// ///
/// Returns the moved stack if successful, or `ItemStack::EMPTY` if nothing changed. /// Returns the moved stack if successful, or `ItemStack::EMPTY` if nothing changed.
async fn quick_move(&mut self, player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { fn quick_move<'a>(
let slot = self.get_behaviour().slots[slot_index as usize].clone(); &'a mut self,
player: &'a dyn InventoryPlayer,
slot_index: i32,
) -> ItemStackFuture<'a> {
Box::pin(async move {
let slot = self.get_behaviour().slots[slot_index as usize].clone();
// TODO: Equippable component // TODO: Equippable component
if slot.has_stack().await { if slot.has_stack().await {
let slot_stack = slot.get_stack().await; let slot_stack_lock = slot.get_stack().await;
let mut slot_stack = slot_stack.lock().await; let mut slot_stack = slot_stack_lock.lock().await;
let stack_prev = slot_stack.clone(); let stack_prev = slot_stack.clone();
let equipment_slot = slot_stack let equipment_slot = slot_stack
.get_data_component::<EquippableImpl>() .get_data_component::<EquippableImpl>()
.map_or(&EquipmentSlot::MAIN_HAND, |equippable| equippable.slot); .map_or(&EquipmentSlot::MAIN_HAND, |equippable| equippable.slot);
#[allow(clippy::if_same_then_else)] // Quick move logic
if slot_index == 0 { #[allow(clippy::if_same_then_else)]
// From crafting result slot let success = if slot_index == 0 {
if !self.insert_item(&mut slot_stack, 9, 45, true).await { // From crafting result slot (0) -> Player Inventory (9-45, from end)
return ItemStack::EMPTY.clone(); self.insert_item(&mut slot_stack, 9, 45, true).await
} } else if (1..5).contains(&slot_index) {
} else if (1..5).contains(&slot_index) { // From craft ingredient slots (1-4) -> Player Inventory (9-45, from start)
// From craft ingredient slots self.insert_item(&mut slot_stack, 9, 45, false).await
if !self.insert_item(&mut slot_stack, 9, 45, false).await { } else if (5..9).contains(&slot_index) {
return ItemStack::EMPTY.clone(); // From armour slots (5-8) -> Player Inventory (9-45, from start)
} let result = self.insert_item(&mut slot_stack, 9, 45, false).await;
} else if (5..9).contains(&slot_index) {
// From armour slots
if !self.insert_item(&mut slot_stack, 9, 45, false).await {
return ItemStack::EMPTY.clone();
}
player if result {
.enqueue_equipment_change(equipment_slot, ItemStack::EMPTY) player
.await; .enqueue_equipment_change(equipment_slot, ItemStack::EMPTY)
} else if equipment_slot.slot_type() == EquipmentType::HumanoidArmor .await;
&& self }
.get_slot((8 - equipment_slot.get_entity_slot_id()) as usize) result
.await } else if equipment_slot.slot_type() == EquipmentType::HumanoidArmor
.get_cloned_stack() && self
.await .get_slot((8 - equipment_slot.get_entity_slot_id()) as usize)
.is_empty() .await
{ .get_cloned_stack()
// Into armour slots .await
let index = 8 - equipment_slot.get_entity_slot_id(); .is_empty()
if !self
.insert_item(&mut slot_stack, index, index + 1, false)
.await
{ {
return ItemStack::EMPTY.clone(); // Into empty armour slots (5-8)
} let index = 8 - equipment_slot.get_entity_slot_id();
let result = self
.insert_item(&mut slot_stack, index, index + 1, false)
.await;
player if result {
.enqueue_equipment_change(equipment_slot, &stack_prev) player
.await; .enqueue_equipment_change(equipment_slot, &stack_prev)
} else if matches!(equipment_slot, EquipmentSlot::OffHand(_)) .await;
&& slot_index != 45 }
&& self.get_slot(45).await.get_cloned_stack().await.is_empty() result
{ } else if matches!(equipment_slot, EquipmentSlot::OffHand(_))
// Into offhand slot && slot_index != 45
let index = 45; && self.get_slot(45).await.get_cloned_stack().await.is_empty()
if !self
.insert_item(&mut slot_stack, index, index + 1, false)
.await
{ {
// Into empty offhand slot (45)
let index = 45;
self.insert_item(&mut slot_stack, index, index + 1, false)
.await
} else if (9..36).contains(&slot_index) {
// From main inventory (9-35) -> Hotbar (36-44)
self.insert_item(&mut slot_stack, 36, 45, false).await
} else if (36..45).contains(&slot_index) {
// From hotbar (36-44) -> Main inventory (9-35)
self.insert_item(&mut slot_stack, 9, 36, false).await
} else {
// Fallback to moving into the player inventory area
self.insert_item(&mut slot_stack, 9, 45, false).await
};
if !success {
return ItemStack::EMPTY.clone(); return ItemStack::EMPTY.clone();
} }
} else if (9..36).contains(&slot_index) {
if !self.insert_item(&mut slot_stack, 36, 45, false).await { let stack = slot_stack.clone();
drop(slot_stack); // release the lock before calling other methods
if stack.is_empty() {
slot.set_stack_prev(ItemStack::EMPTY.clone(), stack_prev.clone())
.await;
} else {
slot.mark_dirty().await;
}
if stack.item_count == stack_prev.item_count {
return ItemStack::EMPTY.clone(); return ItemStack::EMPTY.clone();
} }
} else if (36..45).contains(&slot_index) {
if !self.insert_item(&mut slot_stack, 9, 36, false).await { slot.on_take_item(player, &stack).await;
return ItemStack::EMPTY.clone();
if slot_index == 0 {
// From crafting result slot (0)
// Notify the result slot to refill
slot.on_quick_move_crafted(stack.clone(), stack_prev.clone())
.await;
// For crafting result slot, drop any remaining items
if !stack.is_empty() {
player.drop_item(stack, false).await;
}
} }
} else if !self.insert_item(&mut slot_stack, 9, 45, false).await {
return ItemStack::EMPTY.clone(); return stack_prev;
} }
let stack = slot_stack.clone(); // Nothing changed
drop(slot_stack); // release the lock before calling other methods ItemStack::EMPTY.clone()
if stack.is_empty() { })
slot.set_stack_prev(ItemStack::EMPTY.clone(), stack_prev.clone())
.await;
} else {
slot.mark_dirty().await;
}
if stack.item_count == stack_prev.item_count {
return ItemStack::EMPTY.clone();
}
slot.on_take_item(player, &stack).await;
if slot_index == 0 {
// From crafting result slot
// Notify the result slot to refill
slot.on_quick_move_crafted(stack.clone(), stack_prev.clone())
.await;
// For crafting result slot, drop any remaining items
if !stack.is_empty() {
player.drop_item(stack, false).await;
}
}
return stack_prev;
}
// Nothing changed
ItemStack::EMPTY.clone()
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,13 @@ use async_trait::async_trait;
use pumpkin_data::block_properties::{BarrelLikeProperties, BlockProperties}; use pumpkin_data::block_properties::{BarrelLikeProperties, BlockProperties};
use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::{Block, FacingExt}; use pumpkin_data::{Block, FacingExt};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::random::xoroshiro128::Xoroshiro; use pumpkin_util::random::xoroshiro128::Xoroshiro;
use pumpkin_util::random::{RandomImpl, get_seed}; use pumpkin_util::random::{RandomImpl, get_seed};
use std::any::Any; use std::any::Any;
use std::pin::Pin;
use std::{ use std::{
array::from_fn, array::from_fn,
sync::{ sync::{
@ -16,7 +18,7 @@ use std::{
}; };
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker}; use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture};
use crate::world::{BlockFlags, SimpleWorld}; use crate::world::{BlockFlags, SimpleWorld};
use crate::{ use crate::{
inventory::{ inventory::{
@ -37,7 +39,6 @@ pub struct BarrelBlockEntity {
viewers: ViewerCountTracker, viewers: ViewerCountTracker,
} }
#[async_trait]
impl BlockEntity for BarrelBlockEntity { impl BlockEntity for BarrelBlockEntity {
fn resource_location(&self) -> &'static str { fn resource_location(&self) -> &'static str {
Self::ID Self::ID
@ -63,16 +64,26 @@ impl BlockEntity for BarrelBlockEntity {
barrel barrel
} }
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { fn write_nbt<'a>(
self.write_data(nbt, &self.items, true).await; &'a self,
nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
self.write_data(nbt, &self.items, true).await;
})
// Safety precaution // Safety precaution
//self.clear().await; //self.clear().await;
} }
async fn tick(&self, world: Arc<dyn SimpleWorld>) { fn tick<'a>(
self.viewers &'a self,
.update_viewer_count::<BarrelBlockEntity>(self, world, &self.position) world: Arc<dyn SimpleWorld>,
.await; ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
self.viewers
.update_viewer_count::<BarrelBlockEntity>(self, world, &self.position)
.await;
})
} }
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> { fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
@ -88,16 +99,27 @@ impl BlockEntity for BarrelBlockEntity {
} }
} }
#[async_trait]
impl ViewerCountListener for BarrelBlockEntity { impl ViewerCountListener for BarrelBlockEntity {
async fn on_container_open(&self, world: &Arc<dyn SimpleWorld>, _position: &BlockPos) { fn on_container_open<'a>(
self.play_sound(world, Sound::BlockBarrelOpen).await; &'a self,
self.set_open(world, true).await; world: &'a Arc<dyn SimpleWorld>,
_position: &'a BlockPos,
) -> ViewerFuture<'a, ()> {
Box::pin(async move {
self.play_sound(world, Sound::BlockBarrelOpen).await;
self.set_open(world, true).await;
})
} }
async fn on_container_close(&self, world: &Arc<dyn SimpleWorld>, _position: &BlockPos) { fn on_container_close<'a>(
self.play_sound(world, Sound::BlockBarrelClose).await; &'a self,
self.set_open(world, false).await; world: &'a Arc<dyn SimpleWorld>,
_position: &'a BlockPos,
) -> ViewerFuture<'a, ()> {
Box::pin(async move {
self.play_sound(world, Sound::BlockBarrelClose).await;
self.set_open(world, false).await;
})
} }
} }
@ -205,11 +227,12 @@ impl Inventory for BarrelBlockEntity {
} }
} }
#[async_trait]
impl Clearable for BarrelBlockEntity { impl Clearable for BarrelBlockEntity {
async fn clear(&self) { fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
for slot in self.items.iter() { Box::pin(async move {
*slot.lock().await = ItemStack::EMPTY.clone(); for slot in self.items.iter() {
} *slot.lock().await = ItemStack::EMPTY.clone();
}
})
} }
} }

View File

@ -1,4 +1,6 @@
use async_trait::async_trait; use std::pin::Pin;
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use super::BlockEntity; use super::BlockEntity;
@ -7,7 +9,6 @@ pub struct BedBlockEntity {
pub position: BlockPos, pub position: BlockPos,
} }
#[async_trait]
impl BlockEntity for BedBlockEntity { impl BlockEntity for BedBlockEntity {
fn resource_location(&self) -> &'static str { fn resource_location(&self) -> &'static str {
Self::ID Self::ID
@ -24,7 +25,12 @@ impl BlockEntity for BedBlockEntity {
Self { position } Self { position }
} }
async fn write_nbt(&self, _nbt: &mut pumpkin_nbt::compound::NbtCompound) {} fn write_nbt<'a>(
&'a self,
_nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {})
}
fn as_any(&self) -> &dyn std::any::Any { fn as_any(&self) -> &dyn std::any::Any {
self self

View File

@ -1,6 +1,7 @@
use std::{ use std::{
any::Any, any::Any,
array::from_fn, array::from_fn,
pin::Pin,
sync::{ sync::{
Arc, Arc,
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
@ -13,6 +14,7 @@ use pumpkin_data::{
block_properties::{BlockProperties, ChestLikeProperties, ChestType}, block_properties::{BlockProperties, ChestLikeProperties, ChestType},
sound::{Sound, SoundCategory}, sound::{Sound, SoundCategory},
}; };
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::{ use pumpkin_util::{
math::{position::BlockPos, vector3::Vector3}, math::{position::BlockPos, vector3::Vector3},
random::{RandomImpl, get_seed, xoroshiro128::Xoroshiro}, random::{RandomImpl, get_seed, xoroshiro128::Xoroshiro},
@ -20,7 +22,7 @@ use pumpkin_util::{
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::{ use crate::{
block::viewer::{ViewerCountListener, ViewerCountTracker}, block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture},
inventory::{Clearable, Inventory, split_stack}, inventory::{Clearable, Inventory, split_stack},
item::ItemStack, item::ItemStack,
world::SimpleWorld, world::SimpleWorld,
@ -38,7 +40,6 @@ pub struct ChestBlockEntity {
viewers: ViewerCountTracker, viewers: ViewerCountTracker,
} }
#[async_trait]
impl BlockEntity for ChestBlockEntity { impl BlockEntity for ChestBlockEntity {
fn resource_location(&self) -> &'static str { fn resource_location(&self) -> &'static str {
Self::ID Self::ID
@ -64,16 +65,26 @@ impl BlockEntity for ChestBlockEntity {
chest chest
} }
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { fn write_nbt<'a>(
self.write_data(nbt, &self.items, true).await; &'a self,
nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
self.write_data(nbt, &self.items, true).await;
})
// Safety precaution // Safety precaution
//self.clear().await; //self.clear().await;
} }
async fn tick(&self, world: Arc<dyn SimpleWorld>) { fn tick<'a>(
self.viewers &'a self,
.update_viewer_count::<ChestBlockEntity>(self, world, &self.position) world: Arc<dyn SimpleWorld>,
.await; ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
self.viewers
.update_viewer_count::<ChestBlockEntity>(self, world, &self.position)
.await;
})
} }
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> { fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
@ -89,29 +100,41 @@ impl BlockEntity for ChestBlockEntity {
} }
} }
#[async_trait]
impl ViewerCountListener for ChestBlockEntity { impl ViewerCountListener for ChestBlockEntity {
async fn on_container_open(&self, world: &Arc<dyn SimpleWorld>, _position: &BlockPos) { fn on_container_open<'a>(
self.play_sound(world, Sound::BlockChestOpen).await; &'a self,
world: &'a Arc<dyn SimpleWorld>,
_position: &'a BlockPos,
) -> ViewerFuture<'a, ()> {
Box::pin(async move {
self.play_sound(world, Sound::BlockEnderChestOpen).await;
})
} }
async fn on_container_close(&self, world: &Arc<dyn SimpleWorld>, _position: &BlockPos) { fn on_container_close<'a>(
self.play_sound(world, Sound::BlockChestClose).await; &'a self,
world: &'a Arc<dyn SimpleWorld>,
_position: &'a BlockPos,
) -> ViewerFuture<'a, ()> {
Box::pin(async move {
self.play_sound(world, Sound::BlockEnderChestClose).await;
})
} }
async fn on_viewer_count_update( fn on_viewer_count_update<'a>(
&self, &'a self,
world: &Arc<dyn SimpleWorld>, world: &'a Arc<dyn SimpleWorld>,
position: &BlockPos, position: &'a BlockPos,
_old: u16, _old: u16,
new: u16, new: u16,
) { ) -> ViewerFuture<'a, ()> {
world Box::pin(async move {
.add_synced_block_event(*position, Self::LID_ANIMATION_EVENT_TYPE, new as u8) world
.await .add_synced_block_event(*position, Self::LID_ANIMATION_EVENT_TYPE, new as u8)
.await
})
} }
} }
impl ChestBlockEntity { impl ChestBlockEntity {
pub const INVENTORY_SIZE: usize = 27; pub const INVENTORY_SIZE: usize = 27;
pub const LID_ANIMATION_EVENT_TYPE: u8 = 1; pub const LID_ANIMATION_EVENT_TYPE: u8 = 1;
@ -212,11 +235,12 @@ impl Inventory for ChestBlockEntity {
} }
} }
#[async_trait]
impl Clearable for ChestBlockEntity { impl Clearable for ChestBlockEntity {
async fn clear(&self) { fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
for slot in self.items.iter() { Box::pin(async move {
*slot.lock().await = ItemStack::EMPTY.clone(); for slot in self.items.iter() {
} *slot.lock().await = ItemStack::EMPTY.clone();
}
})
} }
} }

View File

@ -5,6 +5,7 @@ use pumpkin_data::block_properties::{BlockProperties, ChiseledBookshelfLikePrope
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use std::any::Any; use std::any::Any;
use std::pin::Pin;
use std::{ use std::{
array::from_fn, array::from_fn,
sync::{ sync::{
@ -31,7 +32,6 @@ pub struct ChiseledBookshelfBlockEntity {
const LAST_INTERACTED_SLOT: &str = "last_interacted_slot"; const LAST_INTERACTED_SLOT: &str = "last_interacted_slot";
#[async_trait]
impl BlockEntity for ChiseledBookshelfBlockEntity { impl BlockEntity for ChiseledBookshelfBlockEntity {
fn resource_location(&self) -> &'static str { fn resource_location(&self) -> &'static str {
Self::ID Self::ID
@ -59,12 +59,17 @@ impl BlockEntity for ChiseledBookshelfBlockEntity {
chiseled_bookshelf chiseled_bookshelf
} }
async fn write_nbt(&self, nbt: &mut NbtCompound) { fn write_nbt<'a>(
self.write_data(nbt, &self.items, true).await; &'a self,
nbt.put_int( nbt: &'a mut NbtCompound,
LAST_INTERACTED_SLOT, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
self.last_interacted_slot.load(Ordering::Relaxed).into(), Box::pin(async move {
); self.write_data(nbt, &self.items, true).await;
nbt.put_int(
LAST_INTERACTED_SLOT,
self.last_interacted_slot.load(Ordering::Relaxed).into(),
);
})
} }
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> { fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
@ -169,11 +174,12 @@ impl Inventory for ChiseledBookshelfBlockEntity {
} }
} }
#[async_trait]
impl Clearable for ChiseledBookshelfBlockEntity { impl Clearable for ChiseledBookshelfBlockEntity {
async fn clear(&self) { fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
for slot in self.items.iter() { Box::pin(async move {
*slot.lock().await = ItemStack::EMPTY.clone(); for slot in self.items.iter() {
} *slot.lock().await = ItemStack::EMPTY.clone();
}
})
} }
} }

View File

@ -1,6 +1,9 @@
use std::sync::atomic::{AtomicBool, Ordering}; use std::{
pin::Pin,
sync::atomic::{AtomicBool, Ordering},
};
use async_trait::async_trait; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use super::BlockEntity; use super::BlockEntity;
@ -26,7 +29,6 @@ impl CommandBlockEntity {
} }
} }
#[async_trait]
impl BlockEntity for CommandBlockEntity { impl BlockEntity for CommandBlockEntity {
fn resource_location(&self) -> &'static str { fn resource_location(&self) -> &'static str {
Self::ID Self::ID
@ -42,7 +44,12 @@ impl BlockEntity for CommandBlockEntity {
Self::new(position) Self::new(position)
} }
async fn write_nbt(&self, _nbt: &mut pumpkin_nbt::compound::NbtCompound) {} fn write_nbt<'a>(
&'a self,
_nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {})
}
fn is_dirty(&self) -> bool { fn is_dirty(&self) -> bool {
self.dirty.load(Ordering::Relaxed) self.dirty.load(Ordering::Relaxed)

View File

@ -1,6 +1,8 @@
use std::sync::atomic::{AtomicU8, Ordering}; use std::{
pin::Pin,
sync::atomic::{AtomicU8, Ordering},
};
use async_trait::async_trait;
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
@ -23,7 +25,6 @@ impl ComparatorBlockEntity {
const OUTPUT_SIGNAL: &str = "OutputSignal"; const OUTPUT_SIGNAL: &str = "OutputSignal";
#[async_trait]
impl BlockEntity for ComparatorBlockEntity { impl BlockEntity for ComparatorBlockEntity {
fn resource_location(&self) -> &'static str { fn resource_location(&self) -> &'static str {
Self::ID Self::ID
@ -44,11 +45,16 @@ impl BlockEntity for ComparatorBlockEntity {
} }
} }
async fn write_nbt(&self, nbt: &mut NbtCompound) { fn write_nbt<'a>(
nbt.put_int( &'a self,
OUTPUT_SIGNAL, nbt: &'a mut NbtCompound,
self.output_signal.load(Ordering::Relaxed) as i32, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
); Box::pin(async move {
nbt.put_int(
OUTPUT_SIGNAL,
self.output_signal.load(Ordering::Relaxed) as i32,
);
})
} }
fn chunk_data_nbt(&self) -> Option<NbtCompound> { fn chunk_data_nbt(&self) -> Option<NbtCompound> {

View File

@ -2,10 +2,12 @@ use crate::block::entities::BlockEntity;
use crate::inventory::{Clearable, Inventory, split_stack}; use crate::inventory::{Clearable, Inventory, split_stack};
use crate::item::ItemStack; use crate::item::ItemStack;
use async_trait::async_trait; use async_trait::async_trait;
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use rand::{Rng, rng}; use rand::{Rng, rng};
use std::any::Any; use std::any::Any;
use std::array::from_fn; use std::array::from_fn;
use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use tokio::sync::{Mutex, MutexGuard}; use tokio::sync::{Mutex, MutexGuard};
@ -17,10 +19,14 @@ pub struct DropperBlockEntity {
pub dirty: AtomicBool, pub dirty: AtomicBool,
} }
#[async_trait]
impl BlockEntity for DropperBlockEntity { impl BlockEntity for DropperBlockEntity {
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { fn write_nbt<'a>(
self.write_data(nbt, &self.items, true).await; &'a self,
nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
self.write_data(nbt, &self.items, true).await;
})
// Safety precaution // Safety precaution
//self.clear().await; //self.clear().await;
} }
@ -133,11 +139,12 @@ impl Inventory for DropperBlockEntity {
} }
} }
#[async_trait]
impl Clearable for DropperBlockEntity { impl Clearable for DropperBlockEntity {
async fn clear(&self) { fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
for slot in self.items.iter() { Box::pin(async move {
*slot.lock().await = ItemStack::EMPTY.clone(); for slot in self.items.iter() {
} *slot.lock().await = ItemStack::EMPTY.clone();
}
})
} }
} }

View File

@ -1,4 +1,6 @@
use async_trait::async_trait; use std::pin::Pin;
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use super::BlockEntity; use super::BlockEntity;
@ -14,7 +16,6 @@ impl EndPortalBlockEntity {
} }
} }
#[async_trait]
impl BlockEntity for EndPortalBlockEntity { impl BlockEntity for EndPortalBlockEntity {
fn resource_location(&self) -> &'static str { fn resource_location(&self) -> &'static str {
Self::ID Self::ID
@ -31,7 +32,12 @@ impl BlockEntity for EndPortalBlockEntity {
Self { position } Self { position }
} }
async fn write_nbt(&self, _nbt: &mut pumpkin_nbt::compound::NbtCompound) {} fn write_nbt<'a>(
&'a self,
_nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {})
}
fn as_any(&self) -> &dyn std::any::Any { fn as_any(&self) -> &dyn std::any::Any {
self self

View File

@ -1,13 +1,13 @@
use async_trait::async_trait;
use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::random::xoroshiro128::Xoroshiro; use pumpkin_util::random::xoroshiro128::Xoroshiro;
use pumpkin_util::random::{RandomImpl, get_seed}; use pumpkin_util::random::{RandomImpl, get_seed};
use std::any::Any; use std::any::Any;
use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker}; use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture};
use crate::world::SimpleWorld; use crate::world::SimpleWorld;
use super::BlockEntity; use super::BlockEntity;
@ -20,7 +20,6 @@ pub struct EnderChestBlockEntity {
viewers: Arc<ViewerCountTracker>, viewers: Arc<ViewerCountTracker>,
} }
#[async_trait]
impl BlockEntity for EnderChestBlockEntity { impl BlockEntity for EnderChestBlockEntity {
fn resource_location(&self) -> &'static str { fn resource_location(&self) -> &'static str {
Self::ID Self::ID
@ -40,12 +39,22 @@ impl BlockEntity for EnderChestBlockEntity {
} }
} }
async fn write_nbt(&self, _nbt: &mut NbtCompound) {} fn write_nbt<'a>(
&'a self,
_nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {})
}
async fn tick(&self, world: Arc<dyn SimpleWorld>) { fn tick<'a>(
self.viewers &'a self,
.update_viewer_count::<EnderChestBlockEntity>(self, world, &self.position) world: Arc<dyn SimpleWorld>,
.await; ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
self.viewers
.update_viewer_count::<EnderChestBlockEntity>(self, world, &self.position)
.await;
})
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
@ -53,26 +62,39 @@ impl BlockEntity for EnderChestBlockEntity {
} }
} }
#[async_trait]
impl ViewerCountListener for EnderChestBlockEntity { impl ViewerCountListener for EnderChestBlockEntity {
async fn on_container_open(&self, world: &Arc<dyn SimpleWorld>, _position: &BlockPos) { fn on_container_open<'a>(
self.play_sound(world, Sound::BlockEnderChestOpen).await; &'a self,
world: &'a Arc<dyn SimpleWorld>,
_position: &'a BlockPos,
) -> ViewerFuture<'a, ()> {
Box::pin(async move {
self.play_sound(world, Sound::BlockEnderChestOpen).await;
})
} }
async fn on_container_close(&self, world: &Arc<dyn SimpleWorld>, _position: &BlockPos) { fn on_container_close<'a>(
self.play_sound(world, Sound::BlockEnderChestClose).await; &'a self,
world: &'a Arc<dyn SimpleWorld>,
_position: &'a BlockPos,
) -> ViewerFuture<'a, ()> {
Box::pin(async move {
self.play_sound(world, Sound::BlockEnderChestClose).await;
})
} }
async fn on_viewer_count_update( fn on_viewer_count_update<'a>(
&self, &'a self,
world: &Arc<dyn SimpleWorld>, world: &'a Arc<dyn SimpleWorld>,
position: &BlockPos, position: &'a BlockPos,
_old: u16, _old: u16,
new: u16, new: u16,
) { ) -> ViewerFuture<'a, ()> {
world Box::pin(async move {
.add_synced_block_event(*position, Self::LID_ANIMATION_EVENT_TYPE, new as u8) world
.await .add_synced_block_event(*position, Self::LID_ANIMATION_EVENT_TYPE, new as u8)
.await
})
} }
} }

View File

@ -1,5 +1,6 @@
use std::{ use std::{
array::from_fn, array::from_fn,
pin::Pin,
sync::{ sync::{
Arc, Arc,
atomic::{AtomicBool, AtomicU16, Ordering}, atomic::{AtomicBool, AtomicU16, Ordering},
@ -14,6 +15,7 @@ use pumpkin_data::{
recipe_remainder::get_recipe_remainder_id, recipe_remainder::get_recipe_remainder_id,
recipes::{CookingRecipe, CookingRecipeType, RECIPES_COOKING}, recipes::{CookingRecipe, CookingRecipeType, RECIPES_COOKING},
}; };
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use tokio::sync::Mutex; use tokio::sync::Mutex;
@ -156,112 +158,117 @@ impl FurnaceBlockEntity {
} }
} }
#[async_trait]
impl BlockEntity for FurnaceBlockEntity { impl BlockEntity for FurnaceBlockEntity {
async fn tick(&self, world: Arc<dyn SimpleWorld>) { fn tick<'a>(
let is_burning = self.is_burning(); &'a self,
let mut is_dirty = false; world: Arc<dyn SimpleWorld>,
if self.is_burning() { ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
self.lit_time_remaining.fetch_sub(1, Ordering::Relaxed); Box::pin(async move {
} let is_burning = self.is_burning();
let mut is_dirty = false;
if self.is_burning() {
self.lit_time_remaining.fetch_sub(1, Ordering::Relaxed);
}
let top_items = self.items[0].lock().await; let top_items = self.items[0].lock().await;
let is_top_items_empty = top_items.is_empty(); let is_top_items_empty = top_items.is_empty();
let furnace_recipe = Self::get_furnace_cooking_recipe(top_items.item); let furnace_recipe = Self::get_furnace_cooking_recipe(top_items.item);
drop(top_items); drop(top_items);
let can_accept_output = self let can_accept_output = self
.can_accept_recipe_output(furnace_recipe, self.get_max_count_per_stack()) .can_accept_recipe_output(furnace_recipe, self.get_max_count_per_stack())
.await; .await;
let bottom_items_is_empty = self.items[1].lock().await.is_empty(); let bottom_items_is_empty = self.items[1].lock().await.is_empty();
if self.is_burning() || !bottom_items_is_empty && !is_top_items_empty { if self.is_burning() || !bottom_items_is_empty && !is_top_items_empty {
if !self.is_burning() && can_accept_output { if !self.is_burning() && can_accept_output {
let mut bottom_items = self.items[1].lock().await; let mut bottom_items = self.items[1].lock().await;
let fuel_ticks = get_item_burn_ticks(bottom_items.item.id).unwrap_or(0); let fuel_ticks = get_item_burn_ticks(bottom_items.item.id).unwrap_or(0);
self.lit_time_remaining.store(fuel_ticks, Ordering::Relaxed); self.lit_time_remaining.store(fuel_ticks, Ordering::Relaxed);
self.lit_total_time.store(fuel_ticks, Ordering::Relaxed); self.lit_total_time.store(fuel_ticks, Ordering::Relaxed);
if self.is_burning() { if self.is_burning() {
is_dirty = true; is_dirty = true;
if !bottom_items.is_empty() { if !bottom_items.is_empty() {
bottom_items.decrement(1); bottom_items.decrement(1);
if let Some(remainder_id) = get_recipe_remainder_id(bottom_items.item.id) if let Some(remainder_id) =
&& bottom_items.is_empty() get_recipe_remainder_id(bottom_items.item.id)
&& let Some(remainder_item) = Item::from_id(remainder_id) && bottom_items.is_empty()
{ && let Some(remainder_item) = Item::from_id(remainder_id)
drop(bottom_items); {
self.set_stack(1, ItemStack::new(1, remainder_item)).await; drop(bottom_items);
self.set_stack(1, ItemStack::new(1, remainder_item)).await;
}
} }
} }
} }
}
if self.is_burning() && can_accept_output { if self.is_burning() && can_accept_output {
self.cooking_time_spent.fetch_add(1, Ordering::Relaxed); self.cooking_time_spent.fetch_add(1, Ordering::Relaxed);
if self.cooking_time_spent.load(Ordering::Relaxed) if self.cooking_time_spent.load(Ordering::Relaxed)
== self.cooking_total_time.load(Ordering::Relaxed) == self.cooking_total_time.load(Ordering::Relaxed)
{ {
self.cooking_time_spent.store(0, Ordering::Relaxed); self.cooking_time_spent.store(0, Ordering::Relaxed);
if let Some(cooking_recipe) = furnace_recipe { if let Some(cooking_recipe) = furnace_recipe {
let cooking_total_time = cooking_recipe.cookingtime; let cooking_total_time = cooking_recipe.cookingtime;
self.cooking_total_time self.cooking_total_time
.store(cooking_total_time as u16, Ordering::Relaxed); .store(cooking_total_time as u16, Ordering::Relaxed);
self.craft_recipe(Some(cooking_recipe)).await; self.craft_recipe(Some(cooking_recipe)).await;
is_dirty = true; is_dirty = true;
}
} }
} else {
self.cooking_time_spent.store(0, Ordering::Relaxed);
} }
} else { } else if !self.is_burning() && self.cooking_time_spent.load(Ordering::Relaxed) > 0 {
self.cooking_time_spent.store(0, Ordering::Relaxed); self.cooking_time_spent
.fetch_update(Ordering::Acquire, Ordering::Acquire, |v| {
Some(
v.saturating_sub(2)
.min(self.cooking_total_time.load(Ordering::Acquire)),
)
})
.unwrap();
} }
} else if !self.is_burning() && self.cooking_time_spent.load(Ordering::Relaxed) > 0 {
self.cooking_time_spent
.fetch_update(Ordering::Acquire, Ordering::Acquire, |v| {
Some(
v.saturating_sub(2)
.min(self.cooking_total_time.load(Ordering::Acquire)),
)
})
.unwrap();
}
if is_burning != self.is_burning() { if is_burning != self.is_burning() {
is_dirty = true; is_dirty = true;
let world = world.clone(); let world = world.clone();
let (furnace_block, furnace_block_state) = let (furnace_block, furnace_block_state) =
world.get_block_and_state(&self.position).await; world.get_block_and_state(&self.position).await;
let mut props = let mut props =
FurnaceLikeProperties::from_state_id(furnace_block_state.id, furnace_block); FurnaceLikeProperties::from_state_id(furnace_block_state.id, furnace_block);
if self.is_burning() { if self.is_burning() {
props.lit = true; props.lit = true;
world world
.set_block_state( .set_block_state(
&self.position, &self.position,
props.to_state_id(furnace_block), props.to_state_id(furnace_block),
BlockFlags::NOTIFY_ALL, BlockFlags::NOTIFY_ALL,
) )
.await; .await;
} else { } else {
props.lit = false; props.lit = false;
world world
.set_block_state( .set_block_state(
&self.position, &self.position,
props.to_state_id(furnace_block), props.to_state_id(furnace_block),
BlockFlags::NOTIFY_ALL, BlockFlags::NOTIFY_ALL,
) )
.await; .await;
}
} }
}
if is_dirty { if is_dirty {
self.is_dirty(); self.is_dirty();
} }
})
} }
fn resource_location(&self) -> &'static str { fn resource_location(&self) -> &'static str {
@ -307,24 +314,29 @@ impl BlockEntity for FurnaceBlockEntity {
furnace furnace
} }
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { fn write_nbt<'a>(
nbt.put_short( &'a self,
"cooking_total_time", nbt: &'a mut NbtCompound,
self.cooking_total_time.load(Ordering::Relaxed) as i16, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
); Box::pin(async move {
nbt.put_short( nbt.put_short(
"cooking_time_spent", "cooking_total_time",
self.cooking_time_spent.load(Ordering::Relaxed) as i16, self.cooking_total_time.load(Ordering::Relaxed) as i16,
); );
nbt.put_short( nbt.put_short(
"lit_total_time", "cooking_time_spent",
self.lit_total_time.load(Ordering::Relaxed) as i16, self.cooking_time_spent.load(Ordering::Relaxed) as i16,
); );
nbt.put_short( nbt.put_short(
"lit_time_remaining", "lit_total_time",
self.lit_time_remaining.load(Ordering::Relaxed) as i16, self.lit_total_time.load(Ordering::Relaxed) as i16,
); );
self.write_data(nbt, &self.items, true).await; nbt.put_short(
"lit_time_remaining",
self.lit_time_remaining.load(Ordering::Relaxed) as i16,
);
self.write_data(nbt, &self.items, true).await;
})
// Safety precaution // Safety precaution
// self.clear().await; // self.clear().await;
} }
@ -425,12 +437,13 @@ impl Inventory for FurnaceBlockEntity {
} }
} }
#[async_trait]
impl Clearable for FurnaceBlockEntity { impl Clearable for FurnaceBlockEntity {
async fn clear(&self) { fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
for slot in self.items.iter() { Box::pin(async move {
*slot.lock().await = ItemStack::EMPTY.clone(); for slot in self.items.iter() {
} *slot.lock().await = ItemStack::EMPTY.clone();
}
})
} }
} }

View File

@ -7,11 +7,13 @@ use async_trait::async_trait;
use pumpkin_data::block_properties::{BlockProperties, HopperFacing, HopperLikeProperties}; use pumpkin_data::block_properties::{BlockProperties, HopperFacing, HopperLikeProperties};
use pumpkin_data::tag::Taggable; use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, tag}; use pumpkin_data::{Block, tag};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_nbt::tag::NbtTag; use pumpkin_nbt::tag::NbtTag;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use std::any::Any; use std::any::Any;
use std::array::from_fn; use std::array::from_fn;
use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64}; use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64};
use tokio::sync::Mutex; use tokio::sync::Mutex;
@ -37,17 +39,21 @@ pub fn to_offset(facing: &HopperFacing) -> Vector3<i32> {
.into() .into()
} }
#[async_trait]
impl BlockEntity for HopperBlockEntity { impl BlockEntity for HopperBlockEntity {
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { fn write_nbt<'a>(
self.write_data(nbt, &self.items, true).await; &'a self,
nbt.put( nbt: &'a mut NbtCompound,
"TransferCooldown", ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
NbtTag::Int( Box::pin(async move {
self.cooldown_time self.write_data(nbt, &self.items, true).await;
.load(std::sync::atomic::Ordering::Relaxed), nbt.put(
), "TransferCooldown",
); NbtTag::Int(
self.cooldown_time
.load(std::sync::atomic::Ordering::Relaxed),
),
);
})
// Safety precaution // Safety precaution
//self.clear().await; //self.clear().await;
} }
@ -70,24 +76,29 @@ impl BlockEntity for HopperBlockEntity {
hopper hopper
} }
async fn tick(&self, world: Arc<dyn SimpleWorld>) { fn tick<'a>(
self.ticked_game_time.store( &'a self,
world.get_world_age().await, world: Arc<dyn SimpleWorld>,
std::sync::atomic::Ordering::Relaxed, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
); Box::pin(async move {
if self self.ticked_game_time.store(
.cooldown_time world.get_world_age().await,
.fetch_sub(1, std::sync::atomic::Ordering::Relaxed) std::sync::atomic::Ordering::Relaxed,
<= 0
{
self.cooldown_time
.store(0, std::sync::atomic::Ordering::Relaxed);
let state = HopperLikeProperties::from_state_id(
world.get_block_state(&self.position).await.id,
&Block::HOPPER,
); );
self.try_move_items(&state, &world).await; if self
} .cooldown_time
.fetch_sub(1, std::sync::atomic::Ordering::Relaxed)
<= 0
{
self.cooldown_time
.store(0, std::sync::atomic::Ordering::Relaxed);
let state = HopperLikeProperties::from_state_id(
world.get_block_state(&self.position).await.id,
&Block::HOPPER,
);
self.try_move_items(&state, &world).await;
}
})
} }
fn resource_location(&self) -> &'static str { fn resource_location(&self) -> &'static str {
@ -327,11 +338,12 @@ impl Inventory for HopperBlockEntity {
} }
} }
#[async_trait]
impl Clearable for HopperBlockEntity { impl Clearable for HopperBlockEntity {
async fn clear(&self) { fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
for slot in self.items.iter() { Box::pin(async move {
*slot.lock().await = ItemStack::EMPTY.clone(); for slot in self.items.iter() {
} *slot.lock().await = ItemStack::EMPTY.clone();
}
})
} }
} }

View File

@ -1,9 +1,11 @@
use std::sync::{ use std::{
Arc, pin::Pin,
atomic::{AtomicI32, Ordering}, sync::{
Arc,
atomic::{AtomicI32, Ordering},
},
}; };
use async_trait::async_trait;
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use pumpkin_data::{entity::EntityType, world::WorldEvent}; use pumpkin_data::{entity::EntityType, world::WorldEvent};
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
@ -67,7 +69,6 @@ impl MobSpawnerBlockEntity {
} }
} }
#[async_trait]
impl BlockEntity for MobSpawnerBlockEntity { impl BlockEntity for MobSpawnerBlockEntity {
fn resource_location(&self) -> &'static str { fn resource_location(&self) -> &'static str {
Self::ID Self::ID
@ -77,53 +78,58 @@ impl BlockEntity for MobSpawnerBlockEntity {
self.position self.position
} }
async fn tick(&self, world: Arc<dyn SimpleWorld>) { fn tick<'a>(
if let Some(entity_type) = &self.entity_type.load() { &'a self,
if self.delay.load(Ordering::Relaxed) == -1 { world: Arc<dyn SimpleWorld>,
self.update_spawns(&world).await; ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
} else { Box::pin(async move {
self.delay.fetch_sub(1, Ordering::Relaxed); if let Some(entity_type) = &self.entity_type.load() {
return; if self.delay.load(Ordering::Relaxed) == -1 {
} self.update_spawns(&world).await;
let spawn_range = self.spawn_range; } else {
let mut update_spawns = false; self.delay.fetch_sub(1, Ordering::Relaxed);
for _ in 0..self.spawn_count { return;
let pos = self.position.0; }
let spawn_range = self.spawn_range;
let spawn_pos = Vector3::new( let mut update_spawns = false;
pos.x as f64 for _ in 0..self.spawn_count {
+ (rand::random::<f64>() + rand::random::<f64>()) * spawn_range as f64 let pos = self.position.0;
+ 0.5,
(pos.y + rand::random_range(0..3) - 1) as f64, let spawn_pos = Vector3::new(
pos.z as f64 pos.x as f64
+ (rand::random::<f64>() + rand::random::<f64>()) * spawn_range as f64 + (rand::random::<f64>() + rand::random::<f64>()) * spawn_range as f64
+ 0.5, + 0.5,
); (pos.y + rand::random_range(0..3) - 1) as f64,
// TODO: we should use getSpawnBox, but this is only modified for slimes and magma slimes pos.z as f64
if !world + (rand::random::<f64>() + rand::random::<f64>()) * spawn_range as f64
.is_space_empty(BoundingBox::new_from_pos( + 0.5,
spawn_pos.x, );
spawn_pos.y, // TODO: we should use getSpawnBox, but this is only modified for slimes and magma slimes
spawn_pos.z, if !world
&EntityDimensions { .is_space_empty(BoundingBox::new_from_pos(
width: entity_type.dimension[0], spawn_pos.x,
height: entity_type.dimension[1], spawn_pos.y,
}, spawn_pos.z,
)) &EntityDimensions {
.await width: entity_type.dimension[0],
{ height: entity_type.dimension[1],
continue; },
))
.await
{
continue;
}
world.clone().spawn_from_type(entity_type, spawn_pos).await;
world
.sync_world_event(WorldEvent::SpawnerSpawnsMob, self.position, 0)
.await;
update_spawns = true;
}
if update_spawns {
self.update_spawns(&world).await;
} }
world.clone().spawn_from_type(entity_type, spawn_pos).await;
world
.sync_world_event(WorldEvent::SpawnerSpawnsMob, self.position, 0)
.await;
update_spawns = true;
} }
if update_spawns { })
self.update_spawns(&world).await;
}
}
} }
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
@ -156,13 +162,18 @@ impl BlockEntity for MobSpawnerBlockEntity {
} }
} }
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { fn write_nbt<'a>(
if let Some(entity_type) = self.entity_type.load() { &'a self,
let mut entity_nbt = NbtCompound::new(); nbt: &'a mut NbtCompound,
entity_nbt.put_string("id", format!("minecraft:{}", entity_type.resource_name)); ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
if let Some(entity_type) = self.entity_type.load() {
let mut entity_nbt = NbtCompound::new();
entity_nbt.put_string("id", format!("minecraft:{}", entity_type.resource_name));
nbt.put_component("entity", entity_nbt); nbt.put_component("entity", entity_nbt);
} }
})
} }
fn chunk_data_nbt(&self) -> Option<NbtCompound> { fn chunk_data_nbt(&self) -> Option<NbtCompound> {

View File

@ -1,6 +1,6 @@
use std::pin::Pin;
use std::{any::Any, sync::Arc}; use std::{any::Any, sync::Arc};
use async_trait::async_trait;
use barrel::BarrelBlockEntity; use barrel::BarrelBlockEntity;
use bed::BedBlockEntity; use bed::BedBlockEntity;
use chest::ChestBlockEntity; use chest::ChestBlockEntity;
@ -39,22 +39,34 @@ pub mod shulker_box;
pub mod sign; pub mod sign;
//TODO: We need a mark_dirty for chests //TODO: We need a mark_dirty for chests
#[async_trait]
pub trait BlockEntity: Send + Sync { pub trait BlockEntity: Send + Sync {
async fn write_nbt(&self, nbt: &mut NbtCompound); fn write_nbt<'a>(
&'a self,
nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self
where where
Self: Sized; Self: Sized;
async fn tick(&self, _world: Arc<dyn SimpleWorld>) {} fn tick<'a>(
&'a self,
_world: Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {})
}
fn resource_location(&self) -> &'static str; fn resource_location(&self) -> &'static str;
fn get_position(&self) -> BlockPos; fn get_position(&self) -> BlockPos;
async fn write_internal(&self, nbt: &mut NbtCompound) { fn write_internal<'a>(
nbt.put_string("id", self.resource_location().to_string()); &'a self,
let position = self.get_position(); nbt: &'a mut NbtCompound,
nbt.put_int("x", position.0.x); ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
nbt.put_int("y", position.0.y); Box::pin(async move {
nbt.put_int("z", position.0.z); nbt.put_string("id", self.resource_location().to_string());
self.write_nbt(nbt).await; let position = self.get_position();
nbt.put_int("x", position.0.x);
nbt.put_int("y", position.0.y);
nbt.put_int("z", position.0.z);
self.write_nbt(nbt).await;
})
} }
fn get_id(&self) -> u32 { fn get_id(&self) -> u32 {
pumpkin_data::block_properties::BLOCK_ENTITY_TYPES pumpkin_data::block_properties::BLOCK_ENTITY_TYPES
@ -71,10 +83,20 @@ pub trait BlockEntity: Send + Sync {
None None
} }
fn set_block_state(&mut self, _block_state: BlockStateId) {} fn set_block_state(&mut self, _block_state: BlockStateId) {}
async fn on_block_replaced(self: Arc<Self>, world: Arc<dyn SimpleWorld>, position: BlockPos) { fn on_block_replaced<'a>(
if let Some(inventory) = self.get_inventory() { self: Arc<Self>,
world.scatter_inventory(&position, &inventory).await; world: Arc<dyn SimpleWorld>,
} position: BlockPos,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>
where
Self: 'a,
{
Box::pin(async move {
if let Some(inventory) = self.get_inventory() {
// Assuming scatter_inventory is an async method on SimpleWorld
world.scatter_inventory(&position, &inventory).await;
}
})
} }
fn is_dirty(&self) -> bool { fn is_dirty(&self) -> bool {
false false

View File

@ -1,6 +1,5 @@
use std::sync::Arc; use std::{pin::Pin, sync::Arc};
use async_trait::async_trait;
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use pumpkin_data::{Block, BlockDirection, BlockState}; use pumpkin_data::{Block, BlockDirection, BlockState};
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
@ -50,7 +49,6 @@ const LAST_PROGRESS: &str = "progress";
const EXTENDING: &str = "extending"; const EXTENDING: &str = "extending";
const SOURCE: &str = "source"; const SOURCE: &str = "source";
#[async_trait]
impl BlockEntity for PistonBlockEntity { impl BlockEntity for PistonBlockEntity {
fn resource_location(&self) -> &'static str { fn resource_location(&self) -> &'static str {
Self::ID Self::ID
@ -60,42 +58,47 @@ impl BlockEntity for PistonBlockEntity {
self.position self.position
} }
async fn tick(&self, world: Arc<dyn SimpleWorld>) { fn tick<'a>(
let current_progress = self.current_progress.load(); &'a self,
self.last_progress.store(current_progress); world: Arc<dyn SimpleWorld>,
if current_progress >= 1.0 { ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
let pos = self.position; Box::pin(async move {
world.remove_block_entity(&pos).await; let current_progress = self.current_progress.load();
if world.get_block(&pos).await == &Block::MOVING_PISTON { self.last_progress.store(current_progress);
if self.pushed_block_state.is_air() { if current_progress >= 1.0 {
world let pos = self.position;
.clone() world.remove_block_entity(&pos).await;
.set_block_state( if world.get_block(&pos).await == &Block::MOVING_PISTON {
&pos, if self.pushed_block_state.is_air() {
self.pushed_block_state.id, world
BlockFlags::FORCE_STATE | BlockFlags::MOVED, .clone()
) .set_block_state(
.await; &pos,
} else { self.pushed_block_state.id,
world BlockFlags::FORCE_STATE | BlockFlags::MOVED,
.clone() )
.set_block_state( .await;
&pos, } else {
self.pushed_block_state.id, world
BlockFlags::NOTIFY_ALL | BlockFlags::MOVED, .clone()
) .set_block_state(
.await; &pos,
world self.pushed_block_state.id,
.clone() BlockFlags::NOTIFY_ALL | BlockFlags::MOVED,
.update_neighbor(&pos, Block::from_state_id(self.pushed_block_state.id)) )
.await; .await;
world
.clone()
.update_neighbor(&pos, Block::from_state_id(self.pushed_block_state.id))
.await;
}
} }
} }
} self.current_progress.store(current_progress + 0.5);
self.current_progress.store(current_progress + 0.5); if current_progress + 0.5 >= 1.0 {
if current_progress + 0.5 >= 1.0 { self.current_progress.store(1.0);
self.current_progress.store(1.0); }
} })
} }
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
@ -119,12 +122,17 @@ impl BlockEntity for PistonBlockEntity {
} }
} }
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { fn write_nbt<'a>(
// TODO: pushed_block_state &'a self,
nbt.put_byte(FACING, self.facing.to_index() as i8); nbt: &'a mut NbtCompound,
nbt.put_float(LAST_PROGRESS, self.last_progress.load()); ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
nbt.put_bool(EXTENDING, self.extending); Box::pin(async move {
nbt.put_bool(SOURCE, self.source); // 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<NbtCompound> { fn chunk_data_nbt(&self) -> Option<NbtCompound> {

View File

@ -1,16 +1,18 @@
use async_trait::async_trait; use async_trait::async_trait;
use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::random::xoroshiro128::Xoroshiro; use pumpkin_util::random::xoroshiro128::Xoroshiro;
use pumpkin_util::random::{RandomImpl, get_seed}; use pumpkin_util::random::{RandomImpl, get_seed};
use std::any::Any; use std::any::Any;
use std::pin::Pin;
use std::{ use std::{
array::from_fn, array::from_fn,
sync::{Arc, atomic::AtomicBool}, sync::{Arc, atomic::AtomicBool},
}; };
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker}; use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture};
use crate::world::SimpleWorld; use crate::world::SimpleWorld;
use crate::{ use crate::{
inventory::{ inventory::{
@ -31,7 +33,6 @@ pub struct ShulkerBoxBlockEntity {
viewers: ViewerCountTracker, viewers: ViewerCountTracker,
} }
#[async_trait]
impl BlockEntity for ShulkerBoxBlockEntity { impl BlockEntity for ShulkerBoxBlockEntity {
fn resource_location(&self) -> &'static str { fn resource_location(&self) -> &'static str {
Self::ID Self::ID
@ -57,20 +58,39 @@ impl BlockEntity for ShulkerBoxBlockEntity {
shulker_box shulker_box
} }
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { fn write_nbt<'a>(
self.write_data(nbt, &self.items, true).await; &'a self,
nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
self.write_data(nbt, &self.items, true).await;
})
// Safety precaution // Safety precaution
//self.clear().await; //self.clear().await;
} }
async fn tick(&self, world: Arc<dyn SimpleWorld>) { fn tick<'a>(
self.viewers &'a self,
.update_viewer_count::<ShulkerBoxBlockEntity>(self, world, &self.position) world: Arc<dyn SimpleWorld>,
.await; ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
self.viewers
.update_viewer_count::<ShulkerBoxBlockEntity>(self, world, &self.position)
.await;
})
} }
async fn on_block_replaced(self: Arc<Self>, _world: Arc<dyn SimpleWorld>, _position: BlockPos) { fn on_block_replaced<'a>(
// Do nothing self: Arc<Self>,
_world: Arc<dyn SimpleWorld>,
_position: BlockPos,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>
where
Self: 'a,
{
Box::pin(async move {
// Do nothing
})
} }
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> { fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
@ -86,30 +106,43 @@ impl BlockEntity for ShulkerBoxBlockEntity {
} }
} }
#[async_trait]
impl ViewerCountListener for ShulkerBoxBlockEntity { impl ViewerCountListener for ShulkerBoxBlockEntity {
async fn on_container_open(&self, world: &Arc<dyn SimpleWorld>, position: &BlockPos) { fn on_container_open<'a>(
self.play_sound(world, position, Sound::BlockShulkerBoxOpen) &'a self,
.await; world: &'a Arc<dyn SimpleWorld>,
// TODO: this.world.emitGameEvent(player, GameEvent.CONTAINER_OPEN, this.pos); position: &'a BlockPos,
) -> ViewerFuture<'a, ()> {
Box::pin(async move {
self.play_sound(world, position, Sound::BlockShulkerBoxOpen)
.await;
// TODO: this.world.emitGameEvent(player, GameEvent.CONTAINER_OPEN, this.pos);
})
} }
async fn on_container_close(&self, world: &Arc<dyn SimpleWorld>, position: &BlockPos) { fn on_container_close<'a>(
self.play_sound(world, position, Sound::BlockShulkerBoxClose) &'a self,
.await; world: &'a Arc<dyn SimpleWorld>,
// TODO: this.world.emitGameEvent(player, GameEvent.CONTAINER_CLOSE, this.pos); position: &'a BlockPos,
) -> ViewerFuture<'a, ()> {
Box::pin(async move {
self.play_sound(world, position, Sound::BlockShulkerBoxClose)
.await;
// TODO: this.world.emitGameEvent(player, GameEvent.CONTAINER_CLOSE, this.pos);
})
} }
async fn on_viewer_count_update( fn on_viewer_count_update<'a>(
&self, &'a self,
world: &Arc<dyn SimpleWorld>, world: &'a Arc<dyn SimpleWorld>,
position: &BlockPos, position: &'a BlockPos,
_old: u16, _old: u16,
new: u16, new: u16,
) { ) -> ViewerFuture<'a, ()> {
world Box::pin(async move {
.add_synced_block_event(*position, Self::OPEN_ANIMATION_EVENT_TYPE, new as u8) world
.await .add_synced_block_event(*position, Self::OPEN_ANIMATION_EVENT_TYPE, new as u8)
.await
})
} }
} }
@ -194,11 +227,12 @@ impl Inventory for ShulkerBoxBlockEntity {
} }
} }
#[async_trait]
impl Clearable for ShulkerBoxBlockEntity { impl Clearable for ShulkerBoxBlockEntity {
async fn clear(&self) { fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
for slot in self.items.iter() { Box::pin(async move {
*slot.lock().await = ItemStack::EMPTY.clone(); for slot in self.items.iter() {
} *slot.lock().await = ItemStack::EMPTY.clone();
}
})
} }
} }

View File

@ -1,10 +1,12 @@
use std::sync::{ use std::{
Arc, pin::Pin,
atomic::{AtomicBool, AtomicI8, Ordering}, sync::{
Arc,
atomic::{AtomicBool, AtomicI8, Ordering},
},
}; };
use super::BlockEntity; use super::BlockEntity;
use async_trait::async_trait;
use num_derive::FromPrimitive; use num_derive::FromPrimitive;
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag}; use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
@ -208,7 +210,6 @@ impl Text {
} }
} }
#[async_trait]
impl BlockEntity for SignBlockEntity { impl BlockEntity for SignBlockEntity {
fn resource_location(&self) -> &'static str { fn resource_location(&self) -> &'static str {
Self::ID Self::ID
@ -234,10 +235,15 @@ impl BlockEntity for SignBlockEntity {
} }
} }
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { fn write_nbt<'a>(
nbt.put("front_text", self.front_text.clone()); &'a self,
nbt.put("back_text", self.back_text.clone()); nbt: &'a mut NbtCompound,
nbt.put_bool("is_waxed", self.is_waxed.load(Ordering::Relaxed)); ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
nbt.put("front_text", self.front_text.clone());
nbt.put("back_text", self.back_text.clone());
nbt.put_bool("is_waxed", self.is_waxed.load(Ordering::Relaxed));
})
} }
fn chunk_data_nbt(&self) -> Option<NbtCompound> { fn chunk_data_nbt(&self) -> Option<NbtCompound> {

View File

@ -1,9 +1,11 @@
use std::sync::{ use std::{
Arc, pin::Pin,
atomic::{AtomicU16, Ordering}, sync::{
Arc,
atomic::{AtomicU16, Ordering},
},
}; };
use async_trait::async_trait;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use crate::{block::entities::BlockEntity, world::SimpleWorld}; use crate::{block::entities::BlockEntity, world::SimpleWorld};
@ -70,16 +72,32 @@ impl ViewerCountTracker {
} }
} }
#[async_trait] pub type ViewerFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait ViewerCountListener {
async fn on_container_open(&self, _world: &Arc<dyn SimpleWorld>, _position: &BlockPos) {} pub trait ViewerCountListener: Send + Sync {
async fn on_container_close(&self, _world: &Arc<dyn SimpleWorld>, _position: &BlockPos) {} fn on_container_open<'a>(
async fn on_viewer_count_update( &'a self,
&self, _world: &'a Arc<dyn SimpleWorld>,
_world: &Arc<dyn SimpleWorld>, _position: &'a BlockPos,
_position: &BlockPos, ) -> ViewerFuture<'a, ()> {
Box::pin(async {})
}
fn on_container_close<'a>(
&'a self,
_world: &'a Arc<dyn SimpleWorld>,
_position: &'a BlockPos,
) -> ViewerFuture<'a, ()> {
Box::pin(async {})
}
fn on_viewer_count_update<'a>(
&'a self,
_world: &'a Arc<dyn SimpleWorld>,
_position: &'a BlockPos,
_old: u16, _old: u16,
_new: u16, _new: u16,
) { ) -> ViewerFuture<'a, ()> {
Box::pin(async {})
} }
} }

View File

@ -10,6 +10,7 @@ use std::{
io::{Read, SeekFrom, Write}, io::{Read, SeekFrom, Write},
marker::PhantomData, marker::PhantomData,
path::{Path, PathBuf}, path::{Path, PathBuf},
pin::Pin,
time::{SystemTime, UNIX_EPOCH}, time::{SystemTime, UNIX_EPOCH},
}; };
use tokio::{ use tokio::{
@ -517,9 +518,10 @@ impl<S: SingleChunkDataSerializer> Default for AnvilChunkFile<S> {
} }
} }
#[async_trait]
pub trait SingleChunkDataSerializer: Send + Sync + Sized + Dirtiable { pub trait SingleChunkDataSerializer: Send + Sync + Sized + Dirtiable {
async fn to_bytes(&self) -> Result<Bytes, ChunkSerializingError>; fn to_bytes(
&self,
) -> Pin<Box<dyn Future<Output = Result<Bytes, ChunkSerializingError>> + Send + '_>>;
fn from_bytes(bytes: Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError>; fn from_bytes(bytes: Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError>;
fn position(&self) -> &Vector2<i32>; fn position(&self) -> &Vector2<i32>;
} }

View File

@ -1,6 +1,5 @@
use std::{collections::HashMap, io::Cursor, path::PathBuf}; use std::{collections::HashMap, io::Cursor, path::PathBuf, pin::Pin};
use async_trait::async_trait;
use bytes::Bytes; use bytes::Bytes;
use futures::future::join_all; use futures::future::join_all;
use pumpkin_data::{Block, chunk::ChunkStatus, fluid::Fluid}; use pumpkin_data::{Block, chunk::ChunkStatus, fluid::Fluid};
@ -30,7 +29,6 @@ use crate::block::BlockStateCodec;
pub mod anvil; pub mod anvil;
pub mod linear; pub mod linear;
#[async_trait]
impl SingleChunkDataSerializer for ChunkData { impl SingleChunkDataSerializer for ChunkData {
#[inline] #[inline]
fn from_bytes(bytes: Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError> { fn from_bytes(bytes: Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError> {
@ -38,8 +36,10 @@ impl SingleChunkDataSerializer for ChunkData {
} }
#[inline] #[inline]
async fn to_bytes(&self) -> Result<Bytes, ChunkSerializingError> { fn to_bytes(
self.internal_to_bytes().await &self,
) -> Pin<Box<dyn Future<Output = Result<Bytes, ChunkSerializingError>> + Send + '_>> {
Box::pin(async move { self.internal_to_bytes().await })
} }
#[inline] #[inline]
@ -247,7 +247,6 @@ impl Dirtiable for ChunkEntityData {
} }
} }
#[async_trait]
impl SingleChunkDataSerializer for ChunkEntityData { impl SingleChunkDataSerializer for ChunkEntityData {
#[inline] #[inline]
fn from_bytes(bytes: Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError> { fn from_bytes(bytes: Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError> {
@ -255,8 +254,10 @@ impl SingleChunkDataSerializer for ChunkEntityData {
} }
#[inline] #[inline]
async fn to_bytes(&self) -> Result<Bytes, ChunkSerializingError> { fn to_bytes(
self.internal_to_bytes() &self,
) -> Pin<Box<dyn Future<Output = Result<Bytes, ChunkSerializingError>> + Send + '_>> {
Box::pin(async move { self.internal_to_bytes() })
} }
#[inline] #[inline]

View File

@ -11,6 +11,7 @@ use crate::chunk::io::LoadedData::Loaded;
use crate::chunk::{ChunkData, ChunkHeightmapType, ChunkLight, ChunkSections, SubChunk}; use crate::chunk::{ChunkData, ChunkHeightmapType, ChunkLight, ChunkSections, SubChunk};
use crate::dimension::Dimension; use crate::dimension::Dimension;
use std::default::Default; use std::default::Default;
use std::pin::Pin;
use crate::generation::height_limit::HeightLimitView; use crate::generation::height_limit::HeightLimitView;
@ -19,7 +20,6 @@ use crate::generation::settings::{GenerationSettings, gen_settings_from_dimensio
use crate::level::{Level, SyncChunk}; use crate::level::{Level, SyncChunk};
use crate::world::{BlockAccessor, BlockRegistryExt}; use crate::world::{BlockAccessor, BlockRegistryExt};
use crate::{GlobalRandomConfig, ProtoChunk, ProtoNoiseRouters}; use crate::{GlobalRandomConfig, ProtoChunk, ProtoNoiseRouters};
use async_trait::async_trait;
use crossbeam::channel::{Receiver, Sender}; use crossbeam::channel::{Receiver, Sender};
use dashmap::DashMap; use dashmap::DashMap;
use itertools::Itertools; use itertools::Itertools;
@ -849,22 +849,29 @@ impl HeightLimitView for Cache {
} }
} }
#[async_trait]
impl BlockAccessor for Cache { impl BlockAccessor for Cache {
async fn get_block(&self, position: &BlockPos) -> &'static Block { fn get_block<'a>(
GenerationCache::get_block_state(self, &position.0).to_block() &'a self,
position: &'a BlockPos,
) -> Pin<Box<dyn Future<Output = &'static Block> + Send + 'a>> {
Box::pin(async move { GenerationCache::get_block_state(self, &position.0).to_block() })
} }
async fn get_block_state(&self, position: &BlockPos) -> &'static BlockState { fn get_block_state<'a>(
GenerationCache::get_block_state(self, &position.0).to_state() &'a self,
position: &'a BlockPos,
) -> Pin<Box<dyn Future<Output = &'static BlockState> + Send + 'a>> {
Box::pin(async move { GenerationCache::get_block_state(self, &position.0).to_state() })
} }
async fn get_block_and_state( fn get_block_and_state<'a>(
&self, &'a self,
position: &BlockPos, position: &'a BlockPos,
) -> (&'static Block, &'static BlockState) { ) -> Pin<Box<dyn Future<Output = (&'static Block, &'static BlockState)> + Send + 'a>> {
let id = GenerationCache::get_block_state(self, &position.0); Box::pin(async move {
(id.to_block(), id.to_state()) let id = GenerationCache::get_block_state(self, &position.0);
(id.to_block(), id.to_state())
})
} }
} }

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::{Block, BlockDirection}; use pumpkin_data::{Block, BlockDirection};
use pumpkin_util::{HeightMap, include_json_static}; use pumpkin_util::{HeightMap, include_json_static};
use serde::Deserialize; use serde::Deserialize;
@ -378,7 +377,6 @@ pub struct BlockFilterPlacementModifier {
predicate: BlockPredicate, predicate: BlockPredicate,
} }
#[async_trait]
impl ConditionalPlacementModifier for BlockFilterPlacementModifier { impl ConditionalPlacementModifier for BlockFilterPlacementModifier {
fn should_place<T: GenerationCache>( fn should_place<T: GenerationCache>(
&self, &self,
@ -399,7 +397,6 @@ pub struct SurfaceThresholdFilterPlacementModifier {
max_inclusive: Option<i32>, max_inclusive: Option<i32>,
} }
#[async_trait]
impl ConditionalPlacementModifier for SurfaceThresholdFilterPlacementModifier { impl ConditionalPlacementModifier for SurfaceThresholdFilterPlacementModifier {
fn should_place<T: GenerationCache>( fn should_place<T: GenerationCache>(
&self, &self,
@ -421,7 +418,6 @@ pub struct RarityFilterPlacementModifier {
chance: u32, chance: u32,
} }
#[async_trait]
impl ConditionalPlacementModifier for RarityFilterPlacementModifier { impl ConditionalPlacementModifier for RarityFilterPlacementModifier {
fn should_place<T: GenerationCache>( fn should_place<T: GenerationCache>(
&self, &self,
@ -465,7 +461,6 @@ pub struct SurfaceWaterDepthFilterPlacementModifier {
max_water_depth: i32, max_water_depth: i32,
} }
#[async_trait]
impl ConditionalPlacementModifier for SurfaceWaterDepthFilterPlacementModifier { impl ConditionalPlacementModifier for SurfaceWaterDepthFilterPlacementModifier {
fn should_place<T: GenerationCache>( fn should_place<T: GenerationCache>(
&self, &self,
@ -484,7 +479,6 @@ impl ConditionalPlacementModifier for SurfaceWaterDepthFilterPlacementModifier {
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct BiomePlacementModifier; pub struct BiomePlacementModifier;
#[async_trait]
impl ConditionalPlacementModifier for BiomePlacementModifier { impl ConditionalPlacementModifier for BiomePlacementModifier {
fn should_place<T: GenerationCache>( fn should_place<T: GenerationCache>(
&self, &self,
@ -563,7 +557,6 @@ pub trait CountPlacementModifierBase {
fn get_count(&self, random: &mut RandomGenerator, pos: BlockPos) -> i32; fn get_count(&self, random: &mut RandomGenerator, pos: BlockPos) -> i32;
} }
#[async_trait]
pub trait ConditionalPlacementModifier { pub trait ConditionalPlacementModifier {
fn get_positions<T: GenerationCache>( fn get_positions<T: GenerationCache>(
&self, &self,

View File

@ -1,6 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::pin::Pin;
use async_trait::async_trait;
use pumpkin_data::tag; use pumpkin_data::tag;
use pumpkin_data::{ use pumpkin_data::{
Block, BlockState, block_properties::blocks_movement, chunk::Biome, tag::Taggable, Block, BlockState, block_properties::blocks_movement, chunk::Biome, tag::Taggable,
@ -988,22 +988,29 @@ impl ProtoChunk {
} }
} }
#[async_trait]
impl BlockAccessor for ProtoChunk { impl BlockAccessor for ProtoChunk {
async fn get_block(&self, position: &BlockPos) -> &'static pumpkin_data::Block { fn get_block<'a>(
self.get_block_state(&position.0).to_block() &'a self,
position: &'a BlockPos,
) -> Pin<Box<dyn Future<Output = &'static Block> + Send + 'a>> {
Box::pin(async move { self.get_block_state(&position.0).to_block() })
} }
async fn get_block_state(&self, position: &BlockPos) -> &'static pumpkin_data::BlockState { fn get_block_state<'a>(
self.get_block_state(&position.0).to_state() &'a self,
position: &'a BlockPos,
) -> Pin<Box<dyn Future<Output = &'static BlockState> + Send + 'a>> {
Box::pin(async move { self.get_block_state(&position.0).to_state() })
} }
async fn get_block_and_state( fn get_block_and_state<'a>(
&self, &'a self,
position: &BlockPos, position: &'a BlockPos,
) -> (&'static Block, &'static BlockState) { ) -> Pin<Box<dyn Future<Output = (&'static Block, &'static BlockState)> + Send + 'a>> {
let id = self.get_block_state(&position.0); Box::pin(async move {
BlockState::from_id_with_block(id.0) let id = self.get_block_state(&position.0);
BlockState::from_id_with_block(id.0)
})
} }
} }

View File

@ -3,6 +3,7 @@ use async_trait::async_trait;
use pumpkin_data::item::Item; use pumpkin_data::item::Item;
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag}; use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
use std::any::Any; use std::any::Any;
use std::pin::Pin;
use std::{ use std::{
fmt::Debug, fmt::Debug,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
@ -139,9 +140,8 @@ pub trait Inventory: Send + Sync + Debug + Clearable {
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
} }
#[async_trait]
pub trait Clearable { pub trait Clearable {
async fn clear(&self); fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
} }
pub struct ComparableInventory(pub Arc<dyn Inventory>); pub struct ComparableInventory(pub Arc<dyn Inventory>);

View File

@ -1,8 +1,8 @@
use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use crate::block::entities::BlockEntity; use crate::block::entities::BlockEntity;
use crate::{BlockStateId, inventory::Inventory}; use crate::{BlockStateId, inventory::Inventory};
use async_trait::async_trait;
use bitflags::bitflags; use bitflags::bitflags;
use pumpkin_data::entity::EntityType; use pumpkin_data::entity::EntityType;
use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::sound::{Sound, SoundCategory};
@ -40,59 +40,81 @@ impl std::fmt::Display for GetBlockError {
} }
} }
#[async_trait] pub type WorldFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait SimpleWorld: BlockAccessor + Send + Sync { pub trait SimpleWorld: BlockAccessor + Send + Sync {
async fn set_block_state( fn set_block_state<'a>(
self: Arc<Self>, self: Arc<Self>,
position: &BlockPos, position: &'a BlockPos,
block_state_id: BlockStateId, block_state_id: BlockStateId,
flags: BlockFlags, flags: BlockFlags,
) -> BlockStateId; ) -> WorldFuture<'a, BlockStateId>;
async fn update_neighbor( fn update_neighbor<'a>(
self: Arc<Self>, self: Arc<Self>,
neighbor_block_pos: &BlockPos, neighbor_block_pos: &'a BlockPos,
source_block: &pumpkin_data::Block, source_block: &'a pumpkin_data::Block,
); ) -> WorldFuture<'a, ()>;
async fn update_neighbors( fn update_neighbors<'a>(
self: Arc<Self>, self: Arc<Self>,
block_pos: &BlockPos, block_pos: &'a BlockPos,
except: Option<BlockDirection>, except: Option<BlockDirection>,
); ) -> WorldFuture<'a, ()>;
async fn is_space_empty(&self, bounding_box: BoundingBox) -> bool; fn is_space_empty<'a>(&'a self, bounding_box: BoundingBox) -> WorldFuture<'a, bool>;
async fn spawn_from_type( fn spawn_from_type(
self: Arc<Self>, self: Arc<Self>,
entity_type: &'static EntityType, entity_type: &'static EntityType,
position: Vector3<f64>, position: Vector3<f64>,
); ) -> WorldFuture<'static, ()>;
async fn add_synced_block_event(&self, pos: BlockPos, r#type: u8, data: u8); fn add_synced_block_event<'a>(
&'a self,
pos: BlockPos,
r#type: u8,
data: u8,
) -> WorldFuture<'a, ()>;
async fn sync_world_event(&self, world_event: WorldEvent, position: BlockPos, data: i32); fn sync_world_event<'a>(
&'a self,
world_event: WorldEvent,
position: BlockPos,
data: i32,
) -> WorldFuture<'a, ()>;
async fn remove_block_entity(&self, block_pos: &BlockPos); fn remove_block_entity<'a>(&'a self, block_pos: &'a BlockPos) -> WorldFuture<'a, ()>;
async fn get_block_entity(&self, block_pos: &BlockPos) -> Option<Arc<dyn BlockEntity>>;
async fn get_world_age(&self) -> i64;
async fn play_sound(&self, sound: Sound, category: SoundCategory, position: &Vector3<f64>); fn get_block_entity<'a>(
async fn play_sound_fine( &'a self,
&self, block_pos: &'a BlockPos,
) -> WorldFuture<'a, Option<Arc<dyn BlockEntity>>>;
fn get_world_age<'a>(&'a self) -> WorldFuture<'a, i64>;
fn play_sound<'a>(
&'a self,
sound: Sound, sound: Sound,
category: SoundCategory, category: SoundCategory,
position: &Vector3<f64>, position: &'a Vector3<f64>,
) -> WorldFuture<'a, ()>;
fn play_sound_fine<'a>(
&'a self,
sound: Sound,
category: SoundCategory,
position: &'a Vector3<f64>,
volume: f32, volume: f32,
pitch: f32, pitch: f32,
); ) -> WorldFuture<'a, ()>;
/* ItemScatterer */ /* ItemScatterer */
async fn scatter_inventory( fn scatter_inventory<'a>(
self: Arc<Self>, self: Arc<Self>,
position: &BlockPos, position: &'a BlockPos,
inventory: &Arc<dyn Inventory>, inventory: &'a Arc<dyn Inventory>,
); ) -> WorldFuture<'a, ()>;
} }
pub trait BlockRegistryExt: Send + Sync { pub trait BlockRegistryExt: Send + Sync {
@ -105,14 +127,19 @@ pub trait BlockRegistryExt: Send + Sync {
) -> bool; ) -> bool;
} }
#[async_trait]
pub trait BlockAccessor: Send + Sync { pub trait BlockAccessor: Send + Sync {
async fn get_block(&self, position: &BlockPos) -> &'static Block; fn get_block<'a>(
&'a self,
position: &'a BlockPos,
) -> Pin<Box<dyn Future<Output = &'static Block> + Send + 'a>>;
async fn get_block_state(&self, position: &BlockPos) -> &'static BlockState; fn get_block_state<'a>(
&'a self,
position: &'a BlockPos,
) -> Pin<Box<dyn Future<Output = &'static BlockState> + Send + 'a>>;
async fn get_block_and_state( fn get_block_and_state<'a>(
&self, &'a self,
position: &BlockPos, position: &'a BlockPos,
) -> (&'static Block, &'static BlockState); ) -> Pin<Box<dyn Future<Output = (&'static Block, &'static BlockState)> + Send + 'a>>;
} }

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::{ use pumpkin_data::{
Block, BlockDirection, Block, BlockDirection,
block_properties::{BlockFace, HorizontalFacing}, block_properties::{BlockFace, HorizontalFacing},
@ -6,10 +5,12 @@ use pumpkin_data::{
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{BlockStateId, world::BlockAccessor}; use pumpkin_world::{BlockStateId, world::BlockAccessor};
use crate::{block::GetStateForNeighborUpdateArgs, entity::player::Player}; use crate::{
block::{BlockFuture, GetStateForNeighborUpdateArgs},
entity::player::Player,
};
#[async_trait] pub trait WallMountedBlock: Send + Sync {
pub trait WallMountedBlock {
fn get_direction(&self, state_id: BlockStateId, block: &Block) -> BlockDirection; fn get_direction(&self, state_id: BlockStateId, block: &Block) -> BlockDirection;
fn get_placement_face( fn get_placement_face(
@ -32,29 +33,33 @@ pub trait WallMountedBlock {
(face, facing) (face, facing)
} }
async fn can_place_at( fn can_place_at<'a>(
&self, &'a self,
world: &dyn BlockAccessor, world: &'a dyn BlockAccessor,
pos: &BlockPos, pos: &'a BlockPos,
direction: BlockDirection, direction: BlockDirection,
) -> bool { ) -> BlockFuture<'a, bool> {
let block_pos = pos.offset(direction.to_offset()); Box::pin(async move {
let block_state = world.get_block_state(&block_pos).await; let block_pos = pos.offset(direction.to_offset());
block_state.is_side_solid(direction.opposite()) let block_state = world.get_block_state(&block_pos).await;
block_state.is_side_solid(direction.opposite())
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
if self.get_direction(args.state_id, args.block).opposite() == args.direction Box::pin(async move {
&& !self if self.get_direction(args.state_id, args.block).opposite() == args.direction
.can_place_at(args.world, args.position, args.direction) && !self
.await .can_place_at(args.world, args.position, args.direction)
{ .await
Block::AIR.default_state.id {
} else { Block::AIR.default_state.id
args.state_id } else {
} args.state_id
}
})
} }
} }

View File

@ -1,8 +1,8 @@
use crate::block::blocks::falling::FallingBlock; use crate::block::blocks::falling::FallingBlock;
use crate::block::{ use crate::block::{
BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs,
PlacedArgs,
}; };
use async_trait::async_trait;
use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties}; use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties};
use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_data::tag::{RegistryKey, get_tag_values};
use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_macros::pumpkin_block_from_tag;
@ -11,33 +11,40 @@ use pumpkin_world::BlockStateId;
#[pumpkin_block_from_tag("minecraft:anvil")] #[pumpkin_block_from_tag("minecraft:anvil")]
pub struct AnvilBlock; pub struct AnvilBlock;
#[async_trait]
impl BlockBehaviour for AnvilBlock { impl BlockBehaviour for AnvilBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let dir = args Box::pin(async move {
.player let dir = args
.living_entity .player
.entity .living_entity
.get_horizontal_facing() .entity
.rotate_clockwise(); .get_horizontal_facing()
.rotate_clockwise();
let mut props = WallTorchLikeProperties::default(args.block); let mut props = WallTorchLikeProperties::default(args.block);
props.facing = dir; props.facing = dir;
props.to_state_id(args.block) props.to_state_id(args.block)
})
} }
async fn placed(&self, args: PlacedArgs<'_>) { fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
FallingBlock::placed(&FallingBlock, args).await; Box::pin(async move {
FallingBlock::placed(&FallingBlock, args).await;
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
FallingBlock::get_state_for_neighbor_update(&FallingBlock, args).await Box::pin(
async move { FallingBlock::get_state_for_neighbor_update(&FallingBlock, args).await },
)
} }
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
FallingBlock::on_scheduled_tick(&FallingBlock, args).await; Box::pin(async move {
FallingBlock::on_scheduled_tick(&FallingBlock, args).await;
})
} }
} }

View File

@ -1,17 +1,17 @@
use async_trait::async_trait;
use pumpkin_data::tag; use pumpkin_data::tag;
use pumpkin_data::tag::Taggable; use pumpkin_data::tag::Taggable;
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use crate::block::{BlockBehaviour, CanPlaceAtArgs}; use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs};
#[pumpkin_block("minecraft:bamboo")] #[pumpkin_block("minecraft:bamboo")]
pub struct BambooBlock; pub struct BambooBlock;
#[async_trait]
impl BlockBehaviour for BambooBlock { impl BlockBehaviour for BambooBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
let block_below = args.block_accessor.get_block(&args.position.down()).await; Box::pin(async move {
block_below.has_tag(&tag::Block::MINECRAFT_BAMBOO_PLANTABLE_ON) 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,5 @@
use crate::block::{BlockBehaviour, OnPlaceArgs}; use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs};
use crate::entity::EntityBase; use crate::entity::EntityBase;
use async_trait::async_trait;
use pumpkin_data::block_properties::{BlockProperties, WhiteBannerLikeProperties}; use pumpkin_data::block_properties::{BlockProperties, WhiteBannerLikeProperties};
use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_data::tag::{RegistryKey, get_tag_values};
use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_macros::pumpkin_block_from_tag;
@ -9,11 +8,12 @@ use pumpkin_world::BlockStateId;
#[pumpkin_block_from_tag("minecraft:banners")] #[pumpkin_block_from_tag("minecraft:banners")]
pub struct BannerBlock; pub struct BannerBlock;
#[async_trait]
impl BlockBehaviour for BannerBlock { impl BlockBehaviour for BannerBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut props = WhiteBannerLikeProperties::default(args.block); Box::pin(async move {
props.rotation = args.player.get_entity().get_flipped_rotation_16(); let mut props = WhiteBannerLikeProperties::default(args.block);
props.to_state_id(args.block) props.rotation = args.player.get_entity().get_flipped_rotation_16();
props.to_state_id(args.block)
})
} }
} }

View File

@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::{OnPlaceArgs, PlacedArgs}; use crate::block::{BlockFuture, OnPlaceArgs, PlacedArgs};
use crate::block::{ use crate::block::{
registry::BlockActionResult, registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs}, {BlockBehaviour, NormalUseArgs},
@ -40,30 +40,35 @@ impl ScreenHandlerFactory for BarrelScreenFactory {
#[pumpkin_block("minecraft:barrel")] #[pumpkin_block("minecraft:barrel")]
pub struct BarrelBlock; pub struct BarrelBlock;
#[async_trait]
impl BlockBehaviour for BarrelBlock { impl BlockBehaviour for BarrelBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut props = BarrelLikeProperties::default(args.block); Box::pin(async move {
props.facing = args.player.living_entity.entity.get_facing().opposite(); let mut props = BarrelLikeProperties::default(args.block);
props.to_state_id(args.block) props.facing = args.player.living_entity.entity.get_facing().opposite();
props.to_state_id(args.block)
})
} }
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
if let Some(block_entity) = args.world.get_block_entity(args.position).await Box::pin(async move {
&& let Some(inventory) = block_entity.get_inventory() if let Some(block_entity) = args.world.get_block_entity(args.position).await
{ && let Some(inventory) = block_entity.get_inventory()
args.player {
.open_handled_screen(&BarrelScreenFactory(inventory)) args.player
.open_handled_screen(&BarrelScreenFactory(inventory))
.await;
}
BlockActionResult::Success
})
}
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
let barrel_block_entity = BarrelBlockEntity::new(*args.position);
args.world
.add_block_entity(Arc::new(barrel_block_entity))
.await; .await;
} })
BlockActionResult::Success
}
async fn placed(&self, args: PlacedArgs<'_>) {
let barrel_block_entity = BarrelBlockEntity::new(*args.position);
args.world
.add_block_entity(Arc::new(barrel_block_entity))
.await;
} }
} }

View File

@ -1,5 +1,4 @@
use crate::block::{BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs}; use crate::block::{BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs};
use async_trait::async_trait;
use pumpkin_data::block_properties::{ use pumpkin_data::block_properties::{
BlockProperties, MangroveRootsLikeProperties as BarrierLikeProperties, BlockProperties, MangroveRootsLikeProperties as BarrierLikeProperties,
}; };
@ -11,29 +10,32 @@ use pumpkin_world::tick::TickPriority;
#[pumpkin_block("minecraft:barrier")] #[pumpkin_block("minecraft:barrier")]
pub struct BarrierBlock; pub struct BarrierBlock;
#[async_trait]
impl BlockBehaviour for BarrierBlock { impl BlockBehaviour for BarrierBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut props = BarrierLikeProperties::default(args.block); Box::pin(async move {
props.waterlogged = args.replacing.water_source(); let mut props = BarrierLikeProperties::default(args.block);
props.to_state_id(args.block) props.waterlogged = args.replacing.water_source();
props.to_state_id(args.block)
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
let props = BarrierLikeProperties::from_state_id(args.state_id, args.block); Box::pin(async move {
if props.waterlogged { let props = BarrierLikeProperties::from_state_id(args.state_id, args.block);
args.world if props.waterlogged {
.schedule_fluid_tick( args.world
&Fluid::WATER, .schedule_fluid_tick(
*args.position, &Fluid::WATER,
Fluid::WATER.flow_speed as u8, *args.position,
TickPriority::Normal, Fluid::WATER.flow_speed as u8,
) TickPriority::Normal,
.await; )
} .await;
props.to_state_id(args.block) }
props.to_state_id(args.block)
})
} }
} }

View File

@ -1,6 +1,5 @@
use std::sync::Arc; use std::sync::Arc;
use async_trait::async_trait;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_data::block_properties::BedPart; use pumpkin_data::block_properties::BedPart;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
@ -15,6 +14,7 @@ use pumpkin_world::BlockStateId;
use pumpkin_world::block::entities::bed::BedBlockEntity; use pumpkin_world::block::entities::bed::BedBlockEntity;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;
use crate::block::BlockFuture;
use crate::block::registry::BlockActionResult; use crate::block::registry::BlockActionResult;
use crate::block::{ use crate::block::{
BlockBehaviour, BrokenArgs, CanPlaceAtArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs, BlockBehaviour, BrokenArgs, CanPlaceAtArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs,
@ -65,214 +65,223 @@ const NO_SLEEP_IDS: &[u16] = &[
#[pumpkin_block_from_tag("minecraft:beds")] #[pumpkin_block_from_tag("minecraft:beds")]
pub struct BedBlock; pub struct BedBlock;
#[async_trait]
impl BlockBehaviour for BedBlock { impl BlockBehaviour for BedBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
if let Some(player) = args.player { Box::pin(async move {
let facing = player.living_entity.entity.get_horizontal_facing(); if let Some(player) = args.player {
return args let facing = player.living_entity.entity.get_horizontal_facing();
.block_accessor return args
.get_block_state(args.position)
.await
.replaceable()
&& args
.block_accessor .block_accessor
.get_block_state(&args.position.offset(facing.to_offset())) .get_block_state(args.position)
.await .await
.replaceable(); .replaceable()
} && args
false .block_accessor
.get_block_state(&args.position.offset(facing.to_offset()))
.await
.replaceable();
}
false
})
} }
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut bed_props = BedProperties::default(args.block); Box::pin(async move {
let mut bed_props = BedProperties::default(args.block);
bed_props.facing = args.player.living_entity.entity.get_horizontal_facing(); bed_props.facing = args.player.living_entity.entity.get_horizontal_facing();
bed_props.part = BedPart::Foot; bed_props.part = BedPart::Foot;
bed_props.to_state_id(args.block) bed_props.to_state_id(args.block)
})
} }
async fn placed(&self, args: PlacedArgs<'_>) { fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
let bed_entity = BedBlockEntity::new(*args.position); Box::pin(async move {
args.world.add_block_entity(Arc::new(bed_entity)).await; let bed_entity = BedBlockEntity::new(*args.position);
args.world.add_block_entity(Arc::new(bed_entity)).await;
let mut bed_head_props = BedProperties::default(args.block); let mut bed_head_props = BedProperties::default(args.block);
bed_head_props.facing = BedProperties::from_state_id(args.state_id, args.block).facing; bed_head_props.facing = BedProperties::from_state_id(args.state_id, args.block).facing;
bed_head_props.part = BedPart::Head; bed_head_props.part = BedPart::Head;
let bed_head_pos = args.position.offset(bed_head_props.facing.to_offset()); let bed_head_pos = args.position.offset(bed_head_props.facing.to_offset());
args.world args.world
.set_block_state( .set_block_state(
&bed_head_pos, &bed_head_pos,
bed_head_props.to_state_id(args.block), bed_head_props.to_state_id(args.block),
BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK,
) )
.await; .await;
let bed_head_entity = BedBlockEntity::new(bed_head_pos); let bed_head_entity = BedBlockEntity::new(bed_head_pos);
args.world.add_block_entity(Arc::new(bed_head_entity)).await; args.world.add_block_entity(Arc::new(bed_head_entity)).await;
})
} }
async fn broken(&self, args: BrokenArgs<'_>) { fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
let bed_props = BedProperties::from_state_id(args.state.id, args.block); Box::pin(async move {
let other_half_pos = if bed_props.part == BedPart::Head { let bed_props = BedProperties::from_state_id(args.state.id, args.block);
args.position let other_half_pos = if bed_props.part == BedPart::Head {
.offset(bed_props.facing.opposite().to_offset()) args.position
} else { .offset(bed_props.facing.opposite().to_offset())
args.position.offset(bed_props.facing.to_offset()) } else {
}; args.position.offset(bed_props.facing.to_offset())
};
args.world args.world
.break_block( .break_block(
&other_half_pos, &other_half_pos,
Some(args.player.clone()), Some(args.player.clone()),
if args.player.gamemode.load() == GameMode::Creative { if args.player.gamemode.load() == GameMode::Creative {
BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_NEIGHBORS BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_NEIGHBORS
} else { } else {
BlockFlags::NOTIFY_NEIGHBORS BlockFlags::NOTIFY_NEIGHBORS
}, },
) )
.await; .await;
})
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
let state_id = args.world.get_block_state_id(args.position).await; Box::pin(async move {
let bed_props = BedProperties::from_state_id(state_id, args.block); let state_id = args.world.get_block_state_id(args.position).await;
let bed_props = BedProperties::from_state_id(state_id, args.block);
let (bed_head_pos, bed_foot_pos) = if bed_props.part == BedPart::Head { let (bed_head_pos, bed_foot_pos) = if bed_props.part == BedPart::Head {
( (
*args.position, *args.position,
args.position args.position
.offset(bed_props.facing.opposite().to_offset()), .offset(bed_props.facing.opposite().to_offset()),
) )
} else { } else {
( (
args.position.offset(bed_props.facing.to_offset()), args.position.offset(bed_props.facing.to_offset()),
*args.position, *args.position,
) )
}; };
// Explode if not in the overworld // Explode if not in the overworld
if args.world.dimension_type != VanillaDimensionType::Overworld { if args.world.dimension_type != VanillaDimensionType::Overworld {
args.world args.world
.break_block(&bed_head_pos, None, BlockFlags::SKIP_DROPS) .break_block(&bed_head_pos, None, BlockFlags::SKIP_DROPS)
.await; .await;
args.world args.world
.break_block(&bed_foot_pos, None, BlockFlags::SKIP_DROPS) .break_block(&bed_foot_pos, None, BlockFlags::SKIP_DROPS)
.await; .await;
args.world args.world
.explode(bed_head_pos.to_centered_f64(), 5.0) .explode(bed_head_pos.to_centered_f64(), 5.0)
.await; .await;
return BlockActionResult::SuccessServer; return BlockActionResult::SuccessServer;
} }
// Make sure the bed is not obstructed // Make sure the bed is not obstructed
if args if args
.world
.get_block_state(&bed_head_pos.up())
.await
.is_solid()
|| args
.world .world
.get_block_state(&bed_head_pos.up()) .get_block_state(&bed_head_pos.up())
.await .await
.is_solid() .is_solid()
{ || args
args.player .world
.send_system_message_raw( .get_block_state(&bed_head_pos.up())
&TextComponent::translate("block.minecraft.bed.obstructed", []), .await
true, .is_solid()
)
.await;
return BlockActionResult::SuccessServer;
}
// Make sure the bed is not occupied
if bed_props.occupied {
// TODO: Wake up villager
args.player
.send_system_message_raw(
&TextComponent::translate("block.minecraft.bed.occupied", []),
true,
)
.await;
return BlockActionResult::SuccessServer;
}
// Make sure player is close enough
if !args
.player
.position()
.is_within_bounds(bed_head_pos.to_f64(), 3.0, 3.0, 3.0)
&& !args
.player
.position()
.is_within_bounds(bed_foot_pos.to_f64(), 3.0, 3.0, 3.0)
{
args.player
.send_system_message_raw(
&TextComponent::translate("block.minecraft.bed.too_far_away", []),
true,
)
.await;
return BlockActionResult::SuccessServer;
}
// Set respawn point
if args
.player
.set_respawn_point(
args.world.dimension_type,
bed_head_pos,
args.player.get_entity().yaw.load(),
)
.await
{
args.player
.send_system_message(&TextComponent::translate("block.minecraft.set_spawn", []))
.await;
}
// Make sure the time and weather allows sleep
if !can_sleep(args.world).await {
args.player
.send_system_message_raw(
&TextComponent::translate("block.minecraft.bed.no_sleep", []),
true,
)
.await;
return BlockActionResult::SuccessServer;
}
// Make sure there are no monsters nearby
for entity in args.world.entities.read().await.values() {
if !entity_prevents_sleep(entity.get_entity()) {
continue;
}
let pos = entity.get_entity().pos.load();
if pos.is_within_bounds(bed_head_pos.to_f64(), 8.0, 5.0, 8.0)
|| pos.is_within_bounds(bed_foot_pos.to_f64(), 8.0, 5.0, 8.0)
{ {
args.player args.player
.send_system_message_raw( .send_system_message_raw(
&TextComponent::translate("block.minecraft.bed.not_safe", []), &TextComponent::translate("block.minecraft.bed.obstructed", []),
true, true,
) )
.await; .await;
return BlockActionResult::SuccessServer; return BlockActionResult::SuccessServer;
} }
}
args.player.sleep(bed_head_pos).await; // Make sure the bed is not occupied
Self::set_occupied(true, args.world, args.block, args.position, state_id).await; if bed_props.occupied {
// TODO: Wake up villager
BlockActionResult::SuccessServer args.player
.send_system_message_raw(
&TextComponent::translate("block.minecraft.bed.occupied", []),
true,
)
.await;
return BlockActionResult::SuccessServer;
}
// Make sure player is close enough
if !args
.player
.position()
.is_within_bounds(bed_head_pos.to_f64(), 3.0, 3.0, 3.0)
&& !args
.player
.position()
.is_within_bounds(bed_foot_pos.to_f64(), 3.0, 3.0, 3.0)
{
args.player
.send_system_message_raw(
&TextComponent::translate("block.minecraft.bed.too_far_away", []),
true,
)
.await;
return BlockActionResult::SuccessServer;
}
// Set respawn point
if args
.player
.set_respawn_point(
args.world.dimension_type,
bed_head_pos,
args.player.get_entity().yaw.load(),
)
.await
{
args.player
.send_system_message(&TextComponent::translate("block.minecraft.set_spawn", []))
.await;
}
// Make sure the time and weather allows sleep
if !can_sleep(args.world).await {
args.player
.send_system_message_raw(
&TextComponent::translate("block.minecraft.bed.no_sleep", []),
true,
)
.await;
return BlockActionResult::SuccessServer;
}
// Make sure there are no monsters nearby
for entity in args.world.entities.read().await.values() {
if !entity_prevents_sleep(entity.get_entity()) {
continue;
}
let pos = entity.get_entity().pos.load();
if pos.is_within_bounds(bed_head_pos.to_f64(), 8.0, 5.0, 8.0)
|| pos.is_within_bounds(bed_foot_pos.to_f64(), 8.0, 5.0, 8.0)
{
args.player
.send_system_message_raw(
&TextComponent::translate("block.minecraft.bed.not_safe", []),
true,
)
.await;
return BlockActionResult::SuccessServer;
}
}
args.player.sleep(bed_head_pos).await;
Self::set_occupied(true, args.world, args.block, args.position, state_id).await;
BlockActionResult::SuccessServer
})
} }
} }

View File

@ -1,5 +1,4 @@
use crate::block::{BlockBehaviour, OnPlaceArgs}; use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs};
use async_trait::async_trait;
use pumpkin_data::block_properties::{BlockProperties, FurnaceLikeProperties}; use pumpkin_data::block_properties::{BlockProperties, FurnaceLikeProperties};
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
@ -7,16 +6,17 @@ use pumpkin_world::BlockStateId;
#[pumpkin_block("minecraft:blast_furnace")] #[pumpkin_block("minecraft:blast_furnace")]
pub struct BlastFurnaceBlock; pub struct BlastFurnaceBlock;
#[async_trait]
impl BlockBehaviour for BlastFurnaceBlock { impl BlockBehaviour for BlastFurnaceBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut props = FurnaceLikeProperties::default(args.block); Box::pin(async move {
props.facing = args let mut props = FurnaceLikeProperties::default(args.block);
.player props.facing = args
.living_entity .player
.entity .living_entity
.get_horizontal_facing() .entity
.opposite(); .get_horizontal_facing()
props.to_state_id(args.block) .opposite();
props.to_state_id(args.block)
})
} }
} }

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::block_properties::{ use pumpkin_data::block_properties::{
BlockProperties, CactusLikeProperties, EnumVariants, Integer0To15, BlockProperties, CactusLikeProperties, EnumVariants, Integer0To15,
}; };
@ -11,82 +10,87 @@ use pumpkin_world::tick::TickPriority;
use pumpkin_world::world::{BlockAccessor, BlockFlags}; use pumpkin_world::world::{BlockAccessor, BlockFlags};
use crate::block::{ use crate::block::{
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs, BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
OnScheduledTickArgs, RandomTickArgs, OnScheduledTickArgs, RandomTickArgs,
}; };
#[pumpkin_block("minecraft:cactus")] #[pumpkin_block("minecraft:cactus")]
pub struct CactusBlock; pub struct CactusBlock;
#[async_trait]
impl BlockBehaviour for CactusBlock { impl BlockBehaviour for CactusBlock {
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
if !can_place_at(args.world.as_ref(), args.position).await { Box::pin(async move {
args.world if !can_place_at(args.world.as_ref(), args.position).await {
.break_block(args.position, None, BlockFlags::empty())
.await;
}
}
async fn random_tick(&self, args: RandomTickArgs<'_>) {
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 {
args.world args.world
.set_block_state( .break_block(args.position, None, BlockFlags::empty())
&args.position.up(),
Block::CACTUS.default_state.id,
BlockFlags::empty(),
)
.await;
args.world
.set_block_state(
args.position,
Block::CACTUS.default_state.id,
BlockFlags::empty(),
)
.await;
} else {
let props = CactusLikeProperties {
age: Integer0To15::from_index(age.to_index() + 1),
};
args.world
.set_block_state(
args.position,
props.to_state_id(args.block),
BlockFlags::empty(),
)
.await; .await;
} }
} })
} }
async fn on_entity_collision(&self, _args: OnEntityCollisionArgs<'_>) { fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
// TODO Box::pin(async move {
//args.entity.damage(1.0, DamageType::CACTUS).await; 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 {
args.world
.set_block_state(
&args.position.up(),
Block::CACTUS.default_state.id,
BlockFlags::empty(),
)
.await;
args.world
.set_block_state(
args.position,
Block::CACTUS.default_state.id,
BlockFlags::empty(),
)
.await;
} else {
let props = CactusLikeProperties {
age: Integer0To15::from_index(age.to_index() + 1),
};
args.world
.set_block_state(
args.position,
props.to_state_id(args.block),
BlockFlags::empty(),
)
.await;
}
}
})
} }
async fn get_state_for_neighbor_update( // async fn on_entity_collision(&self, _args: OnEntityCollisionArgs<'_>) {
&self, // // TODO
args: GetStateForNeighborUpdateArgs<'_>, // //args.entity.damage(1.0, DamageType::CACTUS).await;
) -> BlockStateId { // }
if !can_place_at(args.world, args.position).await {
args.world
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
.await;
}
args.state_id fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
if !can_place_at(args.world, args.position).await {
args.world
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
.await;
}
args.state_id
})
} }
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
can_place_at(args.block_accessor, args.position).await Box::pin(async move { can_place_at(args.block_accessor, args.position).await })
} }
} }

View File

@ -2,14 +2,12 @@ use std::sync::Arc;
use crate::{ use crate::{
block::{ block::{
blocks::candle_cakes::cake_from_candle, BlockBehaviour, BlockFuture, NormalUseArgs, UseWithItemArgs,
registry::BlockActionResult, blocks::candle_cakes::cake_from_candle, registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs, UseWithItemArgs},
}, },
entity::player::Player, entity::player::Player,
world::World, world::World,
}; };
use async_trait::async_trait;
use pumpkin_data::item::Item; use pumpkin_data::item::Item;
use pumpkin_data::{ use pumpkin_data::{
Block, Block,
@ -78,17 +76,56 @@ impl CakeBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for CakeBlock { impl BlockBehaviour for CakeBlock {
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { fn use_with_item<'a>(
let state_id = args.world.get_block_state_id(args.position).await; &'a self,
let properties = CakeLikeProperties::from_state_id(state_id, args.block); args: UseWithItemArgs<'a>,
let item_lock = args.item_stack.lock().await; ) -> BlockFuture<'a, BlockActionResult> {
let item = item_lock.item; Box::pin(async move {
drop(item_lock); let state_id = args.world.get_block_state_id(args.position).await;
match item.id { let properties = CakeLikeProperties::from_state_id(state_id, args.block);
id if (Item::CANDLE.id..=Item::BLACK_CANDLE.id).contains(&id) => { let item_lock = args.item_stack.lock().await;
if properties.bites.to_index() != 0 { let item = item_lock.item;
drop(item_lock);
match item.id {
id if (Item::CANDLE.id..=Item::BLACK_CANDLE.id).contains(&id) => {
if properties.bites.to_index() != 0 {
return Self::consume_if_hungry(
args.world,
args.player,
args.block,
args.position,
state_id,
)
.await;
}
if args.player.gamemode.load() != GameMode::Creative {
let held_item = args.player.inventory.held_item();
let mut held_item_guard = held_item.lock().await;
held_item_guard.decrement(1);
}
args.world
.set_block_state(
args.position,
cake_from_candle(item).default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
let seed: f64 = rng().random();
args.player
.play_sound(
Sound::BlockCakeAddCandle as u16,
SoundCategory::Blocks,
&args.position.to_f64(),
1.0,
1.0,
seed,
)
.await;
BlockActionResult::Consume
}
_ => {
return Self::consume_if_hungry( return Self::consume_if_hungry(
args.world, args.world,
args.player, args.player,
@ -98,47 +135,15 @@ impl BlockBehaviour for CakeBlock {
) )
.await; .await;
} }
if args.player.gamemode.load() != GameMode::Creative {
let held_item = args.player.inventory.held_item();
let mut held_item_guard = held_item.lock().await;
held_item_guard.decrement(1);
}
args.world
.set_block_state(
args.position,
cake_from_candle(item).default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
let seed: f64 = rng().random();
args.player
.play_sound(
Sound::BlockCakeAddCandle as u16,
SoundCategory::Blocks,
&args.position.to_f64(),
1.0,
1.0,
seed,
)
.await;
return BlockActionResult::Consume;
} }
_ => { })
return Self::consume_if_hungry(
args.world,
args.player,
args.block,
args.position,
state_id,
)
.await;
}
}
} }
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
let state_id = args.world.get_block_state_id(args.position).await; Box::pin(async move {
Self::consume_if_hungry(args.world, args.player, args.block, args.position, state_id).await let state_id = args.world.get_block_state_id(args.position).await;
Self::consume_if_hungry(args.world, args.player, args.block, args.position, state_id)
.await
})
} }
} }

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::{ use pumpkin_data::{
Block, BlockDirection, Block, BlockDirection,
block_properties::{BlockProperties, CampfireLikeProperties}, block_properties::{BlockProperties, CampfireLikeProperties},
@ -8,11 +7,8 @@ use pumpkin_world::{BlockStateId, tick::TickPriority};
use crate::{ use crate::{
block::{ block::{
BlockIsReplacing, BlockBehaviour, BlockFuture, BlockIsReplacing, BlockMetadata,
{ GetStateForNeighborUpdateArgs, OnEntityCollisionArgs, OnPlaceArgs,
BlockBehaviour, BlockMetadata, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs,
OnPlaceArgs,
},
}, },
entity::EntityBase, entity::EntityBase,
}; };
@ -29,53 +25,58 @@ impl BlockMetadata for CampfireBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for CampfireBlock { impl BlockBehaviour for CampfireBlock {
// TODO: cooking food on campfire (CampfireBlockEntity) // TODO: cooking food on campfire (CampfireBlockEntity)
async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
if CampfireLikeProperties::from_state_id(args.state.id, args.block).lit Box::pin(async move {
&& args.entity.get_living_entity().is_some() 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; // TODO
} //args.entity.damage(args.entity, 1.0, DamageType::CAMPFIRE).await;
}
})
} }
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let is_replacing_water = matches!(args.replacing, BlockIsReplacing::Water(_)); Box::pin(async move {
let mut props = let is_replacing_water = matches!(args.replacing, BlockIsReplacing::Water(_));
CampfireLikeProperties::from_state_id(args.block.default_state.id, args.block); let mut props =
props.waterlogged = is_replacing_water; CampfireLikeProperties::from_state_id(args.block.default_state.id, args.block);
props.signal_fire = props.waterlogged = is_replacing_water;
is_signal_fire_base_block(args.world.get_block(&args.position.down()).await);
props.lit = !is_replacing_water;
props.facing = args.player.get_entity().get_horizontal_facing();
props.to_state_id(args.block)
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
let mut props = CampfireLikeProperties::from_state_id(args.state_id, args.block);
if props.waterlogged {
props.lit = false;
args.world
.schedule_fluid_tick(
&Fluid::WATER,
*args.position,
Fluid::WATER.flow_speed as u8,
TickPriority::Normal,
)
.await;
}
if args.direction == BlockDirection::Down {
props.signal_fire = props.signal_fire =
is_signal_fire_base_block(args.world.get_block(args.neighbor_position).await); is_signal_fire_base_block(args.world.get_block(&args.position.down()).await);
} props.lit = !is_replacing_water;
props.facing = args.player.get_entity().get_horizontal_facing();
props.to_state_id(args.block)
})
}
props.to_state_id(args.block) fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut props = CampfireLikeProperties::from_state_id(args.state_id, args.block);
if props.waterlogged {
props.lit = false;
args.world
.schedule_fluid_tick(
&Fluid::WATER,
*args.position,
Fluid::WATER.flow_speed as u8,
TickPriority::Normal,
)
.await;
}
if args.direction == BlockDirection::Down {
props.signal_fire =
is_signal_fire_base_block(args.world.get_block(args.neighbor_position).await);
}
props.to_state_id(args.block)
})
} }
// TODO: onProjectileHit // TODO: onProjectileHit

View File

@ -1,6 +1,5 @@
use std::sync::Arc; use std::sync::Arc;
use async_trait::async_trait;
use pumpkin_data::{ use pumpkin_data::{
Block, Block,
item::Item, item::Item,
@ -12,9 +11,8 @@ use pumpkin_world::{item::ItemStack, world::BlockFlags};
use crate::{ use crate::{
block::{ block::{
blocks::cake::CakeBlock, BlockBehaviour, BlockFuture, NormalUseArgs, UseWithItemArgs, blocks::cake::CakeBlock,
registry::BlockActionResult, registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs, UseWithItemArgs},
}, },
entity::player::Player, entity::player::Player,
world::World, world::World,
@ -100,19 +98,25 @@ impl CandleCakeBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for CandleCakeBlock { impl BlockBehaviour for CandleCakeBlock {
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { fn use_with_item<'a>(
let item_id = args.item_stack.lock().await.item.id; &'a self,
match item_id { args: UseWithItemArgs<'a>,
id if id == Item::FIRE_CHARGE.id || id == Item::FLINT_AND_STEEL.id => { ) -> BlockFuture<'a, BlockActionResult> {
BlockActionResult::Pass Box::pin(async move {
} // Item::FIRE_CHARGE | Item::FLINT_AND_STEEL let item_id = args.item_stack.lock().await.item.id;
_ => BlockActionResult::PassToDefaultBlockAction, match item_id {
} id if id == Item::FIRE_CHARGE.id || id == Item::FLINT_AND_STEEL.id => {
BlockActionResult::Pass
} // Item::FIRE_CHARGE | Item::FLINT_AND_STEEL
_ => BlockActionResult::PassToDefaultBlockAction,
}
})
} }
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Self::consume_and_drop_candle(args.block, args.player, args.position, args.world).await Box::pin(async move {
Self::consume_and_drop_candle(args.block, args.player, args.position, args.world).await
})
} }
} }

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::item::Item; use pumpkin_data::item::Item;
use pumpkin_data::{ use pumpkin_data::{
BlockDirection, BlockDirection,
@ -9,6 +8,7 @@ use pumpkin_data::{
use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_world::{BlockStateId, world::BlockFlags}; use pumpkin_world::{BlockStateId, world::BlockFlags};
use crate::block::BlockFuture;
use crate::{ use crate::{
block::{ block::{
BlockIsReplacing, BlockIsReplacing,
@ -24,99 +24,112 @@ use crate::{
#[pumpkin_block_from_tag("minecraft:candles")] #[pumpkin_block_from_tag("minecraft:candles")]
pub struct CandleBlock; pub struct CandleBlock;
#[async_trait]
impl BlockBehaviour for CandleBlock { impl BlockBehaviour for CandleBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
if args.player.get_entity().pose.load() != EntityPose::Crouching Box::pin(async move {
&& let BlockIsReplacing::Itself(state_id) = args.replacing if args.player.get_entity().pose.load() != EntityPose::Crouching
{ && let BlockIsReplacing::Itself(state_id) = args.replacing
let mut properties = CandleLikeProperties::from_state_id(state_id, args.block);
if properties.candles.to_index() < 3 {
properties.candles = Integer1To4::from_index(properties.candles.to_index() + 1);
}
return properties.to_state_id(args.block);
}
let mut properties = CandleLikeProperties::default(args.block);
properties.waterlogged = args.replacing.water_source();
properties.to_state_id(args.block)
}
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
let state = args.world.get_block_state(args.position).await;
let mut properties = CandleLikeProperties::from_state_id(state.id, args.block);
let item_lock = args.item_stack.lock().await;
let item = item_lock.item;
drop(item_lock);
match item.id {
id if (Item::CANDLE.id..=Item::BLACK_CANDLE.id).contains(&id)
&& item.id == args.block.id =>
{ {
let mut properties = CandleLikeProperties::from_state_id(state_id, args.block);
if properties.candles.to_index() < 3 { if properties.candles.to_index() < 3 {
properties.candles = Integer1To4::from_index(properties.candles.to_index() + 1); properties.candles = Integer1To4::from_index(properties.candles.to_index() + 1);
} }
return properties.to_state_id(args.block);
args.world
.set_block_state(
args.position,
properties.to_state_id(args.block),
BlockFlags::NOTIFY_ALL,
)
.await;
BlockActionResult::Consume
} }
_ => {
if properties.lit { let mut properties = CandleLikeProperties::default(args.block);
properties.lit = false; properties.waterlogged = args.replacing.water_source();
} else { properties.to_state_id(args.block)
return BlockActionResult::Pass; })
}
fn use_with_item<'a>(
&'a self,
args: UseWithItemArgs<'a>,
) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let state = args.world.get_block_state(args.position).await;
let mut properties = CandleLikeProperties::from_state_id(state.id, args.block);
let item_lock = args.item_stack.lock().await;
let item = item_lock.item;
drop(item_lock);
match item.id {
id if (Item::CANDLE.id..=Item::BLACK_CANDLE.id).contains(&id)
&& item.id == args.block.id =>
{
if properties.candles.to_index() < 3 {
properties.candles =
Integer1To4::from_index(properties.candles.to_index() + 1);
}
args.world
.set_block_state(
args.position,
properties.to_state_id(args.block),
BlockFlags::NOTIFY_ALL,
)
.await;
BlockActionResult::Consume
} }
_ => {
if properties.lit {
properties.lit = false;
} else {
return BlockActionResult::Pass;
}
args.world args.world
.set_block_state( .set_block_state(
args.position, args.position,
properties.to_state_id(args.block), properties.to_state_id(args.block),
BlockFlags::NOTIFY_ALL, BlockFlags::NOTIFY_ALL,
) )
.await; .await;
BlockActionResult::Consume BlockActionResult::Consume
}
} }
} })
} }
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
let state_id = args.world.get_block_state_id(args.position).await; Box::pin(async move {
let mut properties = CandleLikeProperties::from_state_id(state_id, args.block); let state_id = args.world.get_block_state_id(args.position).await;
let mut properties = CandleLikeProperties::from_state_id(state_id, args.block);
if properties.lit { if properties.lit {
properties.lit = false; properties.lit = false;
} }
args.world args.world
.set_block_state( .set_block_state(
args.position, args.position,
properties.to_state_id(args.block), properties.to_state_id(args.block),
BlockFlags::NOTIFY_ALL, BlockFlags::NOTIFY_ALL,
) )
.await; .await;
BlockActionResult::Consume BlockActionResult::Consume
})
} }
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
let (support_block, state) = args Box::pin(async move {
.block_accessor let (support_block, state) = args
.get_block_and_state(&args.position.down()) .block_accessor
.await; .get_block_and_state(&args.position.down())
!support_block.is_waterlogged(state.id) && state.is_center_solid(BlockDirection::Up) .await;
!support_block.is_waterlogged(state.id) && state.is_center_solid(BlockDirection::Up)
})
} }
async fn can_update_at(&self, args: CanUpdateAtArgs<'_>) -> bool { fn can_update_at<'a>(&'a self, args: CanUpdateAtArgs<'a>) -> BlockFuture<'a, bool> {
let b = args.world.get_block(args.position).await; Box::pin(async move {
args.player.get_entity().pose.load() != EntityPose::Crouching let b = args.world.get_block(args.position).await;
&& CandleLikeProperties::from_state_id(args.state_id, args.block).candles args.player.get_entity().pose.load() != EntityPose::Crouching
!= Integer1To4::L4 && CandleLikeProperties::from_state_id(args.state_id, args.block).candles
&& args.block.id == b.id // only the same color can update != Integer1To4::L4
&& args.block.id == b.id // only the same color can update
})
} }
} }

View File

@ -1,7 +1,6 @@
use crate::block::{ use crate::block::{
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnScheduledTickArgs, BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnScheduledTickArgs,
}; };
use async_trait::async_trait;
use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_data::tag::{RegistryKey, get_tag_values};
use pumpkin_macros::{pumpkin_block, pumpkin_block_from_tag}; use pumpkin_macros::{pumpkin_block, pumpkin_block_from_tag};
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
@ -12,90 +11,99 @@ use pumpkin_world::world::{BlockAccessor, BlockFlags};
#[pumpkin_block_from_tag("minecraft:wool_carpets")] #[pumpkin_block_from_tag("minecraft:wool_carpets")]
pub struct CarpetBlock; pub struct CarpetBlock;
#[async_trait]
impl BlockBehaviour for CarpetBlock { impl BlockBehaviour for CarpetBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
can_place_at(args.block_accessor, args.position).await Box::pin(async move { can_place_at(args.block_accessor, args.position).await })
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
if !can_place_at(args.world, args.position).await { Box::pin(async move {
args.world if !can_place_at(args.world, args.position).await {
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal) args.world
.await; .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
} .await;
args.state_id }
args.state_id
})
} }
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
if !can_place_at(args.world.as_ref(), args.position).await { Box::pin(async move {
args.world if !can_place_at(args.world.as_ref(), args.position).await {
.break_block(args.position, None, BlockFlags::empty()) args.world
.await; .break_block(args.position, None, BlockFlags::empty())
} .await;
}
})
} }
} }
#[pumpkin_block("minecraft:moss_carpet")] #[pumpkin_block("minecraft:moss_carpet")]
pub struct MossCarpetBlock; pub struct MossCarpetBlock;
#[async_trait]
impl BlockBehaviour for MossCarpetBlock { impl BlockBehaviour for MossCarpetBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
can_place_at(args.block_accessor, args.position).await Box::pin(async move { can_place_at(args.block_accessor, args.position).await })
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
if !can_place_at(args.world, args.position).await { Box::pin(async move {
args.world if !can_place_at(args.world, args.position).await {
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal) args.world
.await; .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
} .await;
args.state_id }
args.state_id
})
} }
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
if !can_place_at(args.world.as_ref(), args.position).await { Box::pin(async move {
args.world if !can_place_at(args.world.as_ref(), args.position).await {
.break_block(args.position, None, BlockFlags::empty()) args.world
.await; .break_block(args.position, None, BlockFlags::empty())
} .await;
}
})
} }
} }
#[pumpkin_block("minecraft:pale_moss_carpet")] #[pumpkin_block("minecraft:pale_moss_carpet")]
pub struct PaleMossCarpetBlock; pub struct PaleMossCarpetBlock;
#[async_trait]
impl BlockBehaviour for PaleMossCarpetBlock { impl BlockBehaviour for PaleMossCarpetBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
can_place_at(args.block_accessor, args.position).await Box::pin(async move { can_place_at(args.block_accessor, args.position).await })
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
if !can_place_at(args.world, args.position).await { Box::pin(async move {
args.world if !can_place_at(args.world, args.position).await {
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal) args.world
.await; .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
} .await;
args.state_id }
args.state_id
})
} }
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
if !can_place_at(args.world.as_ref(), args.position).await { Box::pin(async move {
args.world if !can_place_at(args.world.as_ref(), args.position).await {
.break_block(args.position, None, BlockFlags::empty()) args.world
.await; .break_block(args.position, None, BlockFlags::empty())
} .await;
}
})
} }
} }

View File

@ -1,11 +1,10 @@
use async_trait::async_trait;
use pumpkin_data::{ use pumpkin_data::{
Block, Block,
block_properties::{BlockProperties, WallTorchLikeProperties}, block_properties::{BlockProperties, WallTorchLikeProperties},
}; };
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use crate::block::{BlockBehaviour, BlockMetadata, OnPlaceArgs, PlacedArgs}; use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, OnPlaceArgs};
pub struct CarvedPumpkinBlock; pub struct CarvedPumpkinBlock;
@ -19,18 +18,17 @@ impl BlockMetadata for CarvedPumpkinBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for CarvedPumpkinBlock { impl BlockBehaviour for CarvedPumpkinBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut props = WallTorchLikeProperties::default(args.block); Box::pin(async move {
props.facing = args let mut props = WallTorchLikeProperties::default(args.block);
.player props.facing = args
.living_entity .player
.entity .living_entity
.get_horizontal_facing() .entity
.opposite(); .get_horizontal_facing()
props.to_state_id(args.block) .opposite();
props.to_state_id(args.block)
})
} }
async fn placed(&self, _args: PlacedArgs<'_>) {}
} }

View File

@ -1,5 +1,5 @@
use crate::block::BlockFuture;
use crate::block::{BlockBehaviour, OnPlaceArgs}; use crate::block::{BlockBehaviour, OnPlaceArgs};
use async_trait::async_trait;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::block_properties::Axis; use pumpkin_data::block_properties::Axis;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
@ -9,18 +9,19 @@ use pumpkin_world::BlockStateId;
#[pumpkin_block("minecraft:iron_chain")] #[pumpkin_block("minecraft:iron_chain")]
pub struct ChainBlock; pub struct ChainBlock;
#[async_trait]
impl BlockBehaviour for ChainBlock { impl BlockBehaviour for ChainBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut props = Box::pin(async move {
pumpkin_data::block_properties::IronChainLikeProperties::default(args.block); let mut props =
props.r#waterlogged = args.replacing.water_source(); pumpkin_data::block_properties::IronChainLikeProperties::default(args.block);
props.r#axis = match args.direction { props.r#waterlogged = args.replacing.water_source();
BlockDirection::East | BlockDirection::West => Axis::X, props.r#axis = match args.direction {
BlockDirection::Up | BlockDirection::Down => Axis::Y, BlockDirection::East | BlockDirection::West => Axis::X,
BlockDirection::North | BlockDirection::South => Axis::Z, BlockDirection::Up | BlockDirection::Down => Axis::Y,
}; BlockDirection::North | BlockDirection::South => Axis::Z,
};
props.to_state_id(args.block) props.to_state_id(args.block)
})
} }
} }

View File

@ -22,7 +22,8 @@ use pumpkin_world::world::BlockFlags;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::block::{ use crate::block::{
BlockMetadata, BrokenArgs, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs, BlockFuture, BlockMetadata, BrokenArgs, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs,
PlacedArgs,
}; };
use crate::entity::EntityBase; use crate::entity::EntityBase;
use crate::world::World; use crate::world::World;
@ -69,135 +70,147 @@ impl BlockMetadata for ChestBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for ChestBlock { impl BlockBehaviour for ChestBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut chest_props = ChestLikeProperties::default(args.block); Box::pin(async move {
let mut chest_props = ChestLikeProperties::default(args.block);
chest_props.waterlogged = args.replacing.water_source(); chest_props.waterlogged = args.replacing.water_source();
let (r#type, facing) = compute_chest_props( let (r#type, facing) = compute_chest_props(
args.world, args.world,
args.player, args.player,
args.block, args.block,
args.position, args.position,
args.direction, args.direction,
) )
.await; .await;
chest_props.facing = facing; chest_props.facing = facing;
chest_props.r#type = r#type; chest_props.r#type = r#type;
chest_props.to_state_id(args.block) chest_props.to_state_id(args.block)
})
} }
async fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool { fn on_synced_block_event<'a>(
// On the server, we don't need to do more because the client is responsible for that. &'a self,
args.r#type == Self::LID_ANIMATION_EVENT_TYPE args: OnSyncedBlockEventArgs<'a>,
) -> BlockFuture<'a, bool> {
Box::pin(async move {
// On the server, we don't need to do more because the client is responsible for that.
args.r#type == Self::LID_ANIMATION_EVENT_TYPE
})
} }
async fn placed(&self, args: PlacedArgs<'_>) { fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
let chest = ChestBlockEntity::new(*args.position); Box::pin(async move {
args.world.add_block_entity(Arc::new(chest)).await; let chest = ChestBlockEntity::new(*args.position);
args.world.add_block_entity(Arc::new(chest)).await;
let chest_props = ChestLikeProperties::from_state_id(args.state_id, args.block); let chest_props = ChestLikeProperties::from_state_id(args.state_id, args.block);
let connected_towards = match chest_props.r#type { let connected_towards = match chest_props.r#type {
ChestType::Single => return, ChestType::Single => return,
ChestType::Left => chest_props.facing.rotate_clockwise(), ChestType::Left => chest_props.facing.rotate_clockwise(),
ChestType::Right => chest_props.facing.rotate_counter_clockwise(), ChestType::Right => chest_props.facing.rotate_counter_clockwise(),
}; };
if let Some(mut neighbor_props) = get_chest_properties_if_can_connect( if let Some(mut neighbor_props) = get_chest_properties_if_can_connect(
args.world, args.world,
args.block, args.block,
args.position, args.position,
chest_props.facing, chest_props.facing,
connected_towards, connected_towards,
ChestType::Single, ChestType::Single,
) )
.await .await
{ {
neighbor_props.r#type = chest_props.r#type.opposite(); neighbor_props.r#type = chest_props.r#type.opposite();
args.world args.world
.set_block_state( .set_block_state(
&args.position.offset(connected_towards.to_offset()), &args.position.offset(connected_towards.to_offset()),
neighbor_props.to_state_id(args.block), neighbor_props.to_state_id(args.block),
BlockFlags::NOTIFY_LISTENERS, BlockFlags::NOTIFY_LISTENERS,
) )
.await; .await;
}
}
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
let (state, first_chest) = join(
args.world.get_block_state_id(args.position),
args.world.get_block_entity(args.position),
)
.await;
let Some(first_inventory) = first_chest.and_then(BlockEntity::get_inventory) else {
return BlockActionResult::Fail;
};
let chest_props = ChestLikeProperties::from_state_id(state, args.block);
let connected_towards = match chest_props.r#type {
ChestType::Single => None,
ChestType::Left => Some(chest_props.facing.rotate_clockwise()),
ChestType::Right => Some(chest_props.facing.rotate_counter_clockwise()),
};
let inventory = if let Some(direction) = connected_towards
&& let Some(second_inventory) = args
.world
.get_block_entity(&args.position.offset(direction.to_offset()))
.await
.and_then(BlockEntity::get_inventory)
{
// Vanilla: chestType == ChestType.RIGHT ? DoubleBlockProperties.Type.FIRST : DoubleBlockProperties.Type.SECOND;
if matches!(chest_props.r#type, ChestType::Right) {
DoubleInventory::new(first_inventory, second_inventory)
} else {
DoubleInventory::new(second_inventory, first_inventory)
} }
} else { })
first_inventory }
};
args.player fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
.open_handled_screen(&ChestScreenFactory(inventory)) Box::pin(async move {
let (state, first_chest) = join(
args.world.get_block_state_id(args.position),
args.world.get_block_entity(args.position),
)
.await; .await;
BlockActionResult::Success let Some(first_inventory) = first_chest.and_then(BlockEntity::get_inventory) else {
return BlockActionResult::Fail;
};
let chest_props = ChestLikeProperties::from_state_id(state, args.block);
let connected_towards = match chest_props.r#type {
ChestType::Single => None,
ChestType::Left => Some(chest_props.facing.rotate_clockwise()),
ChestType::Right => Some(chest_props.facing.rotate_counter_clockwise()),
};
let inventory = if let Some(direction) = connected_towards
&& let Some(second_inventory) = args
.world
.get_block_entity(&args.position.offset(direction.to_offset()))
.await
.and_then(BlockEntity::get_inventory)
{
// Vanilla: chestType == ChestType.RIGHT ? DoubleBlockProperties.Type.FIRST : DoubleBlockProperties.Type.SECOND;
if matches!(chest_props.r#type, ChestType::Right) {
DoubleInventory::new(first_inventory, second_inventory)
} else {
DoubleInventory::new(second_inventory, first_inventory)
}
} else {
first_inventory
};
args.player
.open_handled_screen(&ChestScreenFactory(inventory))
.await;
BlockActionResult::Success
})
} }
async fn broken(&self, args: BrokenArgs<'_>) { fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
let chest_props = ChestLikeProperties::from_state_id(args.state.id, args.block); Box::pin(async move {
let connected_towards = match chest_props.r#type { let chest_props = ChestLikeProperties::from_state_id(args.state.id, args.block);
ChestType::Single => return, let connected_towards = match chest_props.r#type {
ChestType::Left => chest_props.facing.rotate_clockwise(), ChestType::Single => return,
ChestType::Right => chest_props.facing.rotate_counter_clockwise(), ChestType::Left => chest_props.facing.rotate_clockwise(),
}; ChestType::Right => chest_props.facing.rotate_counter_clockwise(),
};
if let Some(mut neighbor_props) = get_chest_properties_if_can_connect( if let Some(mut neighbor_props) = get_chest_properties_if_can_connect(
args.world, args.world,
args.block, args.block,
args.position, args.position,
chest_props.facing, chest_props.facing,
connected_towards, connected_towards,
chest_props.r#type.opposite(), chest_props.r#type.opposite(),
) )
.await .await
{ {
neighbor_props.r#type = ChestType::Single; neighbor_props.r#type = ChestType::Single;
args.world args.world
.set_block_state( .set_block_state(
&args.position.offset(connected_towards.to_offset()), &args.position.offset(connected_towards.to_offset()),
neighbor_props.to_state_id(args.block), neighbor_props.to_state_id(args.block),
BlockFlags::NOTIFY_LISTENERS, BlockFlags::NOTIFY_LISTENERS,
) )
.await; .await;
} }
})
} }
} }

View File

@ -2,16 +2,12 @@ use std::sync::{Arc, atomic::Ordering};
use crate::{ use crate::{
block::{ block::{
registry::BlockActionResult, BlockBehaviour, BlockFuture, BlockHitResult, GetComparatorOutputArgs, NormalUseArgs,
{ OnPlaceArgs, PlacedArgs, UseWithItemArgs, registry::BlockActionResult,
BlockBehaviour, BlockHitResult, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs,
PlacedArgs, UseWithItemArgs,
},
}, },
entity::{EntityBase, player::Player}, entity::{EntityBase, player::Player},
world::World, world::World,
}; };
use async_trait::async_trait;
use pumpkin_data::{ use pumpkin_data::{
block_properties::{BlockProperties, ChiseledBookshelfLikeProperties, HorizontalFacing}, block_properties::{BlockProperties, ChiseledBookshelfLikeProperties, HorizontalFacing},
item::Item, item::Item,
@ -31,98 +27,113 @@ use tokio::sync::Mutex;
#[pumpkin_block("minecraft:chiseled_bookshelf")] #[pumpkin_block("minecraft:chiseled_bookshelf")]
pub struct ChiseledBookshelfBlock; pub struct ChiseledBookshelfBlock;
#[async_trait]
impl BlockBehaviour for ChiseledBookshelfBlock { impl BlockBehaviour for ChiseledBookshelfBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut properties = ChiseledBookshelfLikeProperties::default(args.block); Box::pin(async move {
let mut properties = ChiseledBookshelfLikeProperties::default(args.block);
// Face in the opposite direction the player is facing // Face in the opposite direction the player is facing
properties.facing = args.player.get_entity().get_horizontal_facing().opposite(); properties.facing = args.player.get_entity().get_horizontal_facing().opposite();
properties.to_state_id(args.block) properties.to_state_id(args.block)
})
} }
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
let state = args.world.get_block_state(args.position).await; Box::pin(async move {
let properties = ChiseledBookshelfLikeProperties::from_state_id(state.id, args.block); let state = args.world.get_block_state(args.position).await;
let properties = ChiseledBookshelfLikeProperties::from_state_id(state.id, args.block);
if let Some(slot) = Self::get_slot_for_hit(args.hit, properties.facing) { if let Some(slot) = Self::get_slot_for_hit(args.hit, properties.facing) {
if Self::is_slot_used(properties, slot) { if Self::is_slot_used(properties, slot) {
if let Some(block_entity) = args.world.get_block_entity(args.position).await if let Some(block_entity) = args.world.get_block_entity(args.position).await
&& let Some(block_entity) = block_entity
.as_any()
.downcast_ref::<ChiseledBookshelfBlockEntity>()
{
Self::try_remove_book(
args.world,
args.player,
args.position,
block_entity,
properties,
slot,
)
.await;
return BlockActionResult::Success;
}
} else {
return BlockActionResult::Consume;
}
}
BlockActionResult::Pass
})
}
fn use_with_item<'a>(
&'a self,
args: UseWithItemArgs<'a>,
) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let state = args.world.get_block_state(args.position).await;
let properties = ChiseledBookshelfLikeProperties::from_state_id(state.id, args.block);
if !args
.item_stack
.lock()
.await
.get_item()
.has_tag(&tag::Item::MINECRAFT_BOOKSHELF_BOOKS)
{
return BlockActionResult::PassToDefaultBlockAction;
}
if let Some(slot) = Self::get_slot_for_hit(args.hit, properties.facing) {
if Self::is_slot_used(properties, slot) {
return BlockActionResult::PassToDefaultBlockAction;
} else if let Some(block_entity) = args.world.get_block_entity(args.position).await
&& let Some(block_entity) = block_entity && let Some(block_entity) = block_entity
.as_any() .as_any()
.downcast_ref::<ChiseledBookshelfBlockEntity>() .downcast_ref::<ChiseledBookshelfBlockEntity>()
{ {
Self::try_remove_book( Self::try_add_book(
args.world, args.world,
args.player, args.player,
args.position, args.position,
block_entity, block_entity,
properties, properties,
slot, slot,
args.item_stack,
) )
.await; .await;
return BlockActionResult::Success; return BlockActionResult::Success;
} }
} else {
return BlockActionResult::Consume;
} }
}
BlockActionResult::Pass BlockActionResult::Pass
})
} }
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
let state = args.world.get_block_state(args.position).await; Box::pin(async move {
let properties = ChiseledBookshelfLikeProperties::from_state_id(state.id, args.block); let block_entity = ChiseledBookshelfBlockEntity::new(*args.position);
args.world.add_block_entity(Arc::new(block_entity)).await;
})
}
if !args fn get_comparator_output<'a>(
.item_stack &'a self,
.lock() args: GetComparatorOutputArgs<'a>,
.await ) -> BlockFuture<'a, Option<u8>> {
.get_item() Box::pin(async move {
.has_tag(&tag::Item::MINECRAFT_BOOKSHELF_BOOKS) if let Some(block_entity) = args.world.get_block_entity(args.position).await
{
return BlockActionResult::PassToDefaultBlockAction;
}
if let Some(slot) = Self::get_slot_for_hit(args.hit, properties.facing) {
if Self::is_slot_used(properties, slot) {
return BlockActionResult::PassToDefaultBlockAction;
} else if let Some(block_entity) = args.world.get_block_entity(args.position).await
&& let Some(block_entity) = block_entity && let Some(block_entity) = block_entity
.as_any() .as_any()
.downcast_ref::<ChiseledBookshelfBlockEntity>() .downcast_ref::<ChiseledBookshelfBlockEntity>()
{ {
Self::try_add_book( return Some((block_entity.last_interacted_slot.load(Ordering::Relaxed) + 1) as u8);
args.world,
args.player,
args.position,
block_entity,
properties,
slot,
args.item_stack,
)
.await;
return BlockActionResult::Success;
} }
} None
})
BlockActionResult::Pass
}
async fn placed(&self, args: PlacedArgs<'_>) {
let block_entity = ChiseledBookshelfBlockEntity::new(*args.position);
args.world.add_block_entity(Arc::new(block_entity)).await;
}
async fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option<u8> {
if let Some(block_entity) = args.world.get_block_entity(args.position).await
&& let Some(block_entity) = block_entity
.as_any()
.downcast_ref::<ChiseledBookshelfBlockEntity>()
{
return Some((block_entity.last_interacted_slot.load(Ordering::Relaxed) + 1) as u8);
}
None
} }
} }

View File

@ -1,13 +1,13 @@
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use async_trait::async_trait;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_util::{GameMode, math::position::BlockPos}; use pumpkin_util::{GameMode, math::position::BlockPos};
use pumpkin_world::{block::entities::command_block::CommandBlockEntity, tick::TickPriority}; use pumpkin_world::{block::entities::command_block::CommandBlockEntity, tick::TickPriority};
use crate::{ use crate::{
block::{ block::{
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, OnNeighborUpdateArgs, OnScheduledTickArgs, BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, OnNeighborUpdateArgs,
OnScheduledTickArgs,
}, },
world::World, world::World,
}; };
@ -50,45 +50,49 @@ impl BlockMetadata for CommandBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for CommandBlock { impl BlockBehaviour for CommandBlock {
async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
if let Some(block_entity) = args.world.get_block_entity(args.position).await { Box::pin(async move {
if block_entity.resource_location() != CommandBlockEntity::ID { if let Some(block_entity) = args.world.get_block_entity(args.position).await {
return; if block_entity.resource_location() != CommandBlockEntity::ID {
} return;
let command_entity = block_entity }
.as_any() let command_entity = block_entity
.downcast_ref::<CommandBlockEntity>() .as_any()
.unwrap(); .downcast_ref::<CommandBlockEntity>()
.unwrap();
Self::update( Self::update(
args.world, args.world,
args.block, args.block,
command_entity, command_entity,
args.position, args.position,
block_receives_redstone_power(args.world, args.position).await, block_receives_redstone_power(args.world, args.position).await,
) )
.await; .await;
} }
})
} }
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
if let Some(block_entity) = args.world.get_block_entity(args.position).await Box::pin(async move {
&& block_entity.resource_location() != CommandBlockEntity::ID if let Some(block_entity) = args.world.get_block_entity(args.position).await
{ && block_entity.resource_location() != CommandBlockEntity::ID
return; {}
} })
// TODO // TODO
} }
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
if let Some(player) = args.player Box::pin(async move {
&& player.gamemode.load() == GameMode::Creative if let Some(player) = args.player
{ && player.gamemode.load() == GameMode::Creative
return true; {
} return true;
}
false false
})
} }
} }

View File

@ -2,16 +2,12 @@ use std::sync::Arc;
use crate::{ use crate::{
block::{ block::{
registry::BlockActionResult, BlockBehaviour, BlockFuture, GetComparatorOutputArgs, NormalUseArgs, OnScheduledTickArgs,
{ UseWithItemArgs, registry::BlockActionResult,
BlockBehaviour, GetComparatorOutputArgs, NormalUseArgs, OnScheduledTickArgs,
UseWithItemArgs,
},
}, },
entity::{Entity, item::ItemEntity}, entity::{Entity, item::ItemEntity},
world::World, world::World,
}; };
use async_trait::async_trait;
use pumpkin_data::{ use pumpkin_data::{
Block, Block,
block_properties::{BlockProperties, ComposterLikeProperties, EnumVariants, Integer0To8}, block_properties::{BlockProperties, ComposterLikeProperties, EnumVariants, Integer0To8},
@ -29,54 +25,79 @@ use uuid::Uuid;
#[pumpkin_block("minecraft:composter")] #[pumpkin_block("minecraft:composter")]
pub struct ComposterBlock; pub struct ComposterBlock;
#[async_trait]
impl BlockBehaviour for ComposterBlock { impl BlockBehaviour for ComposterBlock {
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
let state_id = args.world.get_block_state_id(args.position).await; Box::pin(async move {
let props = ComposterLikeProperties::from_state_id(state_id, args.block); let state_id = args.world.get_block_state_id(args.position).await;
if props.get_level() == 8 { let props = ComposterLikeProperties::from_state_id(state_id, args.block);
self.clear_composter(args.world, args.position, state_id, args.block) if props.get_level() == 8 {
.await; self.clear_composter(args.world, args.position, state_id, args.block)
} .await;
}
BlockActionResult::Pass BlockActionResult::Pass
})
} }
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { fn use_with_item<'a>(
let state_id = args.world.get_block_state_id(args.position).await; &'a self,
let props = ComposterLikeProperties::from_state_id(state_id, args.block); args: UseWithItemArgs<'a>,
let level = props.get_level(); ) -> BlockFuture<'a, BlockActionResult> {
if level == 8 { Box::pin(async move {
self.clear_composter(args.world, args.position, state_id, args.block) let state_id = args.world.get_block_state_id(args.position).await;
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
let level = props.get_level();
if level == 8 {
self.clear_composter(args.world, args.position, state_id, args.block)
.await;
}
if level < 7
&& let Some(chance) =
get_composter_increase_chance_from_item_id(args.item_stack.lock().await.item.id)
&& (level == 0 || rand::rng().random_bool(f64::from(chance)))
{
self.update_level_composter(
args.world,
args.position,
state_id,
args.block,
level + 1,
)
.await; .await;
} args.world
if level < 7 .sync_world_event(WorldEvent::ComposterUsed, *args.position, 1)
&& let Some(chance) = .await;
get_composter_increase_chance_from_item_id(args.item_stack.lock().await.item.id) }
&& (level == 0 || rand::rng().random_bool(f64::from(chance))) BlockActionResult::Consume
{ })
self.update_level_composter(args.world, args.position, state_id, args.block, level + 1)
.await;
args.world
.sync_world_event(WorldEvent::ComposterUsed, *args.position, 1)
.await;
}
BlockActionResult::Consume
} }
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
let state_id = args.world.get_block_state_id(args.position).await; Box::pin(async move {
let props = ComposterLikeProperties::from_state_id(state_id, args.block); let state_id = args.world.get_block_state_id(args.position).await;
let level = props.get_level(); let props = ComposterLikeProperties::from_state_id(state_id, args.block);
if level == 7 { let level = props.get_level();
self.update_level_composter(args.world, args.position, state_id, args.block, level + 1) if level == 7 {
self.update_level_composter(
args.world,
args.position,
state_id,
args.block,
level + 1,
)
.await; .await;
} }
})
} }
async fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option<u8> { fn get_comparator_output<'a>(
let props = ComposterLikeProperties::from_state_id(args.state.id, args.block); &'a self,
Some(props.get_level()) args: GetComparatorOutputArgs<'a>,
) -> BlockFuture<'a, Option<u8>> {
Box::pin(async move {
let props = ComposterLikeProperties::from_state_id(args.state.id, args.block);
Some(props.get_level())
})
} }
} }

View File

@ -1,5 +1,5 @@
use crate::block::registry::BlockActionResult; use crate::block::registry::BlockActionResult;
use crate::block::{BlockBehaviour, NormalUseArgs}; use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs};
use async_trait::async_trait; use async_trait::async_trait;
use pumpkin_inventory::crafting::crafting_screen_handler::CraftingTableScreenHandler; use pumpkin_inventory::crafting::crafting_screen_handler::CraftingTableScreenHandler;
use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::player::player_inventory::PlayerInventory;
@ -12,14 +12,15 @@ use tokio::sync::Mutex;
#[pumpkin_block("minecraft:crafting_table")] #[pumpkin_block("minecraft:crafting_table")]
pub struct CraftingTableBlock; pub struct CraftingTableBlock;
#[async_trait]
impl BlockBehaviour for CraftingTableBlock { impl BlockBehaviour for CraftingTableBlock {
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
args.player Box::pin(async move {
.open_handled_screen(&CraftingTableScreenFactory) args.player
.await; .open_handled_screen(&CraftingTableScreenFactory)
.await;
BlockActionResult::Success BlockActionResult::Success
})
} }
} }

View File

@ -1,9 +1,9 @@
use crate::block::BlockBehaviour; use crate::block::BlockBehaviour;
use crate::block::BlockFuture;
use crate::block::CanPlaceAtArgs; use crate::block::CanPlaceAtArgs;
use crate::block::GetStateForNeighborUpdateArgs; use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs; use crate::block::OnPlaceArgs;
use crate::block::OnScheduledTickArgs; use crate::block::OnScheduledTickArgs;
use async_trait::async_trait;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
@ -16,41 +16,48 @@ use pumpkin_world::world::BlockFlags;
#[pumpkin_block("minecraft:dirt_path")] #[pumpkin_block("minecraft:dirt_path")]
pub struct DirtPathBlock; pub struct DirtPathBlock;
#[async_trait]
impl BlockBehaviour for DirtPathBlock { impl BlockBehaviour for DirtPathBlock {
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
// TODO: push up entities Box::pin(async move {
args.world // TODO: push up entities
.set_block_state(
args.position,
Block::DIRT.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
}
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
if !can_place_at(args.world, args.position).await {
return Block::DIRT.default_state.id;
}
args.block.default_state.id
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
if args.direction == BlockDirection::Up && !can_place_at(args.world, args.position).await {
args.world args.world
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal) .set_block_state(
args.position,
Block::DIRT.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await; .await;
} })
args.state_id
} }
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
can_place_at(args.block_accessor, args.position).await Box::pin(async move {
if !can_place_at(args.world, args.position).await {
return Block::DIRT.default_state.id;
}
args.block.default_state.id
})
}
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
if args.direction == BlockDirection::Up
&& !can_place_at(args.world, args.position).await
{
args.world
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
.await;
}
args.state_id
})
}
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move { can_place_at(args.block_accessor, args.position).await })
} }
} }

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::HorizontalFacingExt; use pumpkin_data::HorizontalFacingExt;
use pumpkin_data::block_properties::Axis; use pumpkin_data::block_properties::Axis;
@ -20,6 +19,7 @@ use pumpkin_world::world::BlockFlags;
use std::sync::Arc; use std::sync::Arc;
use crate::block::BlockBehaviour; use crate::block::BlockBehaviour;
use crate::block::BlockFuture;
use crate::block::CanPlaceAtArgs; use crate::block::CanPlaceAtArgs;
use crate::block::GetStateForNeighborUpdateArgs; use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::NormalUseArgs; use crate::block::NormalUseArgs;
@ -163,125 +163,136 @@ async fn get_hinge(
#[pumpkin_block_from_tag("minecraft:doors")] #[pumpkin_block_from_tag("minecraft:doors")]
pub struct DoorBlock; pub struct DoorBlock;
#[async_trait]
impl BlockBehaviour for DoorBlock { impl BlockBehaviour for DoorBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let powered = block_receives_redstone_power(args.world, args.position).await Box::pin(async move {
|| block_receives_redstone_power(args.world, &args.position.up()).await; let powered = block_receives_redstone_power(args.world, args.position).await
|| block_receives_redstone_power(args.world, &args.position.up()).await;
let direction = args.player.living_entity.entity.get_horizontal_facing(); let direction = args.player.living_entity.entity.get_horizontal_facing();
let hinge = get_hinge(args.world, args.position, args.use_item_on, direction).await; let hinge = get_hinge(args.world, args.position, args.use_item_on, direction).await;
let mut door_props = DoorProperties::default(args.block); let mut door_props = DoorProperties::default(args.block);
door_props.half = DoubleBlockHalf::Lower; door_props.half = DoubleBlockHalf::Lower;
door_props.facing = direction; door_props.facing = direction;
door_props.hinge = hinge; door_props.hinge = hinge;
door_props.powered = powered; door_props.powered = powered;
door_props.open = powered; door_props.open = powered;
door_props.to_state_id(args.block) door_props.to_state_id(args.block)
})
} }
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
can_place_at(args.block_accessor, args.position).await Box::pin(async move { can_place_at(args.block_accessor, args.position).await })
} }
async fn placed(&self, args: PlacedArgs<'_>) { fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
let mut door_props = DoorProperties::from_state_id(args.state_id, args.block); Box::pin(async move {
door_props.half = DoubleBlockHalf::Upper; let mut door_props = DoorProperties::from_state_id(args.state_id, args.block);
door_props.half = DoubleBlockHalf::Upper;
args.world args.world
.set_block_state( .set_block_state(
&args.position.offset(BlockDirection::Up.to_offset()), &args.position.offset(BlockDirection::Up.to_offset()),
door_props.to_state_id(args.block), door_props.to_state_id(args.block),
BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK,
) )
.await; .await;
})
} }
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
if !can_open_door(args.block) { Box::pin(async move {
return BlockActionResult::Pass; if !can_open_door(args.block) {
} return BlockActionResult::Pass;
}
toggle_door(args.player, args.world, args.position).await; toggle_door(args.player, args.world, args.position).await;
BlockActionResult::Success BlockActionResult::Success
})
} }
async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
let block_state = args.world.get_block_state(args.position).await; Box::pin(async move {
let mut door_props = DoorProperties::from_state_id(block_state.id, args.block); let block_state = args.world.get_block_state(args.position).await;
let mut door_props = DoorProperties::from_state_id(block_state.id, args.block);
let other_half = match door_props.half { let other_half = match door_props.half {
DoubleBlockHalf::Upper => BlockDirection::Down, DoubleBlockHalf::Upper => BlockDirection::Down,
DoubleBlockHalf::Lower => BlockDirection::Up, DoubleBlockHalf::Lower => BlockDirection::Up,
}; };
let other_pos = args.position.offset(other_half.to_offset()); let other_pos = args.position.offset(other_half.to_offset());
let (other_block, other_state_id) = args.world.get_block_and_state_id(&other_pos).await; let (other_block, other_state_id) = args.world.get_block_and_state_id(&other_pos).await;
let powered = block_receives_redstone_power(args.world, args.position).await let powered = block_receives_redstone_power(args.world, args.position).await
|| block_receives_redstone_power(args.world, &other_pos).await; || block_receives_redstone_power(args.world, &other_pos).await;
if args.block.id == other_block.id && powered != door_props.powered { if args.block.id == other_block.id && powered != door_props.powered {
let mut other_door_props = DoorProperties::from_state_id(other_state_id, other_block); let mut other_door_props =
door_props.powered = !door_props.powered; DoorProperties::from_state_id(other_state_id, other_block);
other_door_props.powered = door_props.powered; door_props.powered = !door_props.powered;
other_door_props.powered = door_props.powered;
if powered != door_props.open { if powered != door_props.open {
door_props.open = door_props.powered; door_props.open = door_props.powered;
other_door_props.open = other_door_props.powered; other_door_props.open = other_door_props.powered;
args.world
.play_block_sound(
get_sound(args.block, powered),
SoundCategory::Blocks,
*args.position,
)
.await;
}
args.world args.world
.play_block_sound( .set_block_state(
get_sound(args.block, powered), args.position,
SoundCategory::Blocks, door_props.to_state_id(args.block),
*args.position, BlockFlags::NOTIFY_LISTENERS,
)
.await;
args.world
.set_block_state(
&other_pos,
other_door_props.to_state_id(other_block),
BlockFlags::NOTIFY_LISTENERS,
) )
.await; .await;
} }
})
args.world
.set_block_state(
args.position,
door_props.to_state_id(args.block),
BlockFlags::NOTIFY_LISTENERS,
)
.await;
args.world
.set_block_state(
&other_pos,
other_door_props.to_state_id(other_block),
BlockFlags::NOTIFY_LISTENERS,
)
.await;
}
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
let lv = DoorProperties::from_state_id(args.state_id, args.block).half; Box::pin(async move {
if args.direction.to_axis() != Axis::Y let lv = DoorProperties::from_state_id(args.state_id, args.block).half;
|| (lv == DoubleBlockHalf::Lower) != (args.direction == BlockDirection::Up) if args.direction.to_axis() != Axis::Y
{ || (lv == DoubleBlockHalf::Lower) != (args.direction == BlockDirection::Up)
if lv == DoubleBlockHalf::Lower
&& args.direction == BlockDirection::Down
&& !can_place_at(args.world, args.position).await
{ {
if lv == DoubleBlockHalf::Lower
&& args.direction == BlockDirection::Down
&& !can_place_at(args.world, args.position).await
{
return 0;
}
} else if Block::from_state_id(args.neighbor_state_id).id == args.block.id
&& DoorProperties::from_state_id(args.neighbor_state_id, args.block).half != lv
{
let mut new_state =
DoorProperties::from_state_id(args.neighbor_state_id, args.block);
new_state.half = lv;
return new_state.to_state_id(args.block);
} else {
return 0; return 0;
} }
} else if Block::from_state_id(args.neighbor_state_id).id == args.block.id args.state_id
&& DoorProperties::from_state_id(args.neighbor_state_id, args.block).half != lv })
{
let mut new_state = DoorProperties::from_state_id(args.neighbor_state_id, args.block);
new_state.half = lv;
return new_state.to_state_id(args.block);
} else {
return 0;
}
args.state_id
} }
} }

View File

@ -1,9 +1,9 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::BlockBehaviour; use crate::block::BlockBehaviour;
use crate::block::BlockFuture;
use crate::block::OnEntityCollisionArgs; use crate::block::OnEntityCollisionArgs;
use crate::block::PlacedArgs; use crate::block::PlacedArgs;
use async_trait::async_trait;
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_registry::VanillaDimensionType; use pumpkin_registry::VanillaDimensionType;
use pumpkin_world::block::entities::end_portal::EndPortalBlockEntity; use pumpkin_world::block::entities::end_portal::EndPortalBlockEntity;
@ -11,27 +11,30 @@ use pumpkin_world::block::entities::end_portal::EndPortalBlockEntity;
#[pumpkin_block("minecraft:end_portal")] #[pumpkin_block("minecraft:end_portal")]
pub struct EndPortalBlock; pub struct EndPortalBlock;
#[async_trait]
impl BlockBehaviour for EndPortalBlock { impl BlockBehaviour for EndPortalBlock {
async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
let world = if args.world.dimension_type == VanillaDimensionType::TheEnd { Box::pin(async move {
args.server let world = if args.world.dimension_type == VanillaDimensionType::TheEnd {
.get_world_from_dimension(VanillaDimensionType::Overworld) args.server
.await .get_world_from_dimension(VanillaDimensionType::Overworld)
} else { .await
args.server } else {
.get_world_from_dimension(VanillaDimensionType::TheEnd) args.server
.await .get_world_from_dimension(VanillaDimensionType::TheEnd)
}; .await
args.entity };
.get_entity() args.entity
.try_use_portal(0, world, *args.position) .get_entity()
.await; .try_use_portal(0, world, *args.position)
.await;
})
} }
async fn placed(&self, args: PlacedArgs<'_>) { fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
args.world Box::pin(async move {
.add_block_entity(Arc::new(EndPortalBlockEntity::new(*args.position))) args.world
.await; .add_block_entity(Arc::new(EndPortalBlockEntity::new(*args.position)))
.await;
})
} }
} }

View File

@ -1,10 +1,9 @@
use async_trait::async_trait;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use crate::{ use crate::{
block::{BlockBehaviour, OnPlaceArgs}, block::{BlockBehaviour, BlockFuture, OnPlaceArgs},
entity::EntityBase, entity::EntityBase,
}; };
@ -13,12 +12,14 @@ type EndPortalFrameProperties = pumpkin_data::block_properties::EndPortalFrameLi
#[pumpkin_block("minecraft:end_portal_frame")] #[pumpkin_block("minecraft:end_portal_frame")]
pub struct EndPortalFrameBlock; pub struct EndPortalFrameBlock;
#[async_trait]
impl BlockBehaviour for EndPortalFrameBlock { impl BlockBehaviour for EndPortalFrameBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut end_portal_frame_props = EndPortalFrameProperties::default(args.block); Box::pin(async move {
end_portal_frame_props.facing = args.player.get_entity().get_horizontal_facing().opposite(); let mut end_portal_frame_props = EndPortalFrameProperties::default(args.block);
end_portal_frame_props.facing =
args.player.get_entity().get_horizontal_facing().opposite();
end_portal_frame_props.to_state_id(args.block) end_portal_frame_props.to_state_id(args.block)
})
} }
} }

View File

@ -1,5 +1,5 @@
use crate::block::BlockFuture;
use crate::block::{BlockBehaviour, OnPlaceArgs}; use crate::block::{BlockBehaviour, OnPlaceArgs};
use async_trait::async_trait;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::block_properties::EndRodLikeProperties; use pumpkin_data::block_properties::EndRodLikeProperties;
@ -9,25 +9,26 @@ use pumpkin_world::BlockStateId;
#[pumpkin_block("minecraft:end_rod")] #[pumpkin_block("minecraft:end_rod")]
pub struct EndRodBlock; pub struct EndRodBlock;
#[async_trait]
impl BlockBehaviour for EndRodBlock { impl BlockBehaviour for EndRodBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut props = EndRodLikeProperties::default(args.block); Box::pin(async move {
let mut props = EndRodLikeProperties::default(args.block);
let blockstate = args let blockstate = args
.world .world
.get_block_state_id(&args.position.offset(args.direction.to_offset())) .get_block_state_id(&args.position.offset(args.direction.to_offset()))
.await; .await;
if Block::from_state_id(blockstate).eq(args.block) if Block::from_state_id(blockstate).eq(args.block)
&& EndRodLikeProperties::from_state_id(blockstate, args.block).facing && EndRodLikeProperties::from_state_id(blockstate, args.block).facing
== args.direction.to_facing().opposite() == args.direction.to_facing().opposite()
{ {
props.facing = args.direction.to_facing(); props.facing = args.direction.to_facing();
} else { } else {
props.facing = args.direction.to_facing().opposite(); props.facing = args.direction.to_facing().opposite();
} }
props.to_state_id(args.block) props.to_state_id(args.block)
})
} }
} }

View File

@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::{ use crate::block::{
BlockBehaviour, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs, BlockBehaviour, BlockFuture, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs,
registry::BlockActionResult, registry::BlockActionResult,
}; };
use async_trait::async_trait; use async_trait::async_trait;
@ -41,46 +41,56 @@ impl ScreenHandlerFactory for EnderChestScreenFactory {
#[pumpkin_block("minecraft:ender_chest")] #[pumpkin_block("minecraft:ender_chest")]
pub struct EnderChestBlock; pub struct EnderChestBlock;
#[async_trait]
impl BlockBehaviour for EnderChestBlock { impl BlockBehaviour for EnderChestBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut props = LadderLikeProperties::default(args.block); Box::pin(async move {
props.facing = args let mut props = LadderLikeProperties::default(args.block);
.player props.facing = args
.living_entity .player
.entity .living_entity
.get_horizontal_facing() .entity
.opposite(); .get_horizontal_facing()
props.to_state_id(args.block) .opposite();
props.to_state_id(args.block)
})
} }
async fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool { fn on_synced_block_event<'a>(
// On the server, we don't need to do more because the client is responsible for that. &'a self,
args.r#type == Self::LID_ANIMATION_EVENT_TYPE args: OnSyncedBlockEventArgs<'a>,
) -> BlockFuture<'a, bool> {
Box::pin(async move {
// On the server, we don't need to do more because the client is responsible for that.
args.r#type == Self::LID_ANIMATION_EVENT_TYPE
})
} }
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
if let Some(block_entity) = args.world.get_block_entity(args.position).await Box::pin(async move {
&& let Some(block_entity) = block_entity if let Some(block_entity) = args.world.get_block_entity(args.position).await
.as_any() && let Some(block_entity) = block_entity
.downcast_ref::<EnderChestBlockEntity>() .as_any()
{ .downcast_ref::<EnderChestBlockEntity>()
let inventory = args.player.ender_chest_inventory(); {
inventory.set_tracker(block_entity.get_tracker()).await; let inventory = args.player.ender_chest_inventory();
args.player inventory.set_tracker(block_entity.get_tracker()).await;
.open_handled_screen(&EnderChestScreenFactory(inventory.clone())) args.player
.await; .open_handled_screen(&EnderChestScreenFactory(inventory.clone()))
.await;
// TODO: player.incrementStat(Stats.OPEN_ENDERCHEST); // TODO: player.incrementStat(Stats.OPEN_ENDERCHEST);
// TODO: PiglinBrain.onGuardedBlockInteracted(serverWorld, player, true); // TODO: PiglinBrain.onGuardedBlockInteracted(serverWorld, player, true);
} }
BlockActionResult::Success BlockActionResult::Success
})
} }
async fn placed(&self, args: PlacedArgs<'_>) { fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
let block_entity = EnderChestBlockEntity::new(*args.position); Box::pin(async move {
args.world.add_block_entity(Arc::new(block_entity)).await; let block_entity = EnderChestBlockEntity::new(*args.position);
args.world.add_block_entity(Arc::new(block_entity)).await;
})
} }
} }

View File

@ -1,11 +1,10 @@
use crate::{ use crate::{
block::{ block::{
BlockBehaviour, BlockMetadata, GetStateForNeighborUpdateArgs, OnScheduledTickArgs, BlockBehaviour, BlockFuture, BlockMetadata, GetStateForNeighborUpdateArgs,
PlacedArgs, OnScheduledTickArgs, PlacedArgs,
}, },
entity::falling::FallingEntity, entity::falling::FallingEntity,
}; };
use async_trait::async_trait;
use pumpkin_data::{ use pumpkin_data::{
Block, BlockState, Block, BlockState,
tag::{self, Taggable}, tag::{self, Taggable},
@ -33,30 +32,36 @@ impl BlockMetadata for FallingBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for FallingBlock { impl BlockBehaviour for FallingBlock {
async fn placed(&self, args: PlacedArgs<'_>) { fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
// TODO: make delay configurable Box::pin(async move {
args.world // TODO: make delay configurable
.schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal) args.world
.await; .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal)
.await;
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
// TODO: make delay configurable Box::pin(async move {
args.world // TODO: make delay configurable
.schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal) args.world
.await; .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal)
args.state_id .await;
args.state_id
})
} }
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
let (block, state) = args.world.get_block_and_state(&args.position.down()).await; Box::pin(async move {
if !Self::can_fall_through(state, block) || args.position.0.y < args.world.min_y { let (block, state) = args.world.get_block_and_state(&args.position.down()).await;
return; if !Self::can_fall_through(state, block) || args.position.0.y < args.world.min_y {
} return;
FallingEntity::replace_spawn(args.world, *args.position, args.block.default_state.id).await; }
FallingEntity::replace_spawn(args.world, *args.position, args.block.default_state.id)
.await;
})
} }
} }

View File

@ -1,13 +1,13 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::BlockBehaviour; use crate::block::BlockBehaviour;
use crate::block::BlockFuture;
use crate::block::CanPlaceAtArgs; use crate::block::CanPlaceAtArgs;
use crate::block::GetStateForNeighborUpdateArgs; use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs; use crate::block::OnPlaceArgs;
use crate::block::OnScheduledTickArgs; use crate::block::OnScheduledTickArgs;
use crate::block::RandomTickArgs; use crate::block::RandomTickArgs;
use crate::world::World; use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
@ -29,75 +29,55 @@ type FarmlandProperties = FarmlandLikeProperties;
#[pumpkin_block("minecraft:farmland")] #[pumpkin_block("minecraft:farmland")]
pub struct FarmlandBlock; pub struct FarmlandBlock;
#[async_trait]
impl BlockBehaviour for FarmlandBlock { impl BlockBehaviour for FarmlandBlock {
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
// TODO: push up entities Box::pin(async move {
args.world // TODO: push up entities
.set_block_state(
args.position,
Block::DIRT.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
}
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
if !can_place_at(args.world, args.position).await {
return Block::DIRT.default_state.id;
}
args.block.default_state.id
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
if args.direction == BlockDirection::Up && !can_place_at(args.world, args.position).await {
args.world
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
.await;
}
args.state_id
}
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
can_place_at(args.block_accessor, args.position).await
}
async fn random_tick(&self, args: RandomTickArgs<'_>) {
// TODO: add rain check. Remember to check which one is most optimized.
if is_water_nearby(args.world, args.position).await {
let mut props = FarmlandProperties::default(args.block);
props.moisture = Integer0To7::L7;
args.world args.world
.set_block_state( .set_block_state(
args.position, args.position,
props.to_state_id(args.block), Block::DIRT.default_state.id,
BlockFlags::NOTIFY_NEIGHBORS, BlockFlags::NOTIFY_ALL,
) )
.await; .await;
} else { })
let state_id = args.world.get_block_state_id(args.position).await; }
let mut props = FarmlandProperties::from_state_id(state_id, args.block);
if props.moisture == Integer0To7::L0 { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
if !args Box::pin(async move {
.world if !can_place_at(args.world, args.position).await {
.get_block(&args.position.up()) return Block::DIRT.default_state.id;
.await }
.has_tag(&tag::Block::MINECRAFT_MAINTAINS_FARMLAND) args.block.default_state.id
{ })
//TODO push entities up }
args.world
.set_block_state( fn get_state_for_neighbor_update<'a>(
args.position, &'a self,
Block::DIRT.default_state.id, args: GetStateForNeighborUpdateArgs<'a>,
BlockFlags::NOTIFY_NEIGHBORS, ) -> BlockFuture<'a, BlockStateId> {
) Box::pin(async move {
.await; if args.direction == BlockDirection::Up
} && !can_place_at(args.world, args.position).await
} else { {
props.moisture = Integer0To7::from_index(props.moisture.to_index() - 1); args.world
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
.await;
}
args.state_id
})
}
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move { can_place_at(args.block_accessor, args.position).await })
}
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
// TODO: add rain check. Remember to check which one is most optimized.
if is_water_nearby(args.world, args.position).await {
let mut props = FarmlandProperties::default(args.block);
props.moisture = Integer0To7::L7;
args.world args.world
.set_block_state( .set_block_state(
args.position, args.position,
@ -105,8 +85,37 @@ impl BlockBehaviour for FarmlandBlock {
BlockFlags::NOTIFY_NEIGHBORS, BlockFlags::NOTIFY_NEIGHBORS,
) )
.await; .await;
} else {
let state_id = args.world.get_block_state_id(args.position).await;
let mut props = FarmlandProperties::from_state_id(state_id, args.block);
if props.moisture == Integer0To7::L0 {
if !args
.world
.get_block(&args.position.up())
.await
.has_tag(&tag::Block::MINECRAFT_MAINTAINS_FARMLAND)
{
//TODO push entities up
args.world
.set_block_state(
args.position,
Block::DIRT.default_state.id,
BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
}
} else {
props.moisture = Integer0To7::from_index(props.moisture.to_index() - 1);
args.world
.set_block_state(
args.position,
props.to_state_id(args.block),
BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
}
} }
} })
} }
} }

View File

@ -1,10 +1,10 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::BlockFuture;
use crate::block::GetStateForNeighborUpdateArgs; use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::NormalUseArgs; use crate::block::NormalUseArgs;
use crate::block::OnPlaceArgs; use crate::block::OnPlaceArgs;
use crate::entity::player::Player; use crate::entity::player::Player;
use async_trait::async_trait;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::tag; use pumpkin_data::tag;
use pumpkin_data::tag::RegistryKey; use pumpkin_data::tag::RegistryKey;
@ -57,26 +57,31 @@ pub async fn toggle_fence_gate(
#[pumpkin_block_from_tag("minecraft:fence_gates")] #[pumpkin_block_from_tag("minecraft:fence_gates")]
pub struct FenceGateBlock; pub struct FenceGateBlock;
#[async_trait]
impl BlockBehaviour for FenceGateBlock { impl BlockBehaviour for FenceGateBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut fence_gate_props = FenceGateProperties::default(args.block); Box::pin(async move {
fence_gate_props.facing = args.player.living_entity.entity.get_horizontal_facing(); let mut fence_gate_props = FenceGateProperties::default(args.block);
fence_gate_props.to_state_id(args.block) fence_gate_props.facing = args.player.living_entity.entity.get_horizontal_facing();
fence_gate_props.to_state_id(args.block)
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
let fence_props = is_in_wall(&args).await; Box::pin(async move {
fence_props.to_state_id(args.block) let fence_props = is_in_wall(&args).await;
fence_props.to_state_id(args.block)
})
} }
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
toggle_fence_gate(args.world, args.position, args.player).await; Box::pin(async move {
toggle_fence_gate(args.world, args.position, args.player).await;
BlockActionResult::Success BlockActionResult::Success
})
} }
} }

View File

@ -1,6 +1,6 @@
use crate::block::BlockFuture;
use crate::block::GetStateForNeighborUpdateArgs; use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs; use crate::block::OnPlaceArgs;
use async_trait::async_trait;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::BlockState; use pumpkin_data::BlockState;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
@ -28,21 +28,24 @@ impl BlockMetadata for FenceBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for FenceBlock { impl BlockBehaviour for FenceBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut fence_props = FenceProperties::default(args.block); Box::pin(async move {
fence_props.waterlogged = args.replacing.water_source(); let mut fence_props = FenceProperties::default(args.block);
fence_props.waterlogged = args.replacing.water_source();
compute_fence_state(fence_props, args.world, args.block, args.position).await compute_fence_state(fence_props, args.world, args.block, args.position).await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
let fence_props = FenceProperties::from_state_id(args.state_id, args.block); Box::pin(async move {
compute_fence_state(fence_props, args.world, args.block, args.position).await let fence_props = FenceProperties::from_state_id(args.state_id, args.block);
compute_fence_state(fence_props, args.world, args.block, args.position).await
})
} }
} }

View File

@ -8,7 +8,6 @@ use rand::Rng;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use async_trait::async_trait;
use pumpkin_data::{Block, BlockDirection, BlockState}; use pumpkin_data::{Block, BlockDirection, BlockState};
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
@ -17,7 +16,7 @@ use pumpkin_world::tick::TickPriority;
use crate::block::blocks::tnt::TNTBlock; use crate::block::blocks::tnt::TNTBlock;
use crate::block::{ use crate::block::{
BlockBehaviour, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, BlockBehaviour, BlockFuture, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
OnEntityCollisionArgs, OnScheduledTickArgs, PlacedArgs, OnEntityCollisionArgs, OnScheduledTickArgs, PlacedArgs,
}; };
use crate::world::World; use crate::world::World;
@ -165,144 +164,125 @@ impl FireBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for FireBlock { impl BlockBehaviour for FireBlock {
async fn placed(&self, args: PlacedArgs<'_>) { fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
if args.old_state_id == args.state_id { Box::pin(async move {
// Already a fire if args.old_state_id == args.state_id {
return; // Already a fire
} return;
let dimension = args.world.dimension_type;
// First lets check if we are in OverWorld or Nether, its not possible to place an Nether portal in other dimensions in Vanilla
if (dimension == VanillaDimensionType::Overworld
|| dimension == VanillaDimensionType::TheNether)
&& let Some(portal) =
NetherPortal::get_new_portal(args.world, args.position, HorizontalAxis::X).await
{
portal.create(args.world).await;
return;
}
args.world
.schedule_block_tick(
args.block,
*args.position,
Self::get_fire_tick_delay() as u8,
TickPriority::Normal,
)
.await;
}
async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) {
let base_entity = args.entity.get_entity();
if !base_entity.entity_type.fire_immune {
let ticks = base_entity.fire_ticks.load(Ordering::Relaxed);
if ticks < 0 {
base_entity.fire_ticks.store(ticks + 1, Ordering::Relaxed);
} else if base_entity.entity_type == &EntityType::PLAYER {
let rnd_ticks = rand::rng().random_range(1..3);
base_entity
.fire_ticks
.store(ticks + rnd_ticks, Ordering::Relaxed);
} }
if base_entity.fire_ticks.load(Ordering::Relaxed) >= 0 {
base_entity.set_on_fire_for(8.0);
}
}
}
async fn get_state_for_neighbor_update( let dimension = args.world.dimension_type;
&self, // First lets check if we are in OverWorld or Nether, its not possible to place an Nether portal in other dimensions in Vanilla
args: GetStateForNeighborUpdateArgs<'_>, if (dimension == VanillaDimensionType::Overworld
) -> BlockStateId { || dimension == VanillaDimensionType::TheNether)
if self && let Some(portal) =
.can_place_at(CanPlaceAtArgs { NetherPortal::get_new_portal(args.world, args.position, HorizontalAxis::X).await
server: None, {
world: Some(args.world), portal.create(args.world).await;
block_accessor: args.world, return;
block: &Block::FIRE, }
position: args.position,
direction: BlockDirection::Up, args.world
player: None, .schedule_block_tick(
use_item_on: None, args.block,
}) *args.position,
.await Self::get_fire_tick_delay() as u8,
{ TickPriority::Normal,
let old_fire_props = FireProperties::from_state_id(args.state_id, &Block::FIRE); )
let fire_state_id = self
.get_state_for_position(args.world, &Block::FIRE, args.position)
.await; .await;
let mut fire_props = FireProperties::from_state_id(fire_state_id, &Block::FIRE); })
fire_props.age = EnumVariants::from_index(old_fire_props.age.to_index());
return fire_props.to_state_id(&Block::FIRE);
}
Block::AIR.default_state.id
} }
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
let state = args Box::pin(async move {
.block_accessor let base_entity = args.entity.get_entity();
.get_block_state(&args.position.down()) if !base_entity.entity_type.fire_immune {
.await; let ticks = base_entity.fire_ticks.load(Ordering::Relaxed);
if state.is_side_solid(BlockDirection::Up) { if ticks < 0 {
return true; base_entity.fire_ticks.store(ticks + 1, Ordering::Relaxed);
} } else if base_entity.entity_type == &EntityType::PLAYER {
self.are_blocks_around_flammable(args.block_accessor, args.position) let rnd_ticks = rand::rng().random_range(1..3);
.await base_entity
.fire_ticks
.store(ticks + rnd_ticks, Ordering::Relaxed);
}
if base_entity.fire_ticks.load(Ordering::Relaxed) >= 0 {
base_entity.set_on_fire_for(8.0);
}
}
})
}
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
if self
.can_place_at(CanPlaceAtArgs {
server: None,
world: Some(args.world),
block_accessor: args.world,
block: &Block::FIRE,
position: args.position,
direction: BlockDirection::Up,
player: None,
use_item_on: None,
})
.await
{
let old_fire_props = FireProperties::from_state_id(args.state_id, &Block::FIRE);
let fire_state_id = self
.get_state_for_position(args.world, &Block::FIRE, args.position)
.await;
let mut fire_props = FireProperties::from_state_id(fire_state_id, &Block::FIRE);
fire_props.age = EnumVariants::from_index(old_fire_props.age.to_index());
return fire_props.to_state_id(&Block::FIRE);
}
Block::AIR.default_state.id
})
}
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
let state = args
.block_accessor
.get_block_state(&args.position.down())
.await;
if state.is_side_solid(BlockDirection::Up) {
return true;
}
self.are_blocks_around_flammable(args.block_accessor, args.position)
.await
})
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
let (world, block, pos) = (args.world, args.block, args.position); Box::pin(async move {
world let (world, block, pos) = (args.world, args.block, args.position);
.schedule_block_tick(
block,
*pos,
Self::get_fire_tick_delay() as u8,
TickPriority::Normal,
)
.await;
if !Self
.can_place_at(CanPlaceAtArgs {
server: None,
world: Some(world),
block_accessor: world.as_ref(),
block,
position: pos,
direction: BlockDirection::Up,
player: None,
use_item_on: None,
})
.await
{
world world
.set_block_state( .schedule_block_tick(
pos, block,
Block::AIR.default_state.id, *pos,
BlockFlags::NOTIFY_NEIGHBORS, Self::get_fire_tick_delay() as u8,
TickPriority::Normal,
) )
.await; .await;
return; if !Self
} .can_place_at(CanPlaceAtArgs {
let block_state = world.get_block_state(pos).await; server: None,
//TODO add checks for raining and infiniburn world: Some(world),
let mut fire_props = FireProperties::from_state_id(block_state.id, &Block::FIRE); block_accessor: world.as_ref(),
let age = fire_props.age.to_index() + 1; block,
position: pos,
let random = rand::rng().random_range(0..3) / 2; direction: BlockDirection::Up,
let new_age = (age + random).min(15); player: None,
if new_age != age { use_item_on: None,
fire_props.age = EnumVariants::from_index(new_age); })
let new_state_id = fire_props.to_state_id(&Block::FIRE); .await
world {
.set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS)
.await;
}
if !Self.are_blocks_around_flammable(world.as_ref(), pos).await {
let block_below_state = world.get_block_state(&pos.down()).await;
if block_below_state.is_side_solid(BlockDirection::Up) {
world world
.set_block_state( .set_block_state(
pos, pos,
@ -310,100 +290,132 @@ impl BlockBehaviour for FireBlock {
BlockFlags::NOTIFY_NEIGHBORS, BlockFlags::NOTIFY_NEIGHBORS,
) )
.await; .await;
return;
} }
return; let block_state = world.get_block_state(pos).await;
} //TODO add checks for raining and infiniburn
let mut fire_props = FireProperties::from_state_id(block_state.id, &Block::FIRE);
let age = fire_props.age.to_index() + 1;
if age == 15 let random = rand::rng().random_range(0..3) / 2;
&& rand::rng().random_range(0..4) == 0 let new_age = (age + random).min(15);
&& !Self::is_flammable(world.get_block_state(&pos.down()).await) if new_age != age {
{ fire_props.age = EnumVariants::from_index(new_age);
world let new_state_id = fire_props.to_state_id(&Block::FIRE);
.set_block_state( world
pos, .set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS)
Block::AIR.default_state.id, .await;
BlockFlags::NOTIFY_NEIGHBORS, }
)
.await;
return;
}
Self.try_spreading_fire( if !Self.are_blocks_around_flammable(world.as_ref(), pos).await {
world, let block_below_state = world.get_block_state(&pos.down()).await;
&pos.offset(BlockDirection::East.to_offset()), if block_below_state.is_side_solid(BlockDirection::Up) {
300, world
age, .set_block_state(
) pos,
.await; Block::AIR.default_state.id,
Self.try_spreading_fire( BlockFlags::NOTIFY_NEIGHBORS,
world, )
&pos.offset(BlockDirection::West.to_offset()), .await;
300, }
age, return;
) }
.await;
Self.try_spreading_fire( if age == 15
world, && rand::rng().random_range(0..4) == 0
&pos.offset(BlockDirection::North.to_offset()), && !Self::is_flammable(world.get_block_state(&pos.down()).await)
300, {
age, world
) .set_block_state(
.await; pos,
Self.try_spreading_fire( Block::AIR.default_state.id,
world, BlockFlags::NOTIFY_NEIGHBORS,
&pos.offset(BlockDirection::South.to_offset()), )
300, .await;
age, return;
) }
.await;
Self.try_spreading_fire(world, &pos.offset(BlockDirection::Up.to_offset()), 250, age) Self.try_spreading_fire(
world,
&pos.offset(BlockDirection::East.to_offset()),
300,
age,
)
.await;
Self.try_spreading_fire(
world,
&pos.offset(BlockDirection::West.to_offset()),
300,
age,
)
.await;
Self.try_spreading_fire(
world,
&pos.offset(BlockDirection::North.to_offset()),
300,
age,
)
.await;
Self.try_spreading_fire(
world,
&pos.offset(BlockDirection::South.to_offset()),
300,
age,
)
.await;
Self.try_spreading_fire(world, &pos.offset(BlockDirection::Up.to_offset()), 250, age)
.await;
Self.try_spreading_fire(
world,
&pos.offset(BlockDirection::Down.to_offset()),
250,
age,
)
.await; .await;
Self.try_spreading_fire(
world,
&pos.offset(BlockDirection::Down.to_offset()),
250,
age,
)
.await;
for l in -1..=1 { for l in -1..=1 {
for m in -1..=1 { for m in -1..=1 {
for n in -1..=4 { for n in -1..=4 {
if l != 0 || n != 0 || m != 0 { if l != 0 || n != 0 || m != 0 {
let offset_pos = pos.offset(Vector3::new(l, n, m)); let offset_pos = pos.offset(Vector3::new(l, n, m));
let burn_chance = Self.get_burn_chance(world, &offset_pos).await; let burn_chance = Self.get_burn_chance(world, &offset_pos).await;
if burn_chance > 0 { if burn_chance > 0 {
let o = 100 + if n > 1 { (n - 1) * 100 } else { 0 }; let o = 100 + if n > 1 { (n - 1) * 100 } else { 0 };
let p: i32 = burn_chance let p: i32 = burn_chance
+ 40 + 40
+ (world.level_info.read().await.difficulty as i32) * 7 + (world.level_info.read().await.difficulty as i32) * 7
/ i32::from(age + 30); / i32::from(age + 30);
if p > 0 && rand::rng().random_range(0..o) <= p { if p > 0 && rand::rng().random_range(0..o) <= p {
let new_age = (age + rand::rng().random_range(0..5) / 4).min(15); let new_age =
let fire_state_id = (age + rand::rng().random_range(0..5) / 4).min(15);
self.get_state_for_position(world, block, &offset_pos).await; let fire_state_id = self
let mut new_fire_props = .get_state_for_position(world, block, &offset_pos)
FireProperties::from_state_id(fire_state_id, &Block::FIRE); .await;
new_fire_props.age = EnumVariants::from_index(new_age); let mut new_fire_props =
FireProperties::from_state_id(fire_state_id, &Block::FIRE);
new_fire_props.age = EnumVariants::from_index(new_age);
//TODO drop items for burned blocks //TODO drop items for burned blocks
world world
.set_block_state( .set_block_state(
&offset_pos, &offset_pos,
new_fire_props.to_state_id(&Block::FIRE), new_fire_props.to_state_id(&Block::FIRE),
BlockFlags::NOTIFY_NEIGHBORS, BlockFlags::NOTIFY_NEIGHBORS,
) )
.await; .await;
}
} }
} }
} }
} }
} }
} })
} }
async fn broken(&self, args: BrokenArgs<'_>) { fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
FireBlockBase::broken(args.world.clone(), *args.position).await; Box::pin(async move {
FireBlockBase::broken(args.world.clone(), *args.position).await;
})
} }
} }

View File

@ -1,10 +1,11 @@
use async_trait::async_trait;
use pumpkin_data::tag::Taggable; use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, tag}; use pumpkin_data::{Block, tag};
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use crate::block::{BlockBehaviour, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs}; use crate::block::{
BlockBehaviour, BlockFuture, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
};
use super::FireBlockBase; use super::FireBlockBase;
@ -18,24 +19,29 @@ impl SoulFireBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for SoulFireBlock { impl BlockBehaviour for SoulFireBlock {
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
if !Self::is_soul_base(args.world.get_block(&args.position.down()).await) { Box::pin(async move {
return Block::AIR.default_state.id; if !Self::is_soul_base(args.world.get_block(&args.position.down()).await) {
} return Block::AIR.default_state.id;
}
args.state_id args.state_id
})
} }
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Self::is_soul_base(args.block_accessor.get_block(&args.position.down()).await) Box::pin(async move {
Self::is_soul_base(args.block_accessor.get_block(&args.position.down()).await)
})
} }
async fn broken(&self, args: BrokenArgs<'_>) { fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
FireBlockBase::broken(args.world.clone(), *args.position).await; Box::pin(async move {
FireBlockBase::broken(args.world.clone(), *args.position).await;
})
} }
} }

View File

@ -1,6 +1,5 @@
use crate::block::registry::BlockActionResult; use crate::block::registry::BlockActionResult;
use crate::block::{BlockBehaviour, RandomTickArgs, UseWithItemArgs}; use crate::block::{BlockBehaviour, BlockFuture, RandomTickArgs, UseWithItemArgs};
use async_trait::async_trait;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_data::flower_pot_transformations::get_potted_item; use pumpkin_data::flower_pot_transformations::get_potted_item;
use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_data::tag::{RegistryKey, get_tag_values};
@ -11,69 +10,75 @@ use pumpkin_world::world::BlockFlags;
#[pumpkin_block_from_tag("minecraft:flower_pots")] #[pumpkin_block_from_tag("minecraft:flower_pots")]
pub struct FlowerPotBlock; pub struct FlowerPotBlock;
#[async_trait]
impl BlockBehaviour for FlowerPotBlock { impl BlockBehaviour for FlowerPotBlock {
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { fn use_with_item<'a>(
let item = args.item_stack.lock().await.item; &'a self,
//Place the flower inside the pot args: UseWithItemArgs<'a>,
let potted_block_id = get_potted_item(item.id); ) -> BlockFuture<'a, BlockActionResult> {
if args.block.eq(&Block::FLOWER_POT) { Box::pin(async move {
if potted_block_id != 0 { let item = args.item_stack.lock().await.item;
//Place the flower inside the pot
let potted_block_id = get_potted_item(item.id);
if args.block.eq(&Block::FLOWER_POT) {
if potted_block_id != 0 {
args.world
.set_block_state(
args.position,
Block::from_id(potted_block_id).default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
}
return BlockActionResult::Success;
} else if potted_block_id != 0 {
//if the player have an item that can be potted in his hand, nothing happens
return BlockActionResult::Consume;
}
//get the flower + empty the pot
args.world
.set_block_state(
args.position,
Block::FLOWER_POT.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
BlockActionResult::Success
})
}
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
if (args
.world
.dimension_type
.eq(&VanillaDimensionType::Overworld)
|| args
.world
.dimension_type
.eq(&VanillaDimensionType::OverworldCaves))
&& args.block.eq(&Block::POTTED_CLOSED_EYEBLOSSOM)
&& args.world.level_time.lock().await.time_of_day % 24000 > 14500
{
args.world args.world
.set_block_state( .set_block_state(
args.position, args.position,
Block::from_id(potted_block_id).default_state.id, Block::POTTED_OPEN_EYEBLOSSOM.default_state.id,
BlockFlags::NOTIFY_ALL, BlockFlags::NOTIFY_ALL,
) )
.await; .await;
} }
return BlockActionResult::Success; if args.block.eq(&Block::POTTED_OPEN_EYEBLOSSOM)
} else if potted_block_id != 0 { && args.world.level_time.lock().await.time_of_day % 24000 <= 14500
//if the player have an item that can be potted in his hand, nothing happens {
return BlockActionResult::Consume; args.world
} .set_block_state(
args.position,
//get the flower + empty the pot Block::POTTED_CLOSED_EYEBLOSSOM.default_state.id,
args.world BlockFlags::NOTIFY_ALL,
.set_block_state( )
args.position, .await;
Block::FLOWER_POT.default_state.id, }
BlockFlags::NOTIFY_ALL, })
)
.await;
BlockActionResult::Success
}
async fn random_tick(&self, args: RandomTickArgs<'_>) {
if (args
.world
.dimension_type
.eq(&VanillaDimensionType::Overworld)
|| args
.world
.dimension_type
.eq(&VanillaDimensionType::OverworldCaves))
&& args.block.eq(&Block::POTTED_CLOSED_EYEBLOSSOM)
&& args.world.level_time.lock().await.time_of_day % 24000 > 14500
{
args.world
.set_block_state(
args.position,
Block::POTTED_OPEN_EYEBLOSSOM.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
}
if args.block.eq(&Block::POTTED_OPEN_EYEBLOSSOM)
&& args.world.level_time.lock().await.time_of_day % 24000 <= 14500
{
args.world
.set_block_state(
args.position,
Block::POTTED_CLOSED_EYEBLOSSOM.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
}
} }
} }

View File

@ -8,12 +8,16 @@ use pumpkin_inventory::{
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_world::{ use pumpkin_world::{
BlockStateId,
block::entities::{BlockEntity, furnace::FurnaceBlockEntity}, block::entities::{BlockEntity, furnace::FurnaceBlockEntity},
inventory::Inventory, inventory::Inventory,
}; };
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::block::BlockBehaviour; use crate::block::{
BlockBehaviour, BlockFuture, BrokenArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs,
registry::BlockActionResult,
};
struct FurnaceScreenFactory { struct FurnaceScreenFactory {
inventory: Arc<dyn Inventory>, inventory: Arc<dyn Inventory>,
@ -55,109 +59,47 @@ impl ScreenHandlerFactory for FurnaceScreenFactory {
#[pumpkin_block("minecraft:furnace")] #[pumpkin_block("minecraft:furnace")]
pub struct FurnaceBlock; pub struct FurnaceBlock;
#[async_trait]
impl BlockBehaviour for FurnaceBlock { impl BlockBehaviour for FurnaceBlock {
async fn normal_use( fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
&self, Box::pin(async move {
args: crate::block::NormalUseArgs<'_>, if let Some(block_entity) = args.world.get_block_entity(args.position).await
) -> crate::block::registry::BlockActionResult { && let Some(inventory) = block_entity.clone().get_inventory()
if let Some(block_entity) = args.world.get_block_entity(args.position).await {
&& let Some(inventory) = block_entity.clone().get_inventory() let furnace_screen_factory = FurnaceScreenFactory::new(inventory, block_entity);
{ args.player
let furnace_screen_factory = FurnaceScreenFactory::new(inventory, block_entity); .open_handled_screen(&furnace_screen_factory)
args.player .await;
.open_handled_screen(&furnace_screen_factory) }
crate::block::registry::BlockActionResult::Consume
})
}
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut props = FurnaceLikeProperties::default(args.block);
props.facing = args
.player
.living_entity
.entity
.get_horizontal_facing()
.opposite();
props.to_state_id(args.block)
})
}
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
let furnace_block_entity = FurnaceBlockEntity::new(*args.position);
args.world
.add_block_entity(Arc::new(furnace_block_entity))
.await; .await;
} })
crate::block::registry::BlockActionResult::Consume
} }
//Same to normal_use fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
async fn use_with_item( Box::pin(async move {
&self, args.world.remove_block_entity(args.position).await;
_args: crate::block::UseWithItemArgs<'_>, })
) -> crate::block::registry::BlockActionResult {
crate::block::registry::BlockActionResult::PassToDefaultBlockAction
}
async fn on_entity_collision(&self, _args: crate::block::OnEntityCollisionArgs<'_>) {}
fn should_drop_items_on_explosion(&self) -> bool {
true
}
async fn explode(&self, _args: crate::block::ExplodeArgs<'_>) {}
async fn on_synced_block_event(&self, _args: crate::block::OnSyncedBlockEventArgs<'_>) -> bool {
false
}
async fn on_place(&self, args: crate::block::OnPlaceArgs<'_>) -> pumpkin_world::BlockStateId {
let mut props = FurnaceLikeProperties::default(args.block);
props.facing = args
.player
.living_entity
.entity
.get_horizontal_facing()
.opposite();
props.to_state_id(args.block)
}
async fn random_tick(&self, _args: crate::block::RandomTickArgs<'_>) {}
async fn can_place_at(&self, _args: crate::block::CanPlaceAtArgs<'_>) -> bool {
true
}
async fn can_update_at(&self, _args: crate::block::CanUpdateAtArgs<'_>) -> bool {
false
}
async fn placed(&self, args: crate::block::PlacedArgs<'_>) {
let furnace_block_entity = FurnaceBlockEntity::new(*args.position);
args.world
.add_block_entity(Arc::new(furnace_block_entity))
.await;
}
async fn player_placed(&self, _args: crate::block::PlayerPlacedArgs<'_>) {}
async fn broken(&self, args: crate::block::BrokenArgs<'_>) {
args.world.remove_block_entity(args.position).await;
}
async fn on_neighbor_update(&self, _args: crate::block::OnNeighborUpdateArgs<'_>) {}
async fn prepare(&self, _args: crate::block::PrepareArgs<'_>) {}
async fn get_state_for_neighbor_update(
&self,
args: crate::block::GetStateForNeighborUpdateArgs<'_>,
) -> pumpkin_world::BlockStateId {
args.state_id
}
async fn on_scheduled_tick(&self, _args: crate::block::OnScheduledTickArgs<'_>) {}
async fn on_state_replaced(&self, _args: crate::block::OnStateReplacedArgs<'_>) {}
async fn emits_redstone_power(&self, _args: crate::block::EmitsRedstonePowerArgs<'_>) -> bool {
false
}
async fn get_weak_redstone_power(&self, _args: crate::block::GetRedstonePowerArgs<'_>) -> u8 {
0
}
async fn get_strong_redstone_power(&self, _args: crate::block::GetRedstonePowerArgs<'_>) -> u8 {
0
}
async fn get_comparator_output(
&self,
_args: crate::block::GetComparatorOutputArgs<'_>,
) -> Option<u8> {
None
} }
} }

View File

@ -1,6 +1,6 @@
use crate::block::BlockFuture;
use crate::block::GetStateForNeighborUpdateArgs; use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs; use crate::block::OnPlaceArgs;
use async_trait::async_trait;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::tag::RegistryKey; use pumpkin_data::tag::RegistryKey;
@ -26,21 +26,24 @@ impl BlockMetadata for GlassPaneBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for GlassPaneBlock { impl BlockBehaviour for GlassPaneBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut pane_props = GlassPaneProperties::default(args.block); Box::pin(async move {
pane_props.waterlogged = args.replacing.water_source(); let mut pane_props = GlassPaneProperties::default(args.block);
pane_props.waterlogged = args.replacing.water_source();
compute_pane_state(pane_props, args.world, args.block, args.position).await compute_pane_state(pane_props, args.world, args.block, args.position).await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
let pane_props = GlassPaneProperties::from_state_id(args.state_id, args.block); Box::pin(async move {
compute_pane_state(pane_props, args.world, args.block, args.position).await let pane_props = GlassPaneProperties::from_state_id(args.state_id, args.block);
compute_pane_state(pane_props, args.world, args.block, args.position).await
})
} }
} }

View File

@ -1,5 +1,4 @@
use crate::block::{BlockBehaviour, BlockMetadata, OnPlaceArgs}; use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, OnPlaceArgs};
use async_trait::async_trait;
use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties}; use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties};
use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_data::tag::{RegistryKey, get_tag_values};
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
@ -15,16 +14,17 @@ impl BlockMetadata for GlazedTerracottaBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for GlazedTerracottaBlock { impl BlockBehaviour for GlazedTerracottaBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut prop = WallTorchLikeProperties::default(args.block); Box::pin(async move {
prop.facing = args let mut prop = WallTorchLikeProperties::default(args.block);
.player prop.facing = args
.living_entity .player
.entity .living_entity
.get_horizontal_facing() .entity
.opposite(); .get_horizontal_facing()
prop.to_state_id(args.block) .opposite();
prop.to_state_id(args.block)
})
} }
} }

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::{ use pumpkin_data::{
Block, BlockDirection, HorizontalFacingExt, Block, BlockDirection, HorizontalFacingExt,
block_properties::{BlockFace, BlockProperties, GrindstoneLikeProperties}, block_properties::{BlockFace, BlockProperties, GrindstoneLikeProperties},
@ -7,8 +6,8 @@ use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{BlockStateId, world::BlockAccessor}; use pumpkin_world::{BlockStateId, world::BlockAccessor};
use crate::block::BlockBehaviour;
use crate::block::CanPlaceAtArgs; use crate::block::CanPlaceAtArgs;
use crate::block::{BlockBehaviour, BlockFuture};
use crate::block::{GetStateForNeighborUpdateArgs, OnPlaceArgs}; use crate::block::{GetStateForNeighborUpdateArgs, OnPlaceArgs};
use super::abstract_wall_mounting::WallMountedBlock; use super::abstract_wall_mounting::WallMountedBlock;
@ -16,39 +15,41 @@ use super::abstract_wall_mounting::WallMountedBlock;
#[pumpkin_block("minecraft:grindstone")] #[pumpkin_block("minecraft:grindstone")]
pub struct GrindstoneBlock; pub struct GrindstoneBlock;
#[async_trait]
impl BlockBehaviour for GrindstoneBlock { impl BlockBehaviour for GrindstoneBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut props = Box::pin(async move {
GrindstoneLikeProperties::from_state_id(args.block.default_state.id, args.block); let mut props =
(props.face, props.facing) = GrindstoneLikeProperties::from_state_id(args.block.default_state.id, args.block);
WallMountedBlock::get_placement_face(self, args.player, args.direction); (props.face, props.facing) =
WallMountedBlock::get_placement_face(self, args.player, args.direction);
props.to_state_id(args.block) props.to_state_id(args.block)
})
} }
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
WallMountedBlock::can_place_at(self, args.block_accessor, args.position, args.direction) Box::pin(async move {
.await WallMountedBlock::can_place_at(self, args.block_accessor, args.position, args.direction)
.await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
WallMountedBlock::get_state_for_neighbor_update(self, args).await Box::pin(async move { WallMountedBlock::get_state_for_neighbor_update(self, args).await })
} }
} }
#[async_trait]
impl WallMountedBlock for GrindstoneBlock { impl WallMountedBlock for GrindstoneBlock {
async fn can_place_at( fn can_place_at<'a>(
&self, &'a self,
_world: &dyn BlockAccessor, _world: &'a dyn BlockAccessor,
_pos: &BlockPos, _pos: &'a BlockPos,
_direction: BlockDirection, _direction: BlockDirection,
) -> bool { ) -> BlockFuture<'a, bool> {
true Box::pin(async move { true })
} }
fn get_direction(&self, state_id: BlockStateId, block: &Block) -> BlockDirection { fn get_direction(&self, state_id: BlockStateId, block: &Block) -> BlockDirection {

View File

@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::blocks::redstone::block_receives_redstone_power;
use crate::block::{OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs}; use crate::block::{BlockFuture, OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs};
use crate::block::{ use crate::block::{
registry::BlockActionResult, registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs}, {BlockBehaviour, NormalUseArgs},
@ -47,52 +47,59 @@ pub struct HopperBlock;
type HopperLikeProperties = pumpkin_data::block_properties::HopperLikeProperties; type HopperLikeProperties = pumpkin_data::block_properties::HopperLikeProperties;
#[async_trait]
impl BlockBehaviour for HopperBlock { impl BlockBehaviour for HopperBlock {
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
if let Some(block_entity) = args.world.get_block_entity(args.position).await Box::pin(async move {
&& let Some(inventory) = block_entity.get_inventory() if let Some(block_entity) = args.world.get_block_entity(args.position).await
{ && let Some(inventory) = block_entity.get_inventory()
args.player {
.open_handled_screen(&HopperBlockScreenFactory(inventory)) args.player
.open_handled_screen(&HopperBlockScreenFactory(inventory))
.await;
}
BlockActionResult::Success
})
}
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut props = HopperLikeProperties::default(args.block);
props.facing = match args.direction {
BlockDirection::North => HopperFacing::North,
BlockDirection::East => HopperFacing::East,
BlockDirection::South => HopperFacing::South,
BlockDirection::West => HopperFacing::West,
BlockDirection::Up | BlockDirection::Down => HopperFacing::Down,
};
props.enabled = true;
props.to_state_id(args.block)
})
}
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
let props = HopperLikeProperties::from_state_id(args.state_id, args.block);
let hopper_block_entity = HopperBlockEntity::new(*args.position, props.facing);
args.world
.add_block_entity(Arc::new(hopper_block_entity))
.await; .await;
} if Block::from_state_id(args.old_state_id) != Block::from_state_id(args.state_id) {
check_powered_state(args.world, args.position, args.state_id, args.block).await;
BlockActionResult::Success }
})
} }
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
let mut props = HopperLikeProperties::default(args.block); Box::pin(async move {
props.facing = match args.direction { check_powered_state(
BlockDirection::North => HopperFacing::North, args.world,
BlockDirection::East => HopperFacing::East, args.position,
BlockDirection::South => HopperFacing::South, args.world.get_block_state_id(args.position).await,
BlockDirection::West => HopperFacing::West, args.block,
BlockDirection::Up | BlockDirection::Down => HopperFacing::Down, )
};
props.enabled = true;
props.to_state_id(args.block)
}
async fn placed(&self, args: PlacedArgs<'_>) {
let props = HopperLikeProperties::from_state_id(args.state_id, args.block);
let hopper_block_entity = HopperBlockEntity::new(*args.position, props.facing);
args.world
.add_block_entity(Arc::new(hopper_block_entity))
.await; .await;
if Block::from_state_id(args.old_state_id) != Block::from_state_id(args.state_id) { })
check_powered_state(args.world, args.position, args.state_id, args.block).await;
}
}
async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) {
check_powered_state(
args.world,
args.position,
args.world.get_block_state_id(args.position).await,
args.block,
)
.await;
} }
} }

View File

@ -1,6 +1,6 @@
use crate::block::BlockFuture;
use crate::block::GetStateForNeighborUpdateArgs; use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs; use crate::block::OnPlaceArgs;
use async_trait::async_trait;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::tag::Taggable; use pumpkin_data::tag::Taggable;
@ -17,21 +17,24 @@ use crate::world::World;
#[pumpkin_block("minecraft:iron_bars")] #[pumpkin_block("minecraft:iron_bars")]
pub struct IronBarsBlock; pub struct IronBarsBlock;
#[async_trait]
impl BlockBehaviour for IronBarsBlock { impl BlockBehaviour for IronBarsBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut bars_props = IronBarsProperties::default(args.block); Box::pin(async move {
bars_props.waterlogged = args.replacing.water_source(); let mut bars_props = IronBarsProperties::default(args.block);
bars_props.waterlogged = args.replacing.water_source();
compute_bars_state(bars_props, args.world, args.block, args.position).await compute_bars_state(bars_props, args.world, args.block, args.position).await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
let bars_props = IronBarsProperties::from_state_id(args.state_id, args.block); Box::pin(async move {
compute_bars_state(bars_props, args.world, args.block, args.position).await let bars_props = IronBarsProperties::from_state_id(args.state_id, args.block);
compute_bars_state(bars_props, args.world, args.block, args.position).await
})
} }
} }

View File

@ -1,9 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::registry::BlockActionResult; use crate::block::registry::BlockActionResult;
use crate::block::{BlockBehaviour, BrokenArgs, UseWithItemArgs}; use crate::block::{BlockBehaviour, BlockFuture, BrokenArgs, UseWithItemArgs};
use crate::world::World; use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::data_component_impl::JukeboxPlayableImpl; use pumpkin_data::data_component_impl::JukeboxPlayableImpl;
use pumpkin_data::world::WorldEvent; use pumpkin_data::world::WorldEvent;
use pumpkin_data::{ use pumpkin_data::{
@ -45,56 +44,62 @@ impl JukeboxBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for JukeboxBlock { impl BlockBehaviour for JukeboxBlock {
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { fn use_with_item<'a>(
let world = &args.player.living_entity.entity.world; &'a self,
args: UseWithItemArgs<'a>,
) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let world = &args.player.living_entity.entity.world;
// if the jukebox already has a record, stop playing // if the jukebox already has a record, stop playing
if self.has_record(args.block, args.position, world).await { if self.has_record(args.block, args.position, world).await {
self.stop_music(args.block, args.position, world).await; self.stop_music(args.block, args.position, world).await;
return BlockActionResult::Success; return BlockActionResult::Success;
} }
let jukebox_playable = args let jukebox_playable = args
.item_stack .item_stack
.lock() .lock()
.await .await
.get_data_component::<JukeboxPlayableImpl>() .get_data_component::<JukeboxPlayableImpl>()
.map(|i| i.song); .map(|i| i.song);
let Some(jukebox_playable) = jukebox_playable else { let Some(jukebox_playable) = jukebox_playable else {
return BlockActionResult::Pass; return BlockActionResult::Pass;
}; };
let Some(song) = jukebox_playable.split(':').nth(1) else { let Some(song) = jukebox_playable.split(':').nth(1) else {
return BlockActionResult::Pass; return BlockActionResult::Pass;
}; };
let Some(jukebox_song) = SYNCED_REGISTRIES.get_jukebox_song_index(song) else { let Some(jukebox_song) = SYNCED_REGISTRIES.get_jukebox_song_index(song) else {
log::error!("Jukebox playable song not registered!"); log::error!("Jukebox playable song not registered!");
return BlockActionResult::Pass; return BlockActionResult::Pass;
}; };
// TODO: Update block nbt // TODO: Update block nbt
self.set_record(true, args.block, args.position, world) self.set_record(true, args.block, args.position, world)
.await; .await;
world world
.sync_world_event( .sync_world_event(
WorldEvent::JukeboxStartsPlaying, WorldEvent::JukeboxStartsPlaying,
*args.position, *args.position,
jukebox_song as i32, jukebox_song as i32,
) )
.await; .await;
BlockActionResult::Success BlockActionResult::Success
})
} }
async fn broken(&self, args: BrokenArgs<'_>) { fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
// For now just stop the music at this position Box::pin(async move {
args.world // For now just stop the music at this position
.sync_world_event(WorldEvent::JukeboxStopsPlaying, *args.position, 0) args.world
.await; .sync_world_event(WorldEvent::JukeboxStopsPlaying, *args.position, 0)
.await;
})
} }
} }

View File

@ -1,8 +1,8 @@
use crate::block::{ use crate::block::{
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
OnScheduledTickArgs,
}; };
use crate::world::World; use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::block_properties::HorizontalFacing; use pumpkin_data::block_properties::HorizontalFacing;
use pumpkin_data::block_properties::{BlockProperties, LadderLikeProperties}; use pumpkin_data::block_properties::{BlockProperties, LadderLikeProperties};
@ -15,39 +15,46 @@ use pumpkin_world::world::BlockFlags;
#[pumpkin_block("minecraft:ladder")] #[pumpkin_block("minecraft:ladder")]
pub struct LadderBlock; pub struct LadderBlock;
#[async_trait]
impl BlockBehaviour for LadderBlock { impl BlockBehaviour for LadderBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut props = LadderLikeProperties::default(args.block); Box::pin(async move {
props.facing = args.direction.opposite().to_cardinal_direction(); let mut props = LadderLikeProperties::default(args.block);
props.to_state_id(args.block) props.facing = args.direction.opposite().to_cardinal_direction();
props.to_state_id(args.block)
})
} }
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
args.block_accessor Box::pin(async move {
.get_block_state(&args.use_item_on.unwrap().position) args.block_accessor
.await .get_block_state(&args.use_item_on.unwrap().position)
.is_side_solid(args.direction.opposite()) .await
&& args.direction.is_horizontal() .is_side_solid(args.direction.opposite())
&& args.direction.is_horizontal()
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
if !can_place_at(args.world, args.position).await { Box::pin(async move {
args.world if !can_place_at(args.world, args.position).await {
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal) args.world
.await; .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
} .await;
args.state_id }
args.state_id
})
} }
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
if !can_place_at(args.world, args.position).await { Box::pin(async move {
args.world if !can_place_at(args.world, args.position).await {
.break_block(args.position, None, BlockFlags::empty()) args.world
.await; .break_block(args.position, None, BlockFlags::empty())
} .await;
}
})
} }
} }

View File

@ -1,9 +1,8 @@
use crate::block::{ use crate::block::{
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
OnScheduledTickArgs, OnPlaceArgs, OnScheduledTickArgs,
}; };
use crate::world::World; use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::tag::Taggable; use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, BlockDirection, tag}; use pumpkin_data::{Block, BlockDirection, tag};
@ -24,42 +23,48 @@ impl BlockMetadata for LanternBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for LanternBlock { impl BlockBehaviour for LanternBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut props = pumpkin_data::block_properties::LanternLikeProperties::default(args.block); Box::pin(async move {
props.r#waterlogged = args.replacing.water_source(); let mut props =
pumpkin_data::block_properties::LanternLikeProperties::default(args.block);
props.r#waterlogged = args.replacing.water_source();
let block_up_state = args.world.get_block_state(&args.position.up()).await; let block_up_state = args.world.get_block_state(&args.position.up()).await;
if block_up_state.is_center_solid(BlockDirection::Down) { if block_up_state.is_center_solid(BlockDirection::Down) {
props.r#hanging = true; props.r#hanging = true;
} }
props.to_state_id(args.block) props.to_state_id(args.block)
})
} }
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
can_place_at(args.world.unwrap(), args.position).await Box::pin(async move { can_place_at(args.world.unwrap(), args.position).await })
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
if !can_place_at(args.world, args.position).await { Box::pin(async move {
args.world if !can_place_at(args.world, args.position).await {
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal) args.world
.await; .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
} .await;
args.state_id }
args.state_id
})
} }
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
if !can_place_at(args.world, args.position).await { Box::pin(async move {
args.world if !can_place_at(args.world, args.position).await {
.break_block(args.position, None, BlockFlags::empty()) args.world
.await; .break_block(args.position, None, BlockFlags::empty())
} .await;
}
})
} }
} }

View File

@ -1,5 +1,4 @@
use crate::block::{BlockBehaviour, OnPlaceArgs}; use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs};
use async_trait::async_trait;
use pumpkin_data::block_properties::{BlockProperties, LecternLikeProperties}; use pumpkin_data::block_properties::{BlockProperties, LecternLikeProperties};
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
@ -7,16 +6,17 @@ use pumpkin_world::BlockStateId;
#[pumpkin_block("minecraft:lectern")] #[pumpkin_block("minecraft:lectern")]
pub struct LecternBlock; pub struct LecternBlock;
#[async_trait]
impl BlockBehaviour for LecternBlock { impl BlockBehaviour for LecternBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut props = LecternLikeProperties::default(args.block); Box::pin(async move {
props.facing = args let mut props = LecternLikeProperties::default(args.block);
.player props.facing = args
.living_entity .player
.entity .living_entity
.get_horizontal_facing() .entity
.opposite(); .get_horizontal_facing()
props.to_state_id(args.block) .opposite();
props.to_state_id(args.block)
})
} }
} }

View File

@ -1,23 +1,23 @@
use async_trait::async_trait;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_data::tag::{RegistryKey, get_tag_values};
use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use crate::block::BlockBehaviour;
use crate::block::OnPlaceArgs; use crate::block::OnPlaceArgs;
use crate::block::{BlockBehaviour, BlockFuture};
type LogProperties = pumpkin_data::block_properties::PaleOakWoodLikeProperties; type LogProperties = pumpkin_data::block_properties::PaleOakWoodLikeProperties;
#[pumpkin_block_from_tag("minecraft:logs")] #[pumpkin_block_from_tag("minecraft:logs")]
pub struct LogBlock; pub struct LogBlock;
#[async_trait]
impl BlockBehaviour for LogBlock { impl BlockBehaviour for LogBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut log_props = LogProperties::default(args.block); Box::pin(async move {
log_props.axis = args.direction.to_axis(); let mut log_props = LogProperties::default(args.block);
log_props.axis = args.direction.to_axis();
log_props.to_state_id(args.block) log_props.to_state_id(args.block)
})
} }
} }

View File

@ -1,5 +1,4 @@
use crate::block::{BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs}; use crate::block::{BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs};
use async_trait::async_trait;
use pumpkin_data::block_properties::{BlockProperties, MangroveRootsLikeProperties}; use pumpkin_data::block_properties::{BlockProperties, MangroveRootsLikeProperties};
use pumpkin_data::fluid::Fluid; use pumpkin_data::fluid::Fluid;
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
@ -9,29 +8,32 @@ use pumpkin_world::tick::TickPriority;
#[pumpkin_block("minecraft:mangrove_roots")] #[pumpkin_block("minecraft:mangrove_roots")]
pub struct MangroveRootsBlock; pub struct MangroveRootsBlock;
#[async_trait]
impl BlockBehaviour for MangroveRootsBlock { impl BlockBehaviour for MangroveRootsBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut props = MangroveRootsLikeProperties::default(args.block); Box::pin(async move {
props.waterlogged = args.replacing.water_source(); let mut props = MangroveRootsLikeProperties::default(args.block);
props.to_state_id(args.block) props.waterlogged = args.replacing.water_source();
props.to_state_id(args.block)
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
let props = MangroveRootsLikeProperties::from_state_id(args.state_id, args.block); Box::pin(async move {
if props.waterlogged { let props = MangroveRootsLikeProperties::from_state_id(args.state_id, args.block);
args.world if props.waterlogged {
.schedule_fluid_tick( args.world
&Fluid::WATER, .schedule_fluid_tick(
*args.position, &Fluid::WATER,
Fluid::WATER.flow_speed as u8, *args.position,
TickPriority::Normal, Fluid::WATER.flow_speed as u8,
) TickPriority::Normal,
.await; )
} .await;
props.to_state_id(args.block) }
props.to_state_id(args.block)
})
} }
} }

View File

@ -1,10 +1,11 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::{BlockBehaviour, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs}; use crate::block::{
BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs,
};
use crate::entity::EntityBase; use crate::entity::EntityBase;
use crate::world::World; use crate::world::World;
use crate::world::portal::nether::NetherPortal; use crate::world::portal::nether::NetherPortal;
use async_trait::async_trait;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_data::block_properties::{Axis, BlockProperties, NetherPortalLikeProperties}; use pumpkin_data::block_properties::{Axis, BlockProperties, NetherPortalLikeProperties};
use pumpkin_data::entity::EntityType; use pumpkin_data::entity::EntityType;
@ -36,43 +37,47 @@ impl NetherPortalBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for NetherPortalBlock { impl BlockBehaviour for NetherPortalBlock {
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
let axis = args.direction.to_axis(); Box::pin(async move {
let is_horizontal = axis == Axis::X && axis == Axis::Z; let axis = args.direction.to_axis();
let state_axis = let is_horizontal = axis == Axis::X && axis == Axis::Z;
NetherPortalLikeProperties::from_state_id(args.state_id, &Block::NETHER_PORTAL).axis; let state_axis =
if is_horizontal NetherPortalLikeProperties::from_state_id(args.state_id, &Block::NETHER_PORTAL)
|| args.neighbor_state_id == args.state_id .axis;
|| NetherPortal::get_on_axis(args.world, args.position, state_axis) if is_horizontal
.await || args.neighbor_state_id == args.state_id
.is_some_and(|e| e.was_already_valid()) || NetherPortal::get_on_axis(args.world, args.position, state_axis)
{ .await
return args.state_id; .is_some_and(|e| e.was_already_valid())
} {
Block::AIR.default_state.id return args.state_id;
}
Block::AIR.default_state.id
})
} }
async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
let target_world = if args.world.dimension_type == VanillaDimensionType::TheNether { Box::pin(async move {
args.server let target_world = if args.world.dimension_type == VanillaDimensionType::TheNether {
.get_world_from_dimension(VanillaDimensionType::Overworld) args.server
.await .get_world_from_dimension(VanillaDimensionType::Overworld)
} else { .await
args.server } else {
.get_world_from_dimension(VanillaDimensionType::TheNether) args.server
.await .get_world_from_dimension(VanillaDimensionType::TheNether)
}; .await
};
let portal_delay = Self::get_portal_time(args.world, args.entity).await; let portal_delay = Self::get_portal_time(args.world, args.entity).await;
args.entity args.entity
.get_entity() .get_entity()
.try_use_portal(portal_delay, target_world, *args.position) .try_use_portal(portal_delay, target_world, *args.position)
.await; .await;
})
} }
} }

View File

@ -1,9 +1,8 @@
use crate::block::registry::BlockActionResult; use crate::block::registry::BlockActionResult;
use crate::block::{ use crate::block::{
GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, BlockFuture, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs,
UseWithItemArgs, UseWithItemArgs,
}; };
use async_trait::async_trait;
use pumpkin_data::block_properties::Axis; use pumpkin_data::block_properties::Axis;
use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::{ use pumpkin_data::{
@ -61,18 +60,40 @@ impl NoteBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for NoteBlock { impl BlockBehaviour for NoteBlock {
async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
let block_state = args.world.get_block_state(args.position).await; Box::pin(async move {
let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block); let block_state = args.world.get_block_state(args.position).await;
let powered = block_receives_redstone_power(args.world, args.position).await; let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block);
// check if powered state changed let powered = block_receives_redstone_power(args.world, args.position).await;
if note_props.powered != powered { // check if powered state changed
if powered { if note_props.powered != powered {
Self::play_note(&note_props, args.world, args.position).await; if powered {
Self::play_note(&note_props, args.world, args.position).await;
}
note_props.powered = powered;
args.world
.set_block_state(
args.position,
note_props.to_state_id(args.block),
BlockFlags::NOTIFY_ALL,
)
.await;
} }
note_props.powered = powered; })
}
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let block_state = args.world.get_block_state(args.position).await;
let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block);
let next_index = note_props.note.to_index() + 1;
// Increment and check if max
note_props.note = if next_index >= Integer0To24::variant_count() {
Integer0To24::from_index(0)
} else {
Integer0To24::from_index(next_index)
};
args.world args.world
.set_block_state( .set_block_state(
args.position, args.position,
@ -80,83 +101,78 @@ impl BlockBehaviour for NoteBlock {
BlockFlags::NOTIFY_ALL, BlockFlags::NOTIFY_ALL,
) )
.await; .await;
} Self::play_note(&note_props, args.world, args.position).await;
BlockActionResult::Success
})
} }
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { fn use_with_item<'a>(
let block_state = args.world.get_block_state(args.position).await; &'a self,
let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block); _args: UseWithItemArgs<'a>,
let next_index = note_props.note.to_index() + 1; ) -> BlockFuture<'a, BlockActionResult> {
// Increment and check if max Box::pin(async move {
note_props.note = if next_index >= Integer0To24::variant_count() { // TODO
Integer0To24::from_index(0) BlockActionResult::PassToDefaultBlockAction
} else { })
Integer0To24::from_index(next_index)
};
args.world
.set_block_state(
args.position,
note_props.to_state_id(args.block),
BlockFlags::NOTIFY_ALL,
)
.await;
Self::play_note(&note_props, args.world, args.position).await;
BlockActionResult::Success
} }
async fn use_with_item(&self, _args: UseWithItemArgs<'_>) -> BlockActionResult { fn on_synced_block_event<'a>(
// TODO &'a self,
BlockActionResult::PassToDefaultBlockAction args: OnSyncedBlockEventArgs<'a>,
) -> BlockFuture<'a, bool> {
Box::pin(async move {
let block_state = args.world.get_block_state(args.position).await;
let note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block);
let instrument = note_props.instrument;
let pitch = if is_base_block(instrument) {
// checks if can be pitched
Self::get_note_pitch(note_props.note.to_index())
} else {
1.0 // default pitch
};
// check hasCustomSound
args.world
.play_sound_raw(
convert_instrument_to_sound(instrument) as u16,
SoundCategory::Records,
&args.position.to_f64(),
3.0,
pitch,
)
.await;
true
})
} }
async fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let block_state = args.world.get_block_state(args.position).await; Box::pin(async move {
let note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block); Self::get_state_with_instrument(
let instrument = note_props.instrument;
let pitch = if is_base_block(instrument) {
// checks if can be pitched
Self::get_note_pitch(note_props.note.to_index())
} else {
1.0 // default pitch
};
// check hasCustomSound
args.world
.play_sound_raw(
convert_instrument_to_sound(instrument) as u16,
SoundCategory::Records,
&args.position.to_f64(),
3.0,
pitch,
)
.await;
true
}
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
Self::get_state_with_instrument(
args.world,
args.position,
Block::NOTE_BLOCK.default_state.id,
args.block,
)
.await
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
if args.direction.to_axis() == Axis::Y {
return Self::get_state_with_instrument(
args.world, args.world,
args.position, args.position,
args.state_id, Block::NOTE_BLOCK.default_state.id,
args.block, args.block,
) )
.await; .await
} })
args.state_id }
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
if args.direction.to_axis() == Axis::Y {
return Self::get_state_with_instrument(
args.world,
args.position,
args.state_id,
args.block,
)
.await;
}
args.state_id
})
} }
} }

View File

@ -1,6 +1,5 @@
use std::{collections::HashMap, sync::Arc}; use std::{collections::HashMap, sync::Arc};
use async_trait::async_trait;
use pumpkin_data::{ use pumpkin_data::{
Block, BlockDirection, BlockState, FacingExt, Block, BlockDirection, BlockState, FacingExt,
block_properties::{ block_properties::{
@ -17,11 +16,8 @@ use pumpkin_world::{
use crate::{ use crate::{
block::{ block::{
blocks::redstone::is_emitting_redstone_power, BlockBehaviour, BlockFuture, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs,
{ OnSyncedBlockEventArgs, PlacedArgs, blocks::redstone::is_emitting_redstone_power,
BlockBehaviour, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs,
OnSyncedBlockEventArgs, PlacedArgs,
},
}, },
world::World, world::World,
}; };
@ -84,162 +80,172 @@ impl PistonBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for PistonBlock { impl BlockBehaviour for PistonBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
let mut props = PistonProps::default(args.block); Box::pin(async move {
props.extended = false; let mut props = PistonProps::default(args.block);
props.facing = args.player.living_entity.entity.get_facing().opposite(); props.extended = false;
props.to_state_id(args.block) props.facing = args.player.living_entity.entity.get_facing().opposite();
props.to_state_id(args.block)
})
} }
async fn placed(&self, args: PlacedArgs<'_>) { fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
if args.old_state_id == args.state_id { Box::pin(async move {
return; if args.old_state_id == args.state_id {
} return;
try_move(args.world, args.block, args.position).await; }
try_move(args.world, args.block, args.position).await;
})
} }
async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
try_move(args.world, args.block, args.position).await; Box::pin(async move {
try_move(args.world, args.block, args.position).await;
})
} }
#[expect(clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
async fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool { fn on_synced_block_event<'a>(
let (block, world, pos, r#type, data) = ( &'a self,
args.block, args: OnSyncedBlockEventArgs<'a>,
args.world, ) -> BlockFuture<'a, bool> {
args.position, Box::pin(async move {
args.r#type, let (block, world, pos, r#type, data) = (
args.data, args.block,
); args.world,
args.position,
args.r#type,
args.data,
);
let state = world.get_block_state(pos).await; let state = world.get_block_state(pos).await;
let mut props = PistonProps::from_state_id(state.id, block); let mut props = PistonProps::from_state_id(state.id, block);
let dir = props.facing.to_block_direction(); let dir = props.facing.to_block_direction();
// I don't think this is optimal ? // I don't think this is optimal ?
let sticky = block == &Block::STICKY_PISTON; let sticky = block == &Block::STICKY_PISTON;
let should_extend = should_extend(world, pos, dir).await; let should_extend = should_extend(world, pos, dir).await;
if should_extend && (r#type == 1 || r#type == 2) { if should_extend && (r#type == 1 || r#type == 2) {
props.extended = true; props.extended = true;
world world
.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS) .set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS)
.await; .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;
}
// Extend Piston
if r#type == 0 {
if !move_piston(world, dir, pos, true, sticky).await {
return false; return false;
} }
props.extended = true;
// This may prevents when something happens in the one tick before this function got called
if !should_extend && r#type == 0 {
return false;
}
// Extend Piston
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;
}
// Reduce Piston
let extended_pos = pos.offset(dir.to_offset());
if let Some(block_entity) = world.get_block_entity(&extended_pos).await {
let piston = block_entity
.as_any()
.downcast_ref::<PistonBlockEntity>()
.unwrap();
piston.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
};
world world
.set_block_state( .set_block_state(
pos, pos,
props.to_state_id(block), props.to_state_id(&Block::MOVING_PISTON),
BlockFlags::NOTIFY_ALL | BlockFlags::MOVED, BlockFlags::FORCE_STATE,
) )
.await; .await;
return true;
}
// Reduce Piston
let extended_pos = pos.offset(dir.to_offset()); let mut props = PistonProps::default(block);
props.facing = BlockDirection::by_index((data & 7) as usize)
.unwrap()
.to_facing();
if let Some(block_entity) = world.get_block_entity(&extended_pos).await {
let piston = block_entity
.as_any()
.downcast_ref::<PistonBlockEntity>()
.unwrap();
piston.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
};
world
.set_block_state(
pos,
props.to_state_id(&Block::MOVING_PISTON),
BlockFlags::FORCE_STATE,
)
.await;
let mut props = PistonProps::default(block);
props.facing = BlockDirection::by_index((data & 7) as usize)
.unwrap()
.to_facing();
world
.add_block_entity(Arc::new(PistonBlockEntity {
position: *pos,
facing: dir,
pushed_block_state: BlockState::from_id(props.to_state_id(block)),
current_progress: 0.0.into(),
last_progress: 0.0.into(),
extending: false,
source: true,
}))
.await;
world.update_neighbors(pos, None).await;
if sticky {
let pos = pos.offset_dir(dir.to_offset(), 2);
let (block, state) = world.get_block_and_state(&pos).await;
let mut bl2 = false;
if block == &Block::MOVING_PISTON
&& let Some(entity) = world.get_block_entity(&pos).await
{
let piston = entity.as_any().downcast_ref::<PistonBlockEntity>().unwrap();
if piston.facing == dir && piston.extending {
piston.finish(world.clone()).await;
bl2 = true;
}
}
if !bl2 {
if r#type == 1
&& !state.is_air()
&& Self::is_movable(block, state, dir, false, dir)
&& (state.piston_behavior == PistonBehavior::Normal
|| block == &Block::PISTON
|| block == &Block::STICKY_PISTON)
{
move_piston(world, dir, &pos, false, sticky).await;
} else {
// remove
world
.set_block_state(
&extended_pos,
Block::AIR.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
}
}
} else {
// remove
world world
.set_block_state( .add_block_entity(Arc::new(PistonBlockEntity {
&extended_pos, position: *pos,
Block::AIR.default_state.id, facing: dir,
BlockFlags::NOTIFY_ALL, pushed_block_state: BlockState::from_id(props.to_state_id(block)),
) current_progress: 0.0.into(),
last_progress: 0.0.into(),
extending: false,
source: true,
}))
.await; .await;
}
return true; world.update_neighbors(pos, None).await;
if sticky {
let pos = pos.offset_dir(dir.to_offset(), 2);
let (block, state) = world.get_block_and_state(&pos).await;
let mut bl2 = false;
if block == &Block::MOVING_PISTON
&& let Some(entity) = world.get_block_entity(&pos).await
{
let piston = entity.as_any().downcast_ref::<PistonBlockEntity>().unwrap();
if piston.facing == dir && piston.extending {
piston.finish(world.clone()).await;
bl2 = true;
}
}
if !bl2 {
if r#type == 1
&& !state.is_air()
&& Self::is_movable(block, state, dir, false, dir)
&& (state.piston_behavior == PistonBehavior::Normal
|| block == &Block::PISTON
|| block == &Block::STICKY_PISTON)
{
move_piston(world, dir, &pos, false, sticky).await;
} else {
// remove
world
.set_block_state(
&extended_pos,
Block::AIR.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
}
}
} else {
// remove
world
.set_block_state(
&extended_pos,
Block::AIR.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
}
true
})
} }
} }

View File

@ -1,11 +1,10 @@
use async_trait::async_trait;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::{Block, FacingExt}; use pumpkin_data::{Block, FacingExt};
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;
use crate::block::BlockBehaviour;
use crate::block::BrokenArgs; use crate::block::BrokenArgs;
use crate::block::{BlockBehaviour, BlockFuture};
use super::piston::PistonProps; use super::piston::PistonProps;
@ -14,22 +13,23 @@ pub(crate) type MovingPistonProps = pumpkin_data::block_properties::MovingPiston
#[pumpkin_block("minecraft:moving_piston")] #[pumpkin_block("minecraft:moving_piston")]
pub struct PistonExtensionBlock; pub struct PistonExtensionBlock;
#[async_trait]
impl BlockBehaviour for PistonExtensionBlock { impl BlockBehaviour for PistonExtensionBlock {
async fn broken(&self, args: BrokenArgs<'_>) { fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
let props = MovingPistonProps::from_state_id(args.state.id, &Block::MOVING_PISTON); Box::pin(async move {
let pos = args let props = MovingPistonProps::from_state_id(args.state.id, &Block::MOVING_PISTON);
.position let pos = args
.offset(props.facing.opposite().to_block_direction().to_offset()); .position
let (new_block, new_state) = args.world.get_block_and_state_id(&pos).await; .offset(props.facing.opposite().to_block_direction().to_offset());
if &Block::PISTON == new_block || &Block::STICKY_PISTON == new_block { let (new_block, new_state) = args.world.get_block_and_state_id(&pos).await;
let props = PistonProps::from_state_id(new_state, new_block); if &Block::PISTON == new_block || &Block::STICKY_PISTON == new_block {
if props.extended { let props = PistonProps::from_state_id(new_state, new_block);
// TODO: use player if props.extended {
args.world // TODO: use player
.break_block(&pos, None, BlockFlags::SKIP_DROPS) args.world
.await; .break_block(&pos, None, BlockFlags::SKIP_DROPS)
.await;
}
} }
} })
} }
} }

View File

@ -1,11 +1,10 @@
use async_trait::async_trait;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::{Block, FacingExt}; use pumpkin_data::{Block, FacingExt};
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;
use crate::block::BlockBehaviour;
use crate::block::BrokenArgs; use crate::block::BrokenArgs;
use crate::block::{BlockBehaviour, BlockFuture};
use super::piston::PistonProps; use super::piston::PistonProps;
@ -14,22 +13,23 @@ pub(crate) type PistonHeadProperties = pumpkin_data::block_properties::PistonHea
#[pumpkin_block("minecraft:piston_head")] #[pumpkin_block("minecraft:piston_head")]
pub struct PistonHeadBlock; pub struct PistonHeadBlock;
#[async_trait]
impl BlockBehaviour for PistonHeadBlock { impl BlockBehaviour for PistonHeadBlock {
async fn broken(&self, args: BrokenArgs<'_>) { fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
let props = PistonHeadProperties::from_state_id(args.state.id, &Block::PISTON_HEAD); Box::pin(async move {
let pos = args let props = PistonHeadProperties::from_state_id(args.state.id, &Block::PISTON_HEAD);
.position let pos = args
.offset(props.facing.opposite().to_block_direction().to_offset()); .position
let (new_block, new_state) = args.world.get_block_and_state_id(&pos).await; .offset(props.facing.opposite().to_block_direction().to_offset());
if &Block::PISTON == new_block || &Block::STICKY_PISTON == new_block { let (new_block, new_state) = args.world.get_block_and_state_id(&pos).await;
let props = PistonProps::from_state_id(new_state, new_block); if &Block::PISTON == new_block || &Block::STICKY_PISTON == new_block {
if props.extended { let props = PistonProps::from_state_id(new_state, new_block);
// TODO: use player if props.extended {
args.world // TODO: use player
.break_block(&pos, None, BlockFlags::SKIP_DROPS) args.world
.await; .break_block(&pos, None, BlockFlags::SKIP_DROPS)
.await;
}
} }
} })
} }
} }

View File

@ -1,9 +1,8 @@
use async_trait::async_trait;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use crate::block::{ use crate::block::{
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
blocks::plant::PlantBlockBase, blocks::plant::PlantBlockBase,
}; };
@ -19,23 +18,26 @@ impl BlockMetadata for BushBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for BushBlock { impl BlockBehaviour for BushBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
<Self as PlantBlockBase>::get_state_for_neighbor_update( Box::pin(async move {
self, <Self as PlantBlockBase>::get_state_for_neighbor_update(
args.world, self,
args.position, args.world,
args.state_id, args.position,
) args.state_id,
.await )
.await
})
} }
} }

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_data::block_properties::{ use pumpkin_data::block_properties::{
BlockProperties, EnumVariants, Integer0To3, NetherWartLikeProperties, BlockProperties, EnumVariants, Integer0To3, NetherWartLikeProperties,
@ -9,37 +8,48 @@ use rand::Rng;
use crate::block::blocks::plant::PlantBlockBase; use crate::block::blocks::plant::PlantBlockBase;
use crate::block::blocks::plant::crop::CropBlockBase; use crate::block::blocks::plant::crop::CropBlockBase;
use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs}; use crate::block::{
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs,
};
type BeetrootProperties = NetherWartLikeProperties; type BeetrootProperties = NetherWartLikeProperties;
#[pumpkin_block("minecraft:beetroots")] #[pumpkin_block("minecraft:beetroots")]
pub struct BeetrootBlock; pub struct BeetrootBlock;
#[async_trait]
impl BlockBehaviour for BeetrootBlock { impl BlockBehaviour for BeetrootBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
<Self as CropBlockBase>::can_plant_on_top(self, args.block_accessor, &args.position.down()) Box::pin(async move {
<Self as CropBlockBase>::can_plant_on_top(
self,
args.block_accessor,
&args.position.down(),
)
.await .await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
<Self as PlantBlockBase>::get_state_for_neighbor_update( Box::pin(async move {
self, <Self as PlantBlockBase>::get_state_for_neighbor_update(
args.world, self,
args.position, args.world,
args.state_id, args.position,
) args.state_id,
.await )
.await
})
} }
async fn random_tick(&self, args: RandomTickArgs<'_>) { fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
if rand::rng().random_range(0..2) != 0 { Box::pin(async move {
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await; if rand::rng().random_range(0..2) != 0 {
} <Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
}
})
} }
} }

View File

@ -1,36 +1,46 @@
use async_trait::async_trait;
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use crate::block::blocks::plant::PlantBlockBase; use crate::block::blocks::plant::PlantBlockBase;
use crate::block::blocks::plant::crop::CropBlockBase; use crate::block::blocks::plant::crop::CropBlockBase;
use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs}; use crate::block::{
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs,
};
#[pumpkin_block("minecraft:carrots")] #[pumpkin_block("minecraft:carrots")]
pub struct CarrotBlock; pub struct CarrotBlock;
#[async_trait]
impl BlockBehaviour for CarrotBlock { impl BlockBehaviour for CarrotBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
<Self as CropBlockBase>::can_plant_on_top(self, args.block_accessor, &args.position.down()) Box::pin(async move {
<Self as CropBlockBase>::can_plant_on_top(
self,
args.block_accessor,
&args.position.down(),
)
.await .await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
<Self as PlantBlockBase>::get_state_for_neighbor_update( Box::pin(async move {
self, <Self as PlantBlockBase>::get_state_for_neighbor_update(
args.world, self,
args.position, args.world,
args.state_id, args.position,
) args.state_id,
.await )
.await
})
} }
async fn random_tick(&self, args: RandomTickArgs<'_>) { fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await; Box::pin(async move {
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
})
} }
} }

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::{ use pumpkin_data::{
Block, Block,
block_properties::{ block_properties::{
@ -9,7 +8,7 @@ use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{BlockStateId, world::BlockAccessor}; use pumpkin_world::{BlockStateId, world::BlockAccessor};
use crate::block::{ use crate::block::{
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
blocks::plant::PlantBlockBase, blocks::plant::PlantBlockBase,
}; };
@ -49,31 +48,34 @@ impl AttachedStemBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for AttachedStemBlock { impl BlockBehaviour for AttachedStemBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
let props = AttachedStemProperties::from_state_id(args.state_id, args.block); Box::pin(async move {
if args.direction.to_horizontal_facing() == Some(props.facing) let props = AttachedStemProperties::from_state_id(args.state_id, args.block);
&& args.neighbor_state_id != Self::get_gourd(args.block).default_state.id if args.direction.to_horizontal_facing() == Some(props.facing)
{ && args.neighbor_state_id != Self::get_gourd(args.block).default_state.id
let mut props = StemProperties::default(Self::get_stem(args.block)); {
props.age = Integer0To7::from_index(7); let mut props = StemProperties::default(Self::get_stem(args.block));
return props.to_state_id(Self::get_stem(args.block)); props.age = Integer0To7::from_index(7);
} return props.to_state_id(Self::get_stem(args.block));
<Self as PlantBlockBase>::get_state_for_neighbor_update( }
self, <Self as PlantBlockBase>::get_state_for_neighbor_update(
args.world, self,
args.position, args.world,
args.state_id, args.position,
) args.state_id,
.await )
.await
})
} }
} }

View File

@ -1,8 +1,8 @@
use crate::block::{ use crate::block::{
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs, BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
RandomTickArgs,
blocks::plant::{PlantBlockBase, crop::get_available_moisture}, blocks::plant::{PlantBlockBase, crop::get_available_moisture},
}; };
use async_trait::async_trait;
use pumpkin_data::{ use pumpkin_data::{
Block, BlockDirection, Block, BlockDirection,
block_properties::{ block_properties::{
@ -63,66 +63,75 @@ impl StemBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for StemBlock { impl BlockBehaviour for StemBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
<Self as PlantBlockBase>::get_state_for_neighbor_update( Box::pin(async move {
self, <Self as PlantBlockBase>::get_state_for_neighbor_update(
args.world, self,
args.position, args.world,
args.state_id, args.position,
) args.state_id,
.await )
.await
})
} }
async fn random_tick(&self, args: RandomTickArgs<'_>) { fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
// TODO add light level check Box::pin(async move {
let f: f32 = get_available_moisture(args.world, args.position, args.block).await; // TODO add light level check
if rand::rng().random_range(0..=(25.0 / f).floor() as i32) == 0 { let f: f32 = get_available_moisture(args.world, args.position, args.block).await;
let (block, state) = args.world.get_block_and_state_id(args.position).await; if rand::rng().random_range(0..=(25.0 / f).floor() as i32) == 0 {
let props = StemProperties::from_state_id(state, block); let (block, state) = args.world.get_block_and_state_id(args.position).await;
let age = i32::from(props.age.to_index()); let props = StemProperties::from_state_id(state, block);
if age < 7 { let age = i32::from(props.age.to_index());
args.world if age < 7 {
.set_block_state(
args.position,
Self::state_with_age(block, state, age + 1),
BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
} else {
let dir = BlockDirection::random_horizontal(&mut RandomGenerator::Xoroshiro(
Xoroshiro::from_seed(rand::rng().random()),
));
let plant_block_pos = args.position.offset(dir.to_offset());
let plant_block_state = args.world.get_block_state(&plant_block_pos).await;
let under_block: &Block = args.world.get_block(&plant_block_pos.down()).await;
if plant_block_state.is_air()
&& (under_block == &Block::FARMLAND
|| under_block.has_tag(&tag::Block::MINECRAFT_DIRT))
{
let attached_stem = Self::get_attached_stem(dir, block);
let gourd = Self::get_gourd(block);
args.world args.world
.set_block_state( .set_block_state(
&plant_block_pos, args.position,
gourd.default_state.id, Self::state_with_age(block, state, age + 1),
BlockFlags::NOTIFY_NEIGHBORS, BlockFlags::NOTIFY_NEIGHBORS,
) )
.await; .await;
args.world } else {
.set_block_state(args.position, attached_stem, BlockFlags::NOTIFY_NEIGHBORS) let dir = BlockDirection::random_horizontal(&mut RandomGenerator::Xoroshiro(
.await; Xoroshiro::from_seed(rand::rng().random()),
));
let plant_block_pos = args.position.offset(dir.to_offset());
let plant_block_state = args.world.get_block_state(&plant_block_pos).await;
let under_block: &Block = args.world.get_block(&plant_block_pos.down()).await;
if plant_block_state.is_air()
&& (under_block == &Block::FARMLAND
|| under_block.has_tag(&tag::Block::MINECRAFT_DIRT))
{
let attached_stem = Self::get_attached_stem(dir, block);
let gourd = Self::get_gourd(block);
args.world
.set_block_state(
&plant_block_pos,
gourd.default_state.id,
BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
args.world
.set_block_state(
args.position,
attached_stem,
BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
}
} }
} }
} })
} }
} }

View File

@ -1,36 +1,46 @@
use async_trait::async_trait;
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use crate::block::blocks::plant::PlantBlockBase; use crate::block::blocks::plant::PlantBlockBase;
use crate::block::blocks::plant::crop::CropBlockBase; use crate::block::blocks::plant::crop::CropBlockBase;
use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs}; use crate::block::{
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs,
};
#[pumpkin_block("minecraft:potatoes")] #[pumpkin_block("minecraft:potatoes")]
pub struct PotatoBlock; pub struct PotatoBlock;
#[async_trait]
impl BlockBehaviour for PotatoBlock { impl BlockBehaviour for PotatoBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
<Self as CropBlockBase>::can_plant_on_top(self, args.block_accessor, &args.position.down()) Box::pin(async move {
<Self as CropBlockBase>::can_plant_on_top(
self,
args.block_accessor,
&args.position.down(),
)
.await .await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
<Self as PlantBlockBase>::get_state_for_neighbor_update( Box::pin(async move {
self, <Self as PlantBlockBase>::get_state_for_neighbor_update(
args.world, self,
args.position, args.world,
args.state_id, args.position,
) args.state_id,
.await )
.await
})
} }
async fn random_tick(&self, args: RandomTickArgs<'_>) { fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await; Box::pin(async move {
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
})
} }
} }

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_data::block_properties::{ use pumpkin_data::block_properties::{
BlockProperties, EnumVariants, Integer0To1, TorchflowerCropLikeProperties, BlockProperties, EnumVariants, Integer0To1, TorchflowerCropLikeProperties,
@ -9,37 +8,48 @@ use rand::Rng;
use crate::block::blocks::plant::PlantBlockBase; use crate::block::blocks::plant::PlantBlockBase;
use crate::block::blocks::plant::crop::CropBlockBase; use crate::block::blocks::plant::crop::CropBlockBase;
use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs}; use crate::block::{
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs,
};
type TorchFlowerProperties = TorchflowerCropLikeProperties; type TorchFlowerProperties = TorchflowerCropLikeProperties;
#[pumpkin_block("minecraft:torchflower_crop")] #[pumpkin_block("minecraft:torchflower_crop")]
pub struct TorchFlowerBlock; pub struct TorchFlowerBlock;
#[async_trait]
impl BlockBehaviour for TorchFlowerBlock { impl BlockBehaviour for TorchFlowerBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
<Self as CropBlockBase>::can_plant_on_top(self, args.block_accessor, &args.position.down()) Box::pin(async move {
<Self as CropBlockBase>::can_plant_on_top(
self,
args.block_accessor,
&args.position.down(),
)
.await .await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
<Self as PlantBlockBase>::get_state_for_neighbor_update( Box::pin(async move {
self, <Self as PlantBlockBase>::get_state_for_neighbor_update(
args.world, self,
args.position, args.world,
args.state_id, args.position,
) args.state_id,
.await )
.await
})
} }
async fn random_tick(&self, args: RandomTickArgs<'_>) { fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
if rand::rng().random_range(0..2) != 0 { Box::pin(async move {
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await; if rand::rng().random_range(0..2) != 0 {
} <Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
}
})
} }
} }

View File

@ -1,36 +1,46 @@
use async_trait::async_trait;
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use crate::block::blocks::plant::PlantBlockBase; use crate::block::blocks::plant::PlantBlockBase;
use crate::block::blocks::plant::crop::CropBlockBase; use crate::block::blocks::plant::crop::CropBlockBase;
use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs}; use crate::block::{
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs,
};
#[pumpkin_block("minecraft:wheat")] #[pumpkin_block("minecraft:wheat")]
pub struct WheatBlock; pub struct WheatBlock;
#[async_trait]
impl BlockBehaviour for WheatBlock { impl BlockBehaviour for WheatBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
<Self as CropBlockBase>::can_plant_on_top(self, args.block_accessor, &args.position.down()) Box::pin(async move {
<Self as CropBlockBase>::can_plant_on_top(
self,
args.block_accessor,
&args.position.down(),
)
.await .await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
<Self as PlantBlockBase>::get_state_for_neighbor_update( Box::pin(async move {
self, <Self as PlantBlockBase>::get_state_for_neighbor_update(
args.world, self,
args.position, args.world,
args.state_id, args.position,
) args.state_id,
.await )
.await
})
} }
async fn random_tick(&self, args: RandomTickArgs<'_>) { fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await; Box::pin(async move {
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
})
} }
} }

View File

@ -1,11 +1,10 @@
use async_trait::async_trait;
use pumpkin_data::tag; use pumpkin_data::tag;
use pumpkin_data::tag::Taggable; use pumpkin_data::tag::Taggable;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{BlockStateId, world::BlockAccessor}; use pumpkin_world::{BlockStateId, world::BlockAccessor};
use crate::block::{ use crate::block::{
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
blocks::plant::PlantBlockBase, blocks::plant::PlantBlockBase,
}; };
@ -21,23 +20,26 @@ impl BlockMetadata for DryVegetationBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for DryVegetationBlock { impl BlockBehaviour for DryVegetationBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
<Self as PlantBlockBase>::get_state_for_neighbor_update( Box::pin(async move {
self, <Self as PlantBlockBase>::get_state_for_neighbor_update(
args.world, self,
args.position, args.world,
args.state_id, args.position,
) args.state_id,
.await )
.await
})
} }
} }

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_data::tag::{RegistryKey, get_tag_values};
use pumpkin_registry::VanillaDimensionType; use pumpkin_registry::VanillaDimensionType;
@ -6,7 +5,9 @@ use pumpkin_world::BlockStateId;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;
use crate::block::blocks::plant::PlantBlockBase; use crate::block::blocks::plant::PlantBlockBase;
use crate::block::{BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs}; use crate::block::{
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
};
use crate::block::RandomTickArgs; use crate::block::RandomTickArgs;
@ -22,57 +23,62 @@ impl BlockMetadata for FlowerBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for FlowerBlock { impl BlockBehaviour for FlowerBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
<Self as PlantBlockBase>::get_state_for_neighbor_update( Box::pin(async move {
self, <Self as PlantBlockBase>::get_state_for_neighbor_update(
args.world, self,
args.position, args.world,
args.state_id, args.position,
) args.state_id,
.await )
.await
})
} }
async fn random_tick(&self, args: RandomTickArgs<'_>) { fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
//TODO add trail particule Box::pin(async move {
if (args //TODO add trail particule
.world if (args
.dimension_type
.eq(&VanillaDimensionType::Overworld)
|| args
.world .world
.dimension_type .dimension_type
.eq(&VanillaDimensionType::OverworldCaves)) .eq(&VanillaDimensionType::Overworld)
&& args.block.eq(&Block::CLOSED_EYEBLOSSOM) || args
&& args.world.level_time.lock().await.time_of_day % 24000 > 14500 .world
{ .dimension_type
args.world .eq(&VanillaDimensionType::OverworldCaves))
.set_block_state( && args.block.eq(&Block::CLOSED_EYEBLOSSOM)
args.position, && args.world.level_time.lock().await.time_of_day % 24000 > 14500
Block::OPEN_EYEBLOSSOM.default_state.id, {
BlockFlags::NOTIFY_ALL, args.world
) .set_block_state(
.await; args.position,
} Block::OPEN_EYEBLOSSOM.default_state.id,
if args.block.eq(&Block::OPEN_EYEBLOSSOM) BlockFlags::NOTIFY_ALL,
&& args.world.level_time.lock().await.time_of_day % 24000 <= 14500 )
{ .await;
args.world }
.set_block_state( if args.block.eq(&Block::OPEN_EYEBLOSSOM)
args.position, && args.world.level_time.lock().await.time_of_day % 24000 <= 14500
Block::CLOSED_EYEBLOSSOM.default_state.id, {
BlockFlags::NOTIFY_ALL, args.world
) .set_block_state(
.await; args.position,
} Block::CLOSED_EYEBLOSSOM.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
}
})
} }
} }

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::tag::Taggable; use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, tag}; use pumpkin_data::{Block, tag};
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
@ -6,8 +5,8 @@ use pumpkin_world::BlockStateId;
use crate::block::blocks::plant::PlantBlockBase; use crate::block::blocks::plant::PlantBlockBase;
use crate::block::{ use crate::block::{
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs, BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, CanUpdateAtArgs,
OnPlaceArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
}; };
use super::segmented::Segmented; use super::segmented::Segmented;
@ -26,32 +25,35 @@ impl BlockMetadata for FlowerbedBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for FlowerbedBlock { impl BlockBehaviour for FlowerbedBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
let block_below = args.block_accessor.get_block(&args.position.down()).await; Box::pin(async move {
block_below.has_tag(&tag::Block::MINECRAFT_DIRT) || block_below == &Block::FARMLAND let block_below = args.block_accessor.get_block(&args.position.down()).await;
block_below.has_tag(&tag::Block::MINECRAFT_DIRT) || block_below == &Block::FARMLAND
})
} }
async fn can_update_at(&self, args: CanUpdateAtArgs<'_>) -> bool { fn can_update_at<'a>(&'a self, args: CanUpdateAtArgs<'a>) -> BlockFuture<'a, bool> {
Segmented::can_update_at(self, args).await Box::pin(async move { Segmented::can_update_at(self, args).await })
} }
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Segmented::on_place(self, args).await Box::pin(async move { Segmented::on_place(self, args).await })
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
<Self as PlantBlockBase>::get_state_for_neighbor_update( Box::pin(async move {
self, <Self as PlantBlockBase>::get_state_for_neighbor_update(
args.world, self,
args.position, args.world,
args.state_id, args.position,
) args.state_id,
.await )
.await
})
} }
} }

View File

@ -1,10 +1,9 @@
use async_trait::async_trait;
use pumpkin_data::{Block, BlockDirection}; use pumpkin_data::{Block, BlockDirection};
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use crate::block::{ use crate::block::{
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs, BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, CanUpdateAtArgs,
OnPlaceArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
}; };
use super::segmented::Segmented; use super::segmented::Segmented;
@ -23,35 +22,38 @@ impl BlockMetadata for LeafLitterBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for LeafLitterBlock { impl BlockBehaviour for LeafLitterBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
let block_below = args Box::pin(async move {
.block_accessor let block_below = args
.get_block_state(&args.position.down()) .block_accessor
.await; .get_block_state(&args.position.down())
block_below.is_side_solid(BlockDirection::Up) .await;
block_below.is_side_solid(BlockDirection::Up)
})
} }
async fn can_update_at(&self, args: CanUpdateAtArgs<'_>) -> bool { fn can_update_at<'a>(&'a self, args: CanUpdateAtArgs<'a>) -> BlockFuture<'a, bool> {
Segmented::can_update_at(self, args).await Box::pin(async move { Segmented::can_update_at(self, args).await })
} }
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Segmented::on_place(self, args).await Box::pin(async move { Segmented::on_place(self, args).await })
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
if args.direction == BlockDirection::Down { Box::pin(async move {
let block_below_state = args.world.get_block_state(&args.position.down()).await; if args.direction == BlockDirection::Down {
if !block_below_state.is_side_solid(BlockDirection::Up) { let block_below_state = args.world.get_block_state(&args.position.down()).await;
return Block::AIR.default_state.id; if !block_below_state.is_side_solid(BlockDirection::Up) {
return Block::AIR.default_state.id;
}
} }
} args.state_id
args.state_id })
} }
} }

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
@ -7,45 +6,50 @@ use pumpkin_world::{
world::{BlockAccessor, BlockFlags}, world::{BlockAccessor, BlockFlags},
}; };
use crate::block::{GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase}; use crate::block::{BlockFuture, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase};
use crate::block::{BlockBehaviour, CanPlaceAtArgs, OnEntityCollisionArgs}; use crate::block::{BlockBehaviour, CanPlaceAtArgs, OnEntityCollisionArgs};
#[pumpkin_block("minecraft:lily_pad")] #[pumpkin_block("minecraft:lily_pad")]
pub struct LilyPadBlock; pub struct LilyPadBlock;
#[async_trait]
impl BlockBehaviour for LilyPadBlock { impl BlockBehaviour for LilyPadBlock {
async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
// Proberbly not the best solution, but works Box::pin(async move {
if args // Proberbly not the best solution, but works
.entity if args
.get_entity() .entity
.entity_type .get_entity()
.resource_name .entity_type
.ends_with("_boat") .resource_name
{ .ends_with("_boat")
args.world {
.break_block(args.position, None, BlockFlags::empty()) args.world
.await; .break_block(args.position, None, BlockFlags::empty())
} .await;
}
})
} }
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
<Self as PlantBlockBase>::get_state_for_neighbor_update( Box::pin(async move {
self, <Self as PlantBlockBase>::get_state_for_neighbor_update(
args.world, self,
args.position, args.world,
args.state_id, args.position,
) args.state_id,
.await )
.await
})
} }
} }

View File

@ -1,11 +1,10 @@
use async_trait::async_trait;
use pumpkin_data::tag; use pumpkin_data::tag;
use pumpkin_data::tag::Taggable; use pumpkin_data::tag::Taggable;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{BlockStateId, world::BlockAccessor}; use pumpkin_world::{BlockStateId, world::BlockAccessor};
use crate::block::{ use crate::block::{
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
blocks::plant::PlantBlockBase, blocks::plant::PlantBlockBase,
}; };
@ -21,23 +20,26 @@ impl BlockMetadata for MushroomPlantBlock {
} }
} }
#[async_trait]
impl BlockBehaviour for MushroomPlantBlock { impl BlockBehaviour for MushroomPlantBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
<Self as PlantBlockBase>::get_state_for_neighbor_update( Box::pin(async move {
self, <Self as PlantBlockBase>::get_state_for_neighbor_update(
args.world, self,
args.position, args.world,
args.state_id, args.position,
) args.state_id,
.await )
.await
})
} }
} }

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
@ -6,28 +5,31 @@ use pumpkin_world::BlockStateId;
use pumpkin_world::world::BlockAccessor; use pumpkin_world::world::BlockAccessor;
use crate::block::blocks::plant::PlantBlockBase; use crate::block::blocks::plant::PlantBlockBase;
use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs}; use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs};
#[pumpkin_block("minecraft:nether_wart")] #[pumpkin_block("minecraft:nether_wart")]
pub struct NetherWartBlock; pub struct NetherWartBlock;
#[async_trait]
impl BlockBehaviour for NetherWartBlock { impl BlockBehaviour for NetherWartBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
} }
async fn get_state_for_neighbor_update( fn get_state_for_neighbor_update<'a>(
&self, &'a self,
args: GetStateForNeighborUpdateArgs<'_>, args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockStateId { ) -> BlockFuture<'a, BlockStateId> {
<Self as PlantBlockBase>::get_state_for_neighbor_update( Box::pin(async move {
self, <Self as PlantBlockBase>::get_state_for_neighbor_update(
args.world, self,
args.position, args.world,
args.state_id, args.position,
) args.state_id,
.await )
.await
})
} }
} }

Some files were not shown because too many files have changed in this diff Show More