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

View File

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

View File

@ -1,4 +1,5 @@
use std::any::Any;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicU8, Ordering};
@ -6,7 +7,8 @@ use super::recipes::{RecipeFinderScreenHandler, RecipeInputInventory};
use crate::crafting::crafting_inventory::CraftingInventory;
use crate::player::player_inventory::PlayerInventory;
use crate::screen_handler::{
InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerListener,
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture,
ScreenHandlerListener,
};
use crate::slot::{NormalSlot, Slot};
use async_trait::async_trait;
@ -406,48 +408,54 @@ impl Slot for ResultSlot {
}
}
#[async_trait]
impl ScreenHandlerListener for ResultSlot {
async fn on_slot_update(
&self,
screen_handler: &ScreenHandlerBehaviour,
fn on_slot_update<'a>(
&'a self,
screen_handler: &'a ScreenHandlerBehaviour,
slot: u8,
_stack: ItemStack,
) {
if (0..=(self.inventory.get_width() * self.inventory.get_height()))
.contains(&(slot as usize))
{
let result = self.refill_output().await;
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
if (0..=(self.inventory.get_width() * self.inventory.get_height()))
.contains(&(slot as usize))
{
let result = self.refill_output().await;
let next_revision = screen_handler.next_revision();
if let Some(sync_handler) = screen_handler.sync_handler.as_ref() {
sync_handler
.update_slot(screen_handler, 0, &result, next_revision)
.await;
let next_revision = screen_handler.next_revision();
if let Some(sync_handler) = screen_handler.sync_handler.as_ref() {
sync_handler
.update_slot(screen_handler, 0, &result, next_revision)
.await;
}
}
}
})
}
}
// AbstractCraftingScreenHandler.java
#[async_trait]
pub trait CraftingScreenHandler<I: RecipeInputInventory>:
RecipeFinderScreenHandler + ScreenHandler
{
async fn add_recipe_slots(&mut self, crafing_inventory: Arc<dyn RecipeInputInventory>) {
let result_slot = Arc::new(ResultSlot::new(crafing_inventory.clone()));
self.add_slot(result_slot.clone());
fn add_recipe_slots<'a>(
&'a mut self,
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 height = crafing_inventory.get_height();
for i in 0..width {
for j in 0..height {
let input_slot = NormalSlot::new(crafing_inventory.clone(), j + i * width);
self.add_slot(Arc::new(input_slot));
let width = crafing_inventory.get_width();
let height = crafing_inventory.get_height();
for i in 0..width {
for j in 0..height {
// Assuming j + i * width is the correct slot index calculation
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 {}
#[async_trait]
impl ScreenHandler for CraftingTableScreenHandler {
async fn on_closed(&mut self, player: &dyn InventoryPlayer) {
self.default_on_closed(player).await;
//TODO: this.craftingResultInventory.clear();
self.drop_inventory(player, self.crafting_inventory.clone())
.await;
fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
Box::pin(async move {
self.default_on_closed(player).await;
//TODO: this.craftingResultInventory.clear();
self.drop_inventory(player, self.crafting_inventory.clone())
.await;
})
}
fn as_any(&self) -> &dyn Any {
@ -502,77 +511,83 @@ impl ScreenHandler for CraftingTableScreenHandler {
&mut self.behaviour
}
async fn quick_move(&mut self, player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack {
let slot = self.get_behaviour().slots[slot_index as usize].clone();
fn quick_move<'a>(
&'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 {
let slot_stack = slot.get_stack().await;
let mut slot_stack = slot_stack.lock().await;
let stack_prev = slot_stack.clone();
if slot.has_stack().await {
let slot_stack = slot.get_stack().await;
let mut slot_stack = slot_stack.lock().await;
let stack_prev = slot_stack.clone();
if slot_index == 0 {
// From crafting result slot - move to player inventory (slots 10-46)
if !self.insert_item(&mut slot_stack, 10, 46, true).await {
return ItemStack::EMPTY.clone();
}
} else if (1..=9).contains(&slot_index) {
// From crafting input slots - try to move to player inventory (slots 10-46)
if !self.insert_item(&mut slot_stack, 10, 46, false).await {
return ItemStack::EMPTY.clone();
}
} else if (10..46).contains(&slot_index) {
// 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 that fails, try moving within player inventory
if slot_index < 37 {
// From main inventory to hotbar
if !self.insert_item(&mut slot_stack, 37, 46, false).await {
return ItemStack::EMPTY.clone();
}
} else {
// From hotbar to main inventory
if !self.insert_item(&mut slot_stack, 10, 37, false).await {
return ItemStack::EMPTY.clone();
if slot_index == 0 {
// From crafting result slot - move to player inventory (slots 10-46)
if !self.insert_item(&mut slot_stack, 10, 46, true).await {
return ItemStack::EMPTY.clone();
}
} else if (1..=9).contains(&slot_index) {
// From crafting input slots - try to move to player inventory (slots 10-46)
if !self.insert_item(&mut slot_stack, 10, 46, false).await {
return ItemStack::EMPTY.clone();
}
} else if (10..46).contains(&slot_index) {
// 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 that fails, try moving within player inventory
if slot_index < 37 {
// From main inventory to hotbar
if !self.insert_item(&mut slot_stack, 37, 46, false).await {
return ItemStack::EMPTY.clone();
}
} else {
// From hotbar to main inventory
if !self.insert_item(&mut slot_stack, 10, 37, false).await {
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
if !self.insert_item(&mut slot_stack, 10, 46, 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 {
// Nothing changed
return ItemStack::EMPTY.clone();
}
}
let stack = slot_stack.clone();
drop(slot_stack); // release the lock before calling other methods
slot.on_take_item(player, &stack).await;
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();
}
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;
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 pumpkin_world::{
@ -96,10 +96,11 @@ impl Inventory for DoubleInventory {
}
}
#[async_trait]
impl Clearable for DoubleInventory {
async fn clear(&self) {
self.first.clear().await;
self.second.clear().await;
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move {
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_world::{block::entities::BlockEntity, inventory::Inventory, item::ItemStack};
use crate::{
player::player_inventory::PlayerInventory,
screen_handler::{
InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerListener,
ScreenProperty,
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour,
ScreenHandlerFuture, ScreenHandlerListener, ScreenProperty,
},
};
@ -36,19 +35,20 @@ impl FurnaceScreenHandler {
};
struct FurnaceScreenListener;
#[async_trait]
impl ScreenHandlerListener for FurnaceScreenListener {
async fn on_property_update(
&self,
screen_handler: &ScreenHandlerBehaviour,
fn on_property_update<'a>(
&'a self,
screen_handler: &'a ScreenHandlerBehaviour,
property: u8,
value: i32,
) {
if let Some(sync_handler) = screen_handler.sync_handler.as_ref() {
sync_handler
.update_property(screen_handler, property as i32, value)
.await;
}
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
if let Some(sync_handler) = screen_handler.sync_handler.as_ref() {
sync_handler
.update_property(screen_handler, property as i32, value)
.await;
}
})
}
}
@ -84,11 +84,12 @@ impl FurnaceScreenHandler {
}
}
#[async_trait]
impl ScreenHandler for FurnaceScreenHandler {
async fn on_closed(&mut self, player: &dyn InventoryPlayer) {
self.default_on_closed(player).await;
//TODO: self.inventory.on_closed(player).await;
fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
Box::pin(async move {
self.default_on_closed(player).await;
// TODO: self.inventory.on_closed(player).await;
})
}
fn as_any(&self) -> &dyn Any {
@ -103,40 +104,55 @@ impl ScreenHandler for FurnaceScreenHandler {
&mut self.behaviour
}
async fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack {
const FUEL_SLOT: i32 = 1;
fn quick_move<'a>(
&'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 slot = self.get_behaviour().slots[slot_index as usize].clone();
let mut stack_left = ItemStack::EMPTY.clone();
if !slot.has_stack().await {
return stack_left;
}
let slot = self.get_behaviour().slots[slot_index as usize].clone();
let slot_stack = slot.get_stack().await;
let mut stack = slot_stack.lock().await;
stack_left = stack.clone();
if !slot.has_stack().await {
return stack_left;
}
let success = if slot_index < 3 {
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
};
let slot_stack_lock = slot.get_stack().await;
if !success {
return ItemStack::EMPTY.clone();
}
// Acquire the lock to read/clone the stack
let mut stack = slot_stack_lock.lock().await;
stack_left = stack.clone();
if stack.is_empty() {
drop(stack);
slot.set_stack(ItemStack::EMPTY.clone()).await;
} else {
slot.mark_dirty().await;
}
let success = if slot_index < 3 {
// If clicked slot is one of the Furnace slots (0, 1, 2):
// Try to move to player inventory (slots 3 onwards, starting from the end)
self.insert_item(&mut stack, 3, self.get_behaviour().slots.len() as i32, true)
.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 async_trait::async_trait;
use pumpkin_data::screen::WindowType;
use pumpkin_world::{inventory::Inventory, item::ItemStack};
use crate::{
player::player_inventory::PlayerInventory,
screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour},
screen_handler::{
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour,
ScreenHandlerFuture,
},
slot::NormalSlot,
};
@ -119,11 +121,12 @@ impl GenericContainerScreenHandler {
}
}
#[async_trait]
impl ScreenHandler for GenericContainerScreenHandler {
async fn on_closed(&mut self, player: &dyn InventoryPlayer) {
self.default_on_closed(player).await;
self.inventory.on_close().await;
fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
Box::pin(async move {
self.default_on_closed(player).await;
self.inventory.on_close().await;
})
}
fn as_any(&self) -> &dyn Any {
@ -138,45 +141,58 @@ impl ScreenHandler for GenericContainerScreenHandler {
&mut self.behaviour
}
async fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack {
let mut stack_left = ItemStack::EMPTY.clone();
let slot = self.get_behaviour().slots[slot_index as usize].clone();
fn quick_move<'a>(
&'a mut self,
_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 {
let slot_stack = slot.get_stack().await;
stack_left = slot_stack.lock().await.clone();
if slot.has_stack().await {
let slot_stack_lock = slot.get_stack().await;
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 {
if !self
.insert_item(
&mut *slot_stack.lock().await,
(self.rows * 9).into(),
self.get_behaviour().slots.len() as i32,
true,
)
// Re-acquire lock for insert_item (which expects &mut ItemStack)
let mut slot_stack_mut = slot_stack_lock.lock().await;
if slot_index < (self.rows * 9) as i32 {
// Move from inventory to player area (end)
if !self
.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
{
// Move from player area to inventory (start)
return ItemStack::EMPTY.clone();
}
} else if !self
.insert_item(
&mut *slot_stack.lock().await,
0,
(self.rows * 9).into(),
false,
)
.await
{
return ItemStack::EMPTY.clone();
// Check the resulting state of the slot stack after insert_item
if slot_stack_mut.is_empty() {
drop(slot_stack_mut); // Release lock
slot.set_stack(ItemStack::EMPTY.clone()).await;
} else {
drop(slot_stack_mut); // Release lock
slot.mark_dirty().await;
}
}
if stack_left.is_empty() {
slot.set_stack(ItemStack::EMPTY.clone()).await;
} else {
slot.mark_dirty().await;
}
}
return stack_left;
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 pumpkin_world::{
@ -100,11 +100,12 @@ impl Inventory for EnderChestInventory {
}
}
#[async_trait]
impl Clearable for EnderChestInventory {
async fn clear(&self) {
for slot in self.items.iter() {
*slot.lock().await = ItemStack::EMPTY.clone();
}
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move {
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::array::from_fn;
use std::collections::HashMap;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicU8, Ordering};
use tokio::sync::Mutex;
@ -307,14 +308,15 @@ impl PlayerInventory {
}
}
#[async_trait]
impl Clearable for PlayerInventory {
async fn clear(&self) {
for item in self.main_inventory.iter() {
*item.lock().await = ItemStack::EMPTY.clone();
}
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move {
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_screen_handler::CraftingScreenHandler;
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 async_trait::async_trait;
use pumpkin_data::data_component_impl::{EquipmentSlot, EquipmentType, EquippableImpl};
use pumpkin_data::screen::WindowType;
use pumpkin_world::inventory::Inventory;
@ -75,13 +76,14 @@ impl PlayerScreenHandler {
}
}
#[async_trait]
impl ScreenHandler for PlayerScreenHandler {
async fn on_closed(&mut self, player: &dyn InventoryPlayer) {
self.default_on_closed(player).await;
//TODO: this.craftingResultInventory.clear();
self.drop_inventory(player, self.crafting_inventory.clone())
.await;
fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
Box::pin(async move {
self.default_on_closed(player).await;
//TODO: this.craftingResultInventory.clear();
self.drop_inventory(player, self.crafting_inventory.clone())
.await;
})
}
fn as_any(&self) -> &dyn Any {
@ -99,114 +101,118 @@ impl ScreenHandler for PlayerScreenHandler {
/// Do quick move (Shift + Click) for the given slot index.
///
/// 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 {
let slot = self.get_behaviour().slots[slot_index as usize].clone();
fn quick_move<'a>(
&'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 {
let slot_stack = slot.get_stack().await;
let mut slot_stack = slot_stack.lock().await;
let stack_prev = slot_stack.clone();
if slot.has_stack().await {
let slot_stack_lock = slot.get_stack().await;
let mut slot_stack = slot_stack_lock.lock().await;
let stack_prev = slot_stack.clone();
let equipment_slot = slot_stack
.get_data_component::<EquippableImpl>()
.map_or(&EquipmentSlot::MAIN_HAND, |equippable| equippable.slot);
let equipment_slot = slot_stack
.get_data_component::<EquippableImpl>()
.map_or(&EquipmentSlot::MAIN_HAND, |equippable| equippable.slot);
#[allow(clippy::if_same_then_else)]
if slot_index == 0 {
// From crafting result slot
if !self.insert_item(&mut slot_stack, 9, 45, true).await {
return ItemStack::EMPTY.clone();
}
} else if (1..5).contains(&slot_index) {
// From craft ingredient slots
if !self.insert_item(&mut slot_stack, 9, 45, false).await {
return ItemStack::EMPTY.clone();
}
} 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();
}
// Quick move logic
#[allow(clippy::if_same_then_else)]
let success = if slot_index == 0 {
// From crafting result slot (0) -> Player Inventory (9-45, from end)
self.insert_item(&mut slot_stack, 9, 45, true).await
} else if (1..5).contains(&slot_index) {
// From craft ingredient slots (1-4) -> Player Inventory (9-45, from start)
self.insert_item(&mut slot_stack, 9, 45, false).await
} else if (5..9).contains(&slot_index) {
// From armour slots (5-8) -> Player Inventory (9-45, from start)
let result = self.insert_item(&mut slot_stack, 9, 45, false).await;
player
.enqueue_equipment_change(equipment_slot, ItemStack::EMPTY)
.await;
} else if equipment_slot.slot_type() == EquipmentType::HumanoidArmor
&& self
.get_slot((8 - equipment_slot.get_entity_slot_id()) as usize)
.await
.get_cloned_stack()
.await
.is_empty()
{
// Into armour slots
let index = 8 - equipment_slot.get_entity_slot_id();
if !self
.insert_item(&mut slot_stack, index, index + 1, false)
.await
if result {
player
.enqueue_equipment_change(equipment_slot, ItemStack::EMPTY)
.await;
}
result
} else if equipment_slot.slot_type() == EquipmentType::HumanoidArmor
&& self
.get_slot((8 - equipment_slot.get_entity_slot_id()) as usize)
.await
.get_cloned_stack()
.await
.is_empty()
{
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
.enqueue_equipment_change(equipment_slot, &stack_prev)
.await;
} else if matches!(equipment_slot, EquipmentSlot::OffHand(_))
&& slot_index != 45
&& self.get_slot(45).await.get_cloned_stack().await.is_empty()
{
// Into offhand slot
let index = 45;
if !self
.insert_item(&mut slot_stack, index, index + 1, false)
.await
if result {
player
.enqueue_equipment_change(equipment_slot, &stack_prev)
.await;
}
result
} else if matches!(equipment_slot, EquipmentSlot::OffHand(_))
&& slot_index != 45
&& self.get_slot(45).await.get_cloned_stack().await.is_empty()
{
// 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();
}
} 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();
}
} else if (36..45).contains(&slot_index) {
if !self.insert_item(&mut slot_stack, 9, 36, false).await {
return ItemStack::EMPTY.clone();
slot.on_take_item(player, &stack).await;
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();
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();
}
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()
// 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::sound::{Sound, SoundCategory};
use pumpkin_data::{Block, FacingExt};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::random::xoroshiro128::Xoroshiro;
use pumpkin_util::random::{RandomImpl, get_seed};
use std::any::Any;
use std::pin::Pin;
use std::{
array::from_fn,
sync::{
@ -16,7 +18,7 @@ use std::{
};
use tokio::sync::Mutex;
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker};
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture};
use crate::world::{BlockFlags, SimpleWorld};
use crate::{
inventory::{
@ -37,7 +39,6 @@ pub struct BarrelBlockEntity {
viewers: ViewerCountTracker,
}
#[async_trait]
impl BlockEntity for BarrelBlockEntity {
fn resource_location(&self) -> &'static str {
Self::ID
@ -63,16 +64,26 @@ impl BlockEntity for BarrelBlockEntity {
barrel
}
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
self.write_data(nbt, &self.items, true).await;
fn write_nbt<'a>(
&'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
//self.clear().await;
}
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
self.viewers
.update_viewer_count::<BarrelBlockEntity>(self, world, &self.position)
.await;
fn tick<'a>(
&'a self,
world: Arc<dyn SimpleWorld>,
) -> 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>> {
@ -88,16 +99,27 @@ impl BlockEntity for BarrelBlockEntity {
}
}
#[async_trait]
impl ViewerCountListener for BarrelBlockEntity {
async fn on_container_open(&self, world: &Arc<dyn SimpleWorld>, _position: &BlockPos) {
self.play_sound(world, Sound::BlockBarrelOpen).await;
self.set_open(world, true).await;
fn on_container_open<'a>(
&'a self,
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) {
self.play_sound(world, Sound::BlockBarrelClose).await;
self.set_open(world, false).await;
fn on_container_close<'a>(
&'a self,
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 {
async fn clear(&self) {
for slot in self.items.iter() {
*slot.lock().await = ItemStack::EMPTY.clone();
}
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move {
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 super::BlockEntity;
@ -7,7 +9,6 @@ pub struct BedBlockEntity {
pub position: BlockPos,
}
#[async_trait]
impl BlockEntity for BedBlockEntity {
fn resource_location(&self) -> &'static str {
Self::ID
@ -24,7 +25,12 @@ impl BlockEntity for BedBlockEntity {
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 {
self

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
use std::{
array::from_fn,
pin::Pin,
sync::{
Arc,
atomic::{AtomicBool, AtomicU16, Ordering},
@ -14,6 +15,7 @@ use pumpkin_data::{
recipe_remainder::get_recipe_remainder_id,
recipes::{CookingRecipe, CookingRecipeType, RECIPES_COOKING},
};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos;
use tokio::sync::Mutex;
@ -156,112 +158,117 @@ impl FurnaceBlockEntity {
}
}
#[async_trait]
impl BlockEntity for FurnaceBlockEntity {
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
let is_burning = self.is_burning();
let mut is_dirty = false;
if self.is_burning() {
self.lit_time_remaining.fetch_sub(1, Ordering::Relaxed);
}
fn tick<'a>(
&'a self,
world: Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
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 is_top_items_empty = top_items.is_empty();
let top_items = self.items[0].lock().await;
let is_top_items_empty = top_items.is_empty();
let furnace_recipe = Self::get_furnace_cooking_recipe(top_items.item);
drop(top_items);
let furnace_recipe = Self::get_furnace_cooking_recipe(top_items.item);
drop(top_items);
let can_accept_output = self
.can_accept_recipe_output(furnace_recipe, self.get_max_count_per_stack())
.await;
let can_accept_output = self
.can_accept_recipe_output(furnace_recipe, self.get_max_count_per_stack())
.await;
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() && can_accept_output {
let mut bottom_items = self.items[1].lock().await;
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() && can_accept_output {
let mut bottom_items = self.items[1].lock().await;
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_total_time.store(fuel_ticks, Ordering::Relaxed);
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_total_time.store(fuel_ticks, Ordering::Relaxed);
if self.is_burning() {
is_dirty = true;
if !bottom_items.is_empty() {
bottom_items.decrement(1);
if let Some(remainder_id) = get_recipe_remainder_id(bottom_items.item.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;
if self.is_burning() {
is_dirty = true;
if !bottom_items.is_empty() {
bottom_items.decrement(1);
if let Some(remainder_id) =
get_recipe_remainder_id(bottom_items.item.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;
}
}
}
}
}
if self.is_burning() && can_accept_output {
self.cooking_time_spent.fetch_add(1, Ordering::Relaxed);
if self.is_burning() && can_accept_output {
self.cooking_time_spent.fetch_add(1, Ordering::Relaxed);
if self.cooking_time_spent.load(Ordering::Relaxed)
== self.cooking_total_time.load(Ordering::Relaxed)
{
self.cooking_time_spent.store(0, Ordering::Relaxed);
if let Some(cooking_recipe) = furnace_recipe {
let cooking_total_time = cooking_recipe.cookingtime;
self.cooking_total_time
.store(cooking_total_time as u16, Ordering::Relaxed);
if self.cooking_time_spent.load(Ordering::Relaxed)
== self.cooking_total_time.load(Ordering::Relaxed)
{
self.cooking_time_spent.store(0, Ordering::Relaxed);
if let Some(cooking_recipe) = furnace_recipe {
let cooking_total_time = cooking_recipe.cookingtime;
self.cooking_total_time
.store(cooking_total_time as u16, Ordering::Relaxed);
self.craft_recipe(Some(cooking_recipe)).await;
is_dirty = true;
self.craft_recipe(Some(cooking_recipe)).await;
is_dirty = true;
}
}
} else {
self.cooking_time_spent.store(0, Ordering::Relaxed);
}
} else {
self.cooking_time_spent.store(0, Ordering::Relaxed);
} 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();
}
} 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() {
is_dirty = true;
let world = world.clone();
if is_burning != self.is_burning() {
is_dirty = true;
let world = world.clone();
let (furnace_block, furnace_block_state) =
world.get_block_and_state(&self.position).await;
let mut props =
FurnaceLikeProperties::from_state_id(furnace_block_state.id, furnace_block);
let (furnace_block, furnace_block_state) =
world.get_block_and_state(&self.position).await;
let mut props =
FurnaceLikeProperties::from_state_id(furnace_block_state.id, furnace_block);
if self.is_burning() {
props.lit = true;
world
.set_block_state(
&self.position,
props.to_state_id(furnace_block),
BlockFlags::NOTIFY_ALL,
)
.await;
} else {
props.lit = false;
world
.set_block_state(
&self.position,
props.to_state_id(furnace_block),
BlockFlags::NOTIFY_ALL,
)
.await;
if self.is_burning() {
props.lit = true;
world
.set_block_state(
&self.position,
props.to_state_id(furnace_block),
BlockFlags::NOTIFY_ALL,
)
.await;
} else {
props.lit = false;
world
.set_block_state(
&self.position,
props.to_state_id(furnace_block),
BlockFlags::NOTIFY_ALL,
)
.await;
}
}
}
if is_dirty {
self.is_dirty();
}
if is_dirty {
self.is_dirty();
}
})
}
fn resource_location(&self) -> &'static str {
@ -307,24 +314,29 @@ impl BlockEntity for FurnaceBlockEntity {
furnace
}
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
nbt.put_short(
"cooking_total_time",
self.cooking_total_time.load(Ordering::Relaxed) as i16,
);
nbt.put_short(
"cooking_time_spent",
self.cooking_time_spent.load(Ordering::Relaxed) as i16,
);
nbt.put_short(
"lit_total_time",
self.lit_total_time.load(Ordering::Relaxed) as i16,
);
nbt.put_short(
"lit_time_remaining",
self.lit_time_remaining.load(Ordering::Relaxed) as i16,
);
self.write_data(nbt, &self.items, true).await;
fn write_nbt<'a>(
&'a self,
nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
nbt.put_short(
"cooking_total_time",
self.cooking_total_time.load(Ordering::Relaxed) as i16,
);
nbt.put_short(
"cooking_time_spent",
self.cooking_time_spent.load(Ordering::Relaxed) as i16,
);
nbt.put_short(
"lit_total_time",
self.lit_total_time.load(Ordering::Relaxed) as i16,
);
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
// self.clear().await;
}
@ -425,12 +437,13 @@ impl Inventory for FurnaceBlockEntity {
}
}
#[async_trait]
impl Clearable for FurnaceBlockEntity {
async fn clear(&self) {
for slot in self.items.iter() {
*slot.lock().await = ItemStack::EMPTY.clone();
}
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move {
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::tag::Taggable;
use pumpkin_data::{Block, tag};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_nbt::tag::NbtTag;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use std::any::Any;
use std::array::from_fn;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64};
use tokio::sync::Mutex;
@ -37,17 +39,21 @@ pub fn to_offset(facing: &HopperFacing) -> Vector3<i32> {
.into()
}
#[async_trait]
impl BlockEntity for HopperBlockEntity {
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
self.write_data(nbt, &self.items, true).await;
nbt.put(
"TransferCooldown",
NbtTag::Int(
self.cooldown_time
.load(std::sync::atomic::Ordering::Relaxed),
),
);
fn write_nbt<'a>(
&'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;
nbt.put(
"TransferCooldown",
NbtTag::Int(
self.cooldown_time
.load(std::sync::atomic::Ordering::Relaxed),
),
);
})
// Safety precaution
//self.clear().await;
}
@ -70,24 +76,29 @@ impl BlockEntity for HopperBlockEntity {
hopper
}
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
self.ticked_game_time.store(
world.get_world_age().await,
std::sync::atomic::Ordering::Relaxed,
);
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,
fn tick<'a>(
&'a self,
world: Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
self.ticked_game_time.store(
world.get_world_age().await,
std::sync::atomic::Ordering::Relaxed,
);
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 {
@ -327,11 +338,12 @@ impl Inventory for HopperBlockEntity {
}
}
#[async_trait]
impl Clearable for HopperBlockEntity {
async fn clear(&self) {
for slot in self.items.iter() {
*slot.lock().await = ItemStack::EMPTY.clone();
}
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move {
for slot in self.items.iter() {
*slot.lock().await = ItemStack::EMPTY.clone();
}
})
}
}

View File

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

View File

@ -1,6 +1,6 @@
use std::pin::Pin;
use std::{any::Any, sync::Arc};
use async_trait::async_trait;
use barrel::BarrelBlockEntity;
use bed::BedBlockEntity;
use chest::ChestBlockEntity;
@ -39,22 +39,34 @@ pub mod shulker_box;
pub mod sign;
//TODO: We need a mark_dirty for chests
#[async_trait]
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
where
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 get_position(&self) -> BlockPos;
async fn write_internal(&self, nbt: &mut NbtCompound) {
nbt.put_string("id", self.resource_location().to_string());
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 write_internal<'a>(
&'a self,
nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
nbt.put_string("id", self.resource_location().to_string());
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 {
pumpkin_data::block_properties::BLOCK_ENTITY_TYPES
@ -71,10 +83,20 @@ pub trait BlockEntity: Send + Sync {
None
}
fn set_block_state(&mut self, _block_state: BlockStateId) {}
async fn on_block_replaced(self: Arc<Self>, world: Arc<dyn SimpleWorld>, position: BlockPos) {
if let Some(inventory) = self.get_inventory() {
world.scatter_inventory(&position, &inventory).await;
}
fn on_block_replaced<'a>(
self: Arc<Self>,
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 {
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 pumpkin_data::{Block, BlockDirection, BlockState};
use pumpkin_nbt::compound::NbtCompound;
@ -50,7 +49,6 @@ const LAST_PROGRESS: &str = "progress";
const EXTENDING: &str = "extending";
const SOURCE: &str = "source";
#[async_trait]
impl BlockEntity for PistonBlockEntity {
fn resource_location(&self) -> &'static str {
Self::ID
@ -60,42 +58,47 @@ impl BlockEntity for PistonBlockEntity {
self.position
}
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
let current_progress = self.current_progress.load();
self.last_progress.store(current_progress);
if current_progress >= 1.0 {
let pos = self.position;
world.remove_block_entity(&pos).await;
if world.get_block(&pos).await == &Block::MOVING_PISTON {
if self.pushed_block_state.is_air() {
world
.clone()
.set_block_state(
&pos,
self.pushed_block_state.id,
BlockFlags::FORCE_STATE | BlockFlags::MOVED,
)
.await;
} else {
world
.clone()
.set_block_state(
&pos,
self.pushed_block_state.id,
BlockFlags::NOTIFY_ALL | BlockFlags::MOVED,
)
.await;
world
.clone()
.update_neighbor(&pos, Block::from_state_id(self.pushed_block_state.id))
.await;
fn tick<'a>(
&'a self,
world: Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
let current_progress = self.current_progress.load();
self.last_progress.store(current_progress);
if current_progress >= 1.0 {
let pos = self.position;
world.remove_block_entity(&pos).await;
if world.get_block(&pos).await == &Block::MOVING_PISTON {
if self.pushed_block_state.is_air() {
world
.clone()
.set_block_state(
&pos,
self.pushed_block_state.id,
BlockFlags::FORCE_STATE | BlockFlags::MOVED,
)
.await;
} else {
world
.clone()
.set_block_state(
&pos,
self.pushed_block_state.id,
BlockFlags::NOTIFY_ALL | BlockFlags::MOVED,
)
.await;
world
.clone()
.update_neighbor(&pos, Block::from_state_id(self.pushed_block_state.id))
.await;
}
}
}
}
self.current_progress.store(current_progress + 0.5);
if current_progress + 0.5 >= 1.0 {
self.current_progress.store(1.0);
}
self.current_progress.store(current_progress + 0.5);
if current_progress + 0.5 >= 1.0 {
self.current_progress.store(1.0);
}
})
}
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
@ -119,12 +122,17 @@ impl BlockEntity for PistonBlockEntity {
}
}
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
// TODO: pushed_block_state
nbt.put_byte(FACING, self.facing.to_index() as i8);
nbt.put_float(LAST_PROGRESS, self.last_progress.load());
nbt.put_bool(EXTENDING, self.extending);
nbt.put_bool(SOURCE, self.source);
fn write_nbt<'a>(
&'a self,
nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
// 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> {

View File

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

View File

@ -1,10 +1,12 @@
use std::sync::{
Arc,
atomic::{AtomicBool, AtomicI8, Ordering},
use std::{
pin::Pin,
sync::{
Arc,
atomic::{AtomicBool, AtomicI8, Ordering},
},
};
use super::BlockEntity;
use async_trait::async_trait;
use num_derive::FromPrimitive;
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
use pumpkin_util::math::position::BlockPos;
@ -208,7 +210,6 @@ impl Text {
}
}
#[async_trait]
impl BlockEntity for SignBlockEntity {
fn resource_location(&self) -> &'static str {
Self::ID
@ -234,10 +235,15 @@ impl BlockEntity for SignBlockEntity {
}
}
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
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 write_nbt<'a>(
&'a self,
nbt: &'a mut NbtCompound,
) -> 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> {

View File

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

View File

@ -10,6 +10,7 @@ use std::{
io::{Read, SeekFrom, Write},
marker::PhantomData,
path::{Path, PathBuf},
pin::Pin,
time::{SystemTime, UNIX_EPOCH},
};
use tokio::{
@ -517,9 +518,10 @@ impl<S: SingleChunkDataSerializer> Default for AnvilChunkFile<S> {
}
}
#[async_trait]
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 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 futures::future::join_all;
use pumpkin_data::{Block, chunk::ChunkStatus, fluid::Fluid};
@ -30,7 +29,6 @@ use crate::block::BlockStateCodec;
pub mod anvil;
pub mod linear;
#[async_trait]
impl SingleChunkDataSerializer for ChunkData {
#[inline]
fn from_bytes(bytes: Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError> {
@ -38,8 +36,10 @@ impl SingleChunkDataSerializer for ChunkData {
}
#[inline]
async fn to_bytes(&self) -> Result<Bytes, ChunkSerializingError> {
self.internal_to_bytes().await
fn to_bytes(
&self,
) -> Pin<Box<dyn Future<Output = Result<Bytes, ChunkSerializingError>> + Send + '_>> {
Box::pin(async move { self.internal_to_bytes().await })
}
#[inline]
@ -247,7 +247,6 @@ impl Dirtiable for ChunkEntityData {
}
}
#[async_trait]
impl SingleChunkDataSerializer for ChunkEntityData {
#[inline]
fn from_bytes(bytes: Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError> {
@ -255,8 +254,10 @@ impl SingleChunkDataSerializer for ChunkEntityData {
}
#[inline]
async fn to_bytes(&self) -> Result<Bytes, ChunkSerializingError> {
self.internal_to_bytes()
fn to_bytes(
&self,
) -> Pin<Box<dyn Future<Output = Result<Bytes, ChunkSerializingError>> + Send + '_>> {
Box::pin(async move { self.internal_to_bytes() })
}
#[inline]

View File

@ -11,6 +11,7 @@ use crate::chunk::io::LoadedData::Loaded;
use crate::chunk::{ChunkData, ChunkHeightmapType, ChunkLight, ChunkSections, SubChunk};
use crate::dimension::Dimension;
use std::default::Default;
use std::pin::Pin;
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::world::{BlockAccessor, BlockRegistryExt};
use crate::{GlobalRandomConfig, ProtoChunk, ProtoNoiseRouters};
use async_trait::async_trait;
use crossbeam::channel::{Receiver, Sender};
use dashmap::DashMap;
use itertools::Itertools;
@ -849,22 +849,29 @@ impl HeightLimitView for Cache {
}
}
#[async_trait]
impl BlockAccessor for Cache {
async fn get_block(&self, position: &BlockPos) -> &'static Block {
GenerationCache::get_block_state(self, &position.0).to_block()
fn get_block<'a>(
&'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 {
GenerationCache::get_block_state(self, &position.0).to_state()
fn get_block_state<'a>(
&'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(
&self,
position: &BlockPos,
) -> (&'static Block, &'static BlockState) {
let id = GenerationCache::get_block_state(self, &position.0);
(id.to_block(), id.to_state())
fn get_block_and_state<'a>(
&'a self,
position: &'a BlockPos,
) -> Pin<Box<dyn Future<Output = (&'static Block, &'static BlockState)> + Send + 'a>> {
Box::pin(async move {
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_util::{HeightMap, include_json_static};
use serde::Deserialize;
@ -378,7 +377,6 @@ pub struct BlockFilterPlacementModifier {
predicate: BlockPredicate,
}
#[async_trait]
impl ConditionalPlacementModifier for BlockFilterPlacementModifier {
fn should_place<T: GenerationCache>(
&self,
@ -399,7 +397,6 @@ pub struct SurfaceThresholdFilterPlacementModifier {
max_inclusive: Option<i32>,
}
#[async_trait]
impl ConditionalPlacementModifier for SurfaceThresholdFilterPlacementModifier {
fn should_place<T: GenerationCache>(
&self,
@ -421,7 +418,6 @@ pub struct RarityFilterPlacementModifier {
chance: u32,
}
#[async_trait]
impl ConditionalPlacementModifier for RarityFilterPlacementModifier {
fn should_place<T: GenerationCache>(
&self,
@ -465,7 +461,6 @@ pub struct SurfaceWaterDepthFilterPlacementModifier {
max_water_depth: i32,
}
#[async_trait]
impl ConditionalPlacementModifier for SurfaceWaterDepthFilterPlacementModifier {
fn should_place<T: GenerationCache>(
&self,
@ -484,7 +479,6 @@ impl ConditionalPlacementModifier for SurfaceWaterDepthFilterPlacementModifier {
#[derive(Deserialize)]
pub struct BiomePlacementModifier;
#[async_trait]
impl ConditionalPlacementModifier for BiomePlacementModifier {
fn should_place<T: GenerationCache>(
&self,
@ -563,7 +557,6 @@ pub trait CountPlacementModifierBase {
fn get_count(&self, random: &mut RandomGenerator, pos: BlockPos) -> i32;
}
#[async_trait]
pub trait ConditionalPlacementModifier {
fn get_positions<T: GenerationCache>(
&self,

View File

@ -1,6 +1,6 @@
use std::collections::HashMap;
use std::pin::Pin;
use async_trait::async_trait;
use pumpkin_data::tag;
use pumpkin_data::{
Block, BlockState, block_properties::blocks_movement, chunk::Biome, tag::Taggable,
@ -988,22 +988,29 @@ impl ProtoChunk {
}
}
#[async_trait]
impl BlockAccessor for ProtoChunk {
async fn get_block(&self, position: &BlockPos) -> &'static pumpkin_data::Block {
self.get_block_state(&position.0).to_block()
fn get_block<'a>(
&'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 {
self.get_block_state(&position.0).to_state()
fn get_block_state<'a>(
&'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(
&self,
position: &BlockPos,
) -> (&'static Block, &'static BlockState) {
let id = self.get_block_state(&position.0);
BlockState::from_id_with_block(id.0)
fn get_block_and_state<'a>(
&'a self,
position: &'a BlockPos,
) -> Pin<Box<dyn Future<Output = (&'static Block, &'static BlockState)> + Send + 'a>> {
Box::pin(async move {
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_nbt::{compound::NbtCompound, tag::NbtTag};
use std::any::Any;
use std::pin::Pin;
use std::{
fmt::Debug,
hash::{Hash, Hasher},
@ -139,9 +140,8 @@ pub trait Inventory: Send + Sync + Debug + Clearable {
fn as_any(&self) -> &dyn Any;
}
#[async_trait]
pub trait Clearable {
async fn clear(&self);
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
}
pub struct ComparableInventory(pub Arc<dyn Inventory>);

View File

@ -1,8 +1,8 @@
use std::pin::Pin;
use std::sync::Arc;
use crate::block::entities::BlockEntity;
use crate::{BlockStateId, inventory::Inventory};
use async_trait::async_trait;
use bitflags::bitflags;
use pumpkin_data::entity::EntityType;
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 {
async fn set_block_state(
fn set_block_state<'a>(
self: Arc<Self>,
position: &BlockPos,
position: &'a BlockPos,
block_state_id: BlockStateId,
flags: BlockFlags,
) -> BlockStateId;
) -> WorldFuture<'a, BlockStateId>;
async fn update_neighbor(
fn update_neighbor<'a>(
self: Arc<Self>,
neighbor_block_pos: &BlockPos,
source_block: &pumpkin_data::Block,
);
neighbor_block_pos: &'a BlockPos,
source_block: &'a pumpkin_data::Block,
) -> WorldFuture<'a, ()>;
async fn update_neighbors(
fn update_neighbors<'a>(
self: Arc<Self>,
block_pos: &BlockPos,
block_pos: &'a BlockPos,
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>,
entity_type: &'static EntityType,
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);
async fn get_block_entity(&self, block_pos: &BlockPos) -> Option<Arc<dyn BlockEntity>>;
async fn get_world_age(&self) -> i64;
fn remove_block_entity<'a>(&'a self, block_pos: &'a BlockPos) -> WorldFuture<'a, ()>;
async fn play_sound(&self, sound: Sound, category: SoundCategory, position: &Vector3<f64>);
async fn play_sound_fine(
&self,
fn get_block_entity<'a>(
&'a 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,
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,
pitch: f32,
);
) -> WorldFuture<'a, ()>;
/* ItemScatterer */
async fn scatter_inventory(
fn scatter_inventory<'a>(
self: Arc<Self>,
position: &BlockPos,
inventory: &Arc<dyn Inventory>,
);
position: &'a BlockPos,
inventory: &'a Arc<dyn Inventory>,
) -> WorldFuture<'a, ()>;
}
pub trait BlockRegistryExt: Send + Sync {
@ -105,14 +127,19 @@ pub trait BlockRegistryExt: Send + Sync {
) -> bool;
}
#[async_trait]
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(
&self,
position: &BlockPos,
) -> (&'static Block, &'static BlockState);
fn get_block_and_state<'a>(
&'a self,
position: &'a BlockPos,
) -> 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::{
Block, BlockDirection,
block_properties::{BlockFace, HorizontalFacing},
@ -6,10 +5,12 @@ use pumpkin_data::{
use pumpkin_util::math::position::BlockPos;
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 {
pub trait WallMountedBlock: Send + Sync {
fn get_direction(&self, state_id: BlockStateId, block: &Block) -> BlockDirection;
fn get_placement_face(
@ -32,29 +33,33 @@ pub trait WallMountedBlock {
(face, facing)
}
async fn can_place_at(
&self,
world: &dyn BlockAccessor,
pos: &BlockPos,
fn can_place_at<'a>(
&'a self,
world: &'a dyn BlockAccessor,
pos: &'a BlockPos,
direction: BlockDirection,
) -> bool {
let block_pos = pos.offset(direction.to_offset());
let block_state = world.get_block_state(&block_pos).await;
block_state.is_side_solid(direction.opposite())
) -> BlockFuture<'a, bool> {
Box::pin(async move {
let block_pos = pos.offset(direction.to_offset());
let block_state = world.get_block_state(&block_pos).await;
block_state.is_side_solid(direction.opposite())
})
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
if self.get_direction(args.state_id, args.block).opposite() == args.direction
&& !self
.can_place_at(args.world, args.position, args.direction)
.await
{
Block::AIR.default_state.id
} else {
args.state_id
}
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
if self.get_direction(args.state_id, args.block).opposite() == args.direction
&& !self
.can_place_at(args.world, args.position, args.direction)
.await
{
Block::AIR.default_state.id
} else {
args.state_id
}
})
}
}

View File

@ -1,8 +1,8 @@
use crate::block::blocks::falling::FallingBlock;
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::tag::{RegistryKey, get_tag_values};
use pumpkin_macros::pumpkin_block_from_tag;
@ -11,33 +11,40 @@ use pumpkin_world::BlockStateId;
#[pumpkin_block_from_tag("minecraft:anvil")]
pub struct AnvilBlock;
#[async_trait]
impl BlockBehaviour for AnvilBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let dir = args
.player
.living_entity
.entity
.get_horizontal_facing()
.rotate_clockwise();
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let dir = args
.player
.living_entity
.entity
.get_horizontal_facing()
.rotate_clockwise();
let mut props = WallTorchLikeProperties::default(args.block);
let mut props = WallTorchLikeProperties::default(args.block);
props.facing = dir;
props.to_state_id(args.block)
props.facing = dir;
props.to_state_id(args.block)
})
}
async fn placed(&self, args: PlacedArgs<'_>) {
FallingBlock::placed(&FallingBlock, args).await;
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
FallingBlock::placed(&FallingBlock, args).await;
})
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
FallingBlock::get_state_for_neighbor_update(&FallingBlock, args).await
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(
async move { FallingBlock::get_state_for_neighbor_update(&FallingBlock, args).await },
)
}
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
FallingBlock::on_scheduled_tick(&FallingBlock, args).await;
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
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::Taggable;
use pumpkin_macros::pumpkin_block;
use crate::block::{BlockBehaviour, CanPlaceAtArgs};
use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs};
#[pumpkin_block("minecraft:bamboo")]
pub struct BambooBlock;
#[async_trait]
impl BlockBehaviour for BambooBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
let block_below = args.block_accessor.get_block(&args.position.down()).await;
block_below.has_tag(&tag::Block::MINECRAFT_BAMBOO_PLANTABLE_ON)
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
let block_below = args.block_accessor.get_block(&args.position.down()).await;
block_below.has_tag(&tag::Block::MINECRAFT_BAMBOO_PLANTABLE_ON)
})
}
}

View File

@ -1,6 +1,5 @@
use crate::block::{BlockBehaviour, OnPlaceArgs};
use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs};
use crate::entity::EntityBase;
use async_trait::async_trait;
use pumpkin_data::block_properties::{BlockProperties, WhiteBannerLikeProperties};
use pumpkin_data::tag::{RegistryKey, get_tag_values};
use pumpkin_macros::pumpkin_block_from_tag;
@ -9,11 +8,12 @@ use pumpkin_world::BlockStateId;
#[pumpkin_block_from_tag("minecraft:banners")]
pub struct BannerBlock;
#[async_trait]
impl BlockBehaviour for BannerBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut props = WhiteBannerLikeProperties::default(args.block);
props.rotation = args.player.get_entity().get_flipped_rotation_16();
props.to_state_id(args.block)
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut props = WhiteBannerLikeProperties::default(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 crate::block::{OnPlaceArgs, PlacedArgs};
use crate::block::{BlockFuture, OnPlaceArgs, PlacedArgs};
use crate::block::{
registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs},
@ -40,30 +40,35 @@ impl ScreenHandlerFactory for BarrelScreenFactory {
#[pumpkin_block("minecraft:barrel")]
pub struct BarrelBlock;
#[async_trait]
impl BlockBehaviour for BarrelBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut props = BarrelLikeProperties::default(args.block);
props.facing = args.player.living_entity.entity.get_facing().opposite();
props.to_state_id(args.block)
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut props = BarrelLikeProperties::default(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 {
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))
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
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))
.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;
}
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 async_trait::async_trait;
use crate::block::{BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs};
use pumpkin_data::block_properties::{
BlockProperties, MangroveRootsLikeProperties as BarrierLikeProperties,
};
@ -11,29 +10,32 @@ use pumpkin_world::tick::TickPriority;
#[pumpkin_block("minecraft:barrier")]
pub struct BarrierBlock;
#[async_trait]
impl BlockBehaviour for BarrierBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut props = BarrierLikeProperties::default(args.block);
props.waterlogged = args.replacing.water_source();
props.to_state_id(args.block)
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut props = BarrierLikeProperties::default(args.block);
props.waterlogged = args.replacing.water_source();
props.to_state_id(args.block)
})
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
let props = BarrierLikeProperties::from_state_id(args.state_id, args.block);
if props.waterlogged {
args.world
.schedule_fluid_tick(
&Fluid::WATER,
*args.position,
Fluid::WATER.flow_speed as u8,
TickPriority::Normal,
)
.await;
}
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 props = BarrierLikeProperties::from_state_id(args.state_id, args.block);
if props.waterlogged {
args.world
.schedule_fluid_tick(
&Fluid::WATER,
*args.position,
Fluid::WATER.flow_speed as u8,
TickPriority::Normal,
)
.await;
}
props.to_state_id(args.block)
})
}
}

View File

@ -1,6 +1,5 @@
use std::sync::Arc;
use async_trait::async_trait;
use pumpkin_data::Block;
use pumpkin_data::block_properties::BedPart;
use pumpkin_data::block_properties::BlockProperties;
@ -15,6 +14,7 @@ use pumpkin_world::BlockStateId;
use pumpkin_world::block::entities::bed::BedBlockEntity;
use pumpkin_world::world::BlockFlags;
use crate::block::BlockFuture;
use crate::block::registry::BlockActionResult;
use crate::block::{
BlockBehaviour, BrokenArgs, CanPlaceAtArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs,
@ -65,214 +65,223 @@ const NO_SLEEP_IDS: &[u16] = &[
#[pumpkin_block_from_tag("minecraft:beds")]
pub struct BedBlock;
#[async_trait]
impl BlockBehaviour for BedBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
if let Some(player) = args.player {
let facing = player.living_entity.entity.get_horizontal_facing();
return args
.block_accessor
.get_block_state(args.position)
.await
.replaceable()
&& args
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
if let Some(player) = args.player {
let facing = player.living_entity.entity.get_horizontal_facing();
return args
.block_accessor
.get_block_state(&args.position.offset(facing.to_offset()))
.get_block_state(args.position)
.await
.replaceable();
}
false
.replaceable()
&& args
.block_accessor
.get_block_state(&args.position.offset(facing.to_offset()))
.await
.replaceable();
}
false
})
}
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut bed_props = BedProperties::default(args.block);
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
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.part = BedPart::Foot;
bed_props.facing = args.player.living_entity.entity.get_horizontal_facing();
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<'_>) {
let bed_entity = BedBlockEntity::new(*args.position);
args.world.add_block_entity(Arc::new(bed_entity)).await;
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
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);
bed_head_props.facing = BedProperties::from_state_id(args.state_id, args.block).facing;
bed_head_props.part = BedPart::Head;
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.part = BedPart::Head;
let bed_head_pos = args.position.offset(bed_head_props.facing.to_offset());
args.world
.set_block_state(
&bed_head_pos,
bed_head_props.to_state_id(args.block),
BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK,
)
.await;
let bed_head_pos = args.position.offset(bed_head_props.facing.to_offset());
args.world
.set_block_state(
&bed_head_pos,
bed_head_props.to_state_id(args.block),
BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK,
)
.await;
let bed_head_entity = BedBlockEntity::new(bed_head_pos);
args.world.add_block_entity(Arc::new(bed_head_entity)).await;
let bed_head_entity = BedBlockEntity::new(bed_head_pos);
args.world.add_block_entity(Arc::new(bed_head_entity)).await;
})
}
async fn broken(&self, args: BrokenArgs<'_>) {
let bed_props = BedProperties::from_state_id(args.state.id, args.block);
let other_half_pos = if bed_props.part == BedPart::Head {
args.position
.offset(bed_props.facing.opposite().to_offset())
} else {
args.position.offset(bed_props.facing.to_offset())
};
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
let bed_props = BedProperties::from_state_id(args.state.id, args.block);
let other_half_pos = if bed_props.part == BedPart::Head {
args.position
.offset(bed_props.facing.opposite().to_offset())
} else {
args.position.offset(bed_props.facing.to_offset())
};
args.world
.break_block(
&other_half_pos,
Some(args.player.clone()),
if args.player.gamemode.load() == GameMode::Creative {
BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_NEIGHBORS
} else {
BlockFlags::NOTIFY_NEIGHBORS
},
)
.await;
args.world
.break_block(
&other_half_pos,
Some(args.player.clone()),
if args.player.gamemode.load() == GameMode::Creative {
BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_NEIGHBORS
} else {
BlockFlags::NOTIFY_NEIGHBORS
},
)
.await;
})
}
#[allow(clippy::too_many_lines)]
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
let state_id = args.world.get_block_state_id(args.position).await;
let bed_props = BedProperties::from_state_id(state_id, args.block);
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let state_id = args.world.get_block_state_id(args.position).await;
let bed_props = BedProperties::from_state_id(state_id, args.block);
let (bed_head_pos, bed_foot_pos) = if bed_props.part == BedPart::Head {
(
*args.position,
args.position
.offset(bed_props.facing.opposite().to_offset()),
)
} else {
(
args.position.offset(bed_props.facing.to_offset()),
*args.position,
)
};
let (bed_head_pos, bed_foot_pos) = if bed_props.part == BedPart::Head {
(
*args.position,
args.position
.offset(bed_props.facing.opposite().to_offset()),
)
} else {
(
args.position.offset(bed_props.facing.to_offset()),
*args.position,
)
};
// Explode if not in the overworld
if args.world.dimension_type != VanillaDimensionType::Overworld {
args.world
.break_block(&bed_head_pos, None, BlockFlags::SKIP_DROPS)
.await;
args.world
.break_block(&bed_foot_pos, None, BlockFlags::SKIP_DROPS)
.await;
// Explode if not in the overworld
if args.world.dimension_type != VanillaDimensionType::Overworld {
args.world
.break_block(&bed_head_pos, None, BlockFlags::SKIP_DROPS)
.await;
args.world
.break_block(&bed_foot_pos, None, BlockFlags::SKIP_DROPS)
.await;
args.world
.explode(bed_head_pos.to_centered_f64(), 5.0)
.await;
args.world
.explode(bed_head_pos.to_centered_f64(), 5.0)
.await;
return BlockActionResult::SuccessServer;
}
return BlockActionResult::SuccessServer;
}
// Make sure the bed is not obstructed
if args
.world
.get_block_state(&bed_head_pos.up())
.await
.is_solid()
|| args
// Make sure the bed is not obstructed
if args
.world
.get_block_state(&bed_head_pos.up())
.await
.is_solid()
{
args.player
.send_system_message_raw(
&TextComponent::translate("block.minecraft.bed.obstructed", []),
true,
)
.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
.world
.get_block_state(&bed_head_pos.up())
.await
.is_solid()
{
args.player
.send_system_message_raw(
&TextComponent::translate("block.minecraft.bed.not_safe", []),
&TextComponent::translate("block.minecraft.bed.obstructed", []),
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;
// Make sure the bed is not occupied
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 async_trait::async_trait;
use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs};
use pumpkin_data::block_properties::{BlockProperties, FurnaceLikeProperties};
use pumpkin_macros::pumpkin_block;
use pumpkin_world::BlockStateId;
@ -7,16 +6,17 @@ use pumpkin_world::BlockStateId;
#[pumpkin_block("minecraft:blast_furnace")]
pub struct BlastFurnaceBlock;
#[async_trait]
impl BlockBehaviour for BlastFurnaceBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> 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)
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)
})
}
}

View File

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

@ -2,14 +2,12 @@ use std::sync::Arc;
use crate::{
block::{
blocks::candle_cakes::cake_from_candle,
registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs, UseWithItemArgs},
BlockBehaviour, BlockFuture, NormalUseArgs, UseWithItemArgs,
blocks::candle_cakes::cake_from_candle, registry::BlockActionResult,
},
entity::player::Player,
world::World,
};
use async_trait::async_trait;
use pumpkin_data::item::Item;
use pumpkin_data::{
Block,
@ -78,17 +76,56 @@ impl CakeBlock {
}
}
#[async_trait]
impl BlockBehaviour for CakeBlock {
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
let state_id = args.world.get_block_state_id(args.position).await;
let properties = CakeLikeProperties::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) => {
if properties.bites.to_index() != 0 {
fn use_with_item<'a>(
&'a self,
args: UseWithItemArgs<'a>,
) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let state_id = args.world.get_block_state_id(args.position).await;
let properties = CakeLikeProperties::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) => {
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(
args.world,
args.player,
@ -98,47 +135,15 @@ impl BlockBehaviour for CakeBlock {
)
.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 {
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
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let state_id = args.world.get_block_state_id(args.position).await;
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::{
Block, BlockDirection,
block_properties::{BlockProperties, CampfireLikeProperties},
@ -8,11 +7,8 @@ use pumpkin_world::{BlockStateId, tick::TickPriority};
use crate::{
block::{
BlockIsReplacing,
{
BlockBehaviour, BlockMetadata, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs,
OnPlaceArgs,
},
BlockBehaviour, BlockFuture, BlockIsReplacing, BlockMetadata,
GetStateForNeighborUpdateArgs, OnEntityCollisionArgs, OnPlaceArgs,
},
entity::EntityBase,
};
@ -29,53 +25,58 @@ impl BlockMetadata for CampfireBlock {
}
}
#[async_trait]
impl BlockBehaviour for CampfireBlock {
// TODO: cooking food on campfire (CampfireBlockEntity)
async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) {
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;
}
fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
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;
}
})
}
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let is_replacing_water = matches!(args.replacing, BlockIsReplacing::Water(_));
let mut props =
CampfireLikeProperties::from_state_id(args.block.default_state.id, args.block);
props.waterlogged = is_replacing_water;
props.signal_fire =
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 {
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let is_replacing_water = matches!(args.replacing, BlockIsReplacing::Water(_));
let mut props =
CampfireLikeProperties::from_state_id(args.block.default_state.id, args.block);
props.waterlogged = is_replacing_water;
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

View File

@ -1,6 +1,5 @@
use std::sync::Arc;
use async_trait::async_trait;
use pumpkin_data::{
Block,
item::Item,
@ -12,9 +11,8 @@ use pumpkin_world::{item::ItemStack, world::BlockFlags};
use crate::{
block::{
blocks::cake::CakeBlock,
BlockBehaviour, BlockFuture, NormalUseArgs, UseWithItemArgs, blocks::cake::CakeBlock,
registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs, UseWithItemArgs},
},
entity::player::Player,
world::World,
@ -100,19 +98,25 @@ impl CandleCakeBlock {
}
}
#[async_trait]
impl BlockBehaviour for CandleCakeBlock {
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
let item_id = args.item_stack.lock().await.item.id;
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,
}
fn use_with_item<'a>(
&'a self,
args: UseWithItemArgs<'a>,
) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let item_id = args.item_stack.lock().await.item.id;
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 {
Self::consume_and_drop_candle(args.block, args.player, args.position, args.world).await
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
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::{
BlockDirection,
@ -9,6 +8,7 @@ use pumpkin_data::{
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_world::{BlockStateId, world::BlockFlags};
use crate::block::BlockFuture;
use crate::{
block::{
BlockIsReplacing,
@ -24,99 +24,112 @@ use crate::{
#[pumpkin_block_from_tag("minecraft:candles")]
pub struct CandleBlock;
#[async_trait]
impl BlockBehaviour for CandleBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
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 =>
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
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);
}
args.world
.set_block_state(
args.position,
properties.to_state_id(args.block),
BlockFlags::NOTIFY_ALL,
)
.await;
BlockActionResult::Consume
return properties.to_state_id(args.block);
}
_ => {
if properties.lit {
properties.lit = false;
} else {
return BlockActionResult::Pass;
let mut properties = CandleLikeProperties::default(args.block);
properties.waterlogged = args.replacing.water_source();
properties.to_state_id(args.block)
})
}
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
.set_block_state(
args.position,
properties.to_state_id(args.block),
BlockFlags::NOTIFY_ALL,
)
.await;
BlockActionResult::Consume
args.world
.set_block_state(
args.position,
properties.to_state_id(args.block),
BlockFlags::NOTIFY_ALL,
)
.await;
BlockActionResult::Consume
}
}
}
})
}
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
let state_id = args.world.get_block_state_id(args.position).await;
let mut properties = CandleLikeProperties::from_state_id(state_id, args.block);
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let state_id = args.world.get_block_state_id(args.position).await;
let mut properties = CandleLikeProperties::from_state_id(state_id, args.block);
if properties.lit {
properties.lit = false;
}
if properties.lit {
properties.lit = false;
}
args.world
.set_block_state(
args.position,
properties.to_state_id(args.block),
BlockFlags::NOTIFY_ALL,
)
.await;
args.world
.set_block_state(
args.position,
properties.to_state_id(args.block),
BlockFlags::NOTIFY_ALL,
)
.await;
BlockActionResult::Consume
BlockActionResult::Consume
})
}
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
let (support_block, state) = args
.block_accessor
.get_block_and_state(&args.position.down())
.await;
!support_block.is_waterlogged(state.id) && state.is_center_solid(BlockDirection::Up)
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
let (support_block, state) = args
.block_accessor
.get_block_and_state(&args.position.down())
.await;
!support_block.is_waterlogged(state.id) && state.is_center_solid(BlockDirection::Up)
})
}
async fn can_update_at(&self, args: CanUpdateAtArgs<'_>) -> bool {
let b = args.world.get_block(args.position).await;
args.player.get_entity().pose.load() != EntityPose::Crouching
&& CandleLikeProperties::from_state_id(args.state_id, args.block).candles
!= Integer1To4::L4
&& args.block.id == b.id // only the same color can update
fn can_update_at<'a>(&'a self, args: CanUpdateAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
let b = args.world.get_block(args.position).await;
args.player.get_entity().pose.load() != EntityPose::Crouching
&& CandleLikeProperties::from_state_id(args.state_id, args.block).candles
!= Integer1To4::L4
&& args.block.id == b.id // only the same color can update
})
}
}

View File

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

View File

@ -1,11 +1,10 @@
use async_trait::async_trait;
use pumpkin_data::{
Block,
block_properties::{BlockProperties, WallTorchLikeProperties},
};
use pumpkin_world::BlockStateId;
use crate::block::{BlockBehaviour, BlockMetadata, OnPlaceArgs, PlacedArgs};
use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, OnPlaceArgs};
pub struct CarvedPumpkinBlock;
@ -19,18 +18,17 @@ impl BlockMetadata for CarvedPumpkinBlock {
}
}
#[async_trait]
impl BlockBehaviour for CarvedPumpkinBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut props = WallTorchLikeProperties::default(args.block);
props.facing = args
.player
.living_entity
.entity
.get_horizontal_facing()
.opposite();
props.to_state_id(args.block)
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut props = WallTorchLikeProperties::default(args.block);
props.facing = args
.player
.living_entity
.entity
.get_horizontal_facing()
.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 async_trait::async_trait;
use pumpkin_data::BlockDirection;
use pumpkin_data::block_properties::Axis;
use pumpkin_data::block_properties::BlockProperties;
@ -9,18 +9,19 @@ use pumpkin_world::BlockStateId;
#[pumpkin_block("minecraft:iron_chain")]
pub struct ChainBlock;
#[async_trait]
impl BlockBehaviour for ChainBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut props =
pumpkin_data::block_properties::IronChainLikeProperties::default(args.block);
props.r#waterlogged = args.replacing.water_source();
props.r#axis = match args.direction {
BlockDirection::East | BlockDirection::West => Axis::X,
BlockDirection::Up | BlockDirection::Down => Axis::Y,
BlockDirection::North | BlockDirection::South => Axis::Z,
};
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut props =
pumpkin_data::block_properties::IronChainLikeProperties::default(args.block);
props.r#waterlogged = args.replacing.water_source();
props.r#axis = match args.direction {
BlockDirection::East | BlockDirection::West => Axis::X,
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 crate::block::{
BlockMetadata, BrokenArgs, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs,
BlockFuture, BlockMetadata, BrokenArgs, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs,
PlacedArgs,
};
use crate::entity::EntityBase;
use crate::world::World;
@ -69,135 +70,147 @@ impl BlockMetadata for ChestBlock {
}
}
#[async_trait]
impl BlockBehaviour for ChestBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut chest_props = ChestLikeProperties::default(args.block);
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
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(
args.world,
args.player,
args.block,
args.position,
args.direction,
)
.await;
chest_props.facing = facing;
chest_props.r#type = r#type;
let (r#type, facing) = compute_chest_props(
args.world,
args.player,
args.block,
args.position,
args.direction,
)
.await;
chest_props.facing = facing;
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 {
// 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
fn on_synced_block_event<'a>(
&'a self,
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<'_>) {
let chest = ChestBlockEntity::new(*args.position);
args.world.add_block_entity(Arc::new(chest)).await;
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
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 connected_towards = match chest_props.r#type {
ChestType::Single => return,
ChestType::Left => chest_props.facing.rotate_clockwise(),
ChestType::Right => chest_props.facing.rotate_counter_clockwise(),
};
let chest_props = ChestLikeProperties::from_state_id(args.state_id, args.block);
let connected_towards = match chest_props.r#type {
ChestType::Single => return,
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(
args.world,
args.block,
args.position,
chest_props.facing,
connected_towards,
ChestType::Single,
)
.await
{
neighbor_props.r#type = chest_props.r#type.opposite();
if let Some(mut neighbor_props) = get_chest_properties_if_can_connect(
args.world,
args.block,
args.position,
chest_props.facing,
connected_towards,
ChestType::Single,
)
.await
{
neighbor_props.r#type = chest_props.r#type.opposite();
args.world
.set_block_state(
&args.position.offset(connected_towards.to_offset()),
neighbor_props.to_state_id(args.block),
BlockFlags::NOTIFY_LISTENERS,
)
.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)
args.world
.set_block_state(
&args.position.offset(connected_towards.to_offset()),
neighbor_props.to_state_id(args.block),
BlockFlags::NOTIFY_LISTENERS,
)
.await;
}
} else {
first_inventory
};
})
}
args.player
.open_handled_screen(&ChestScreenFactory(inventory))
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
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;
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<'_>) {
let chest_props = ChestLikeProperties::from_state_id(args.state.id, args.block);
let connected_towards = match chest_props.r#type {
ChestType::Single => return,
ChestType::Left => chest_props.facing.rotate_clockwise(),
ChestType::Right => chest_props.facing.rotate_counter_clockwise(),
};
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
let chest_props = ChestLikeProperties::from_state_id(args.state.id, args.block);
let connected_towards = match chest_props.r#type {
ChestType::Single => return,
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(
args.world,
args.block,
args.position,
chest_props.facing,
connected_towards,
chest_props.r#type.opposite(),
)
.await
{
neighbor_props.r#type = ChestType::Single;
if let Some(mut neighbor_props) = get_chest_properties_if_can_connect(
args.world,
args.block,
args.position,
chest_props.facing,
connected_towards,
chest_props.r#type.opposite(),
)
.await
{
neighbor_props.r#type = ChestType::Single;
args.world
.set_block_state(
&args.position.offset(connected_towards.to_offset()),
neighbor_props.to_state_id(args.block),
BlockFlags::NOTIFY_LISTENERS,
)
.await;
}
args.world
.set_block_state(
&args.position.offset(connected_towards.to_offset()),
neighbor_props.to_state_id(args.block),
BlockFlags::NOTIFY_LISTENERS,
)
.await;
}
})
}
}

View File

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

View File

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

View File

@ -2,16 +2,12 @@ use std::sync::Arc;
use crate::{
block::{
registry::BlockActionResult,
{
BlockBehaviour, GetComparatorOutputArgs, NormalUseArgs, OnScheduledTickArgs,
UseWithItemArgs,
},
BlockBehaviour, BlockFuture, GetComparatorOutputArgs, NormalUseArgs, OnScheduledTickArgs,
UseWithItemArgs, registry::BlockActionResult,
},
entity::{Entity, item::ItemEntity},
world::World,
};
use async_trait::async_trait;
use pumpkin_data::{
Block,
block_properties::{BlockProperties, ComposterLikeProperties, EnumVariants, Integer0To8},
@ -29,54 +25,79 @@ use uuid::Uuid;
#[pumpkin_block("minecraft:composter")]
pub struct ComposterBlock;
#[async_trait]
impl BlockBehaviour for ComposterBlock {
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
let state_id = args.world.get_block_state_id(args.position).await;
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
if props.get_level() == 8 {
self.clear_composter(args.world, args.position, state_id, args.block)
.await;
}
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let state_id = args.world.get_block_state_id(args.position).await;
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
if props.get_level() == 8 {
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 {
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)
fn use_with_item<'a>(
&'a self,
args: UseWithItemArgs<'a>,
) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let state_id = args.world.get_block_state_id(args.position).await;
let props = 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;
}
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;
args.world
.sync_world_event(WorldEvent::ComposterUsed, *args.position, 1)
.await;
}
BlockActionResult::Consume
args.world
.sync_world_event(WorldEvent::ComposterUsed, *args.position, 1)
.await;
}
BlockActionResult::Consume
})
}
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
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 == 7 {
self.update_level_composter(args.world, args.position, state_id, args.block, level + 1)
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
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 == 7 {
self.update_level_composter(
args.world,
args.position,
state_id,
args.block,
level + 1,
)
.await;
}
}
})
}
async fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option<u8> {
let props = ComposterLikeProperties::from_state_id(args.state.id, args.block);
Some(props.get_level())
fn get_comparator_output<'a>(
&'a self,
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::{BlockBehaviour, NormalUseArgs};
use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs};
use async_trait::async_trait;
use pumpkin_inventory::crafting::crafting_screen_handler::CraftingTableScreenHandler;
use pumpkin_inventory::player::player_inventory::PlayerInventory;
@ -12,14 +12,15 @@ use tokio::sync::Mutex;
#[pumpkin_block("minecraft:crafting_table")]
pub struct CraftingTableBlock;
#[async_trait]
impl BlockBehaviour for CraftingTableBlock {
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
args.player
.open_handled_screen(&CraftingTableScreenFactory)
.await;
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
args.player
.open_handled_screen(&CraftingTableScreenFactory)
.await;
BlockActionResult::Success
BlockActionResult::Success
})
}
}

View File

@ -1,9 +1,9 @@
use crate::block::BlockBehaviour;
use crate::block::BlockFuture;
use crate::block::CanPlaceAtArgs;
use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs;
use crate::block::OnScheduledTickArgs;
use async_trait::async_trait;
use pumpkin_data::Block;
use pumpkin_data::BlockDirection;
use pumpkin_macros::pumpkin_block;
@ -16,41 +16,48 @@ use pumpkin_world::world::BlockFlags;
#[pumpkin_block("minecraft:dirt_path")]
pub struct DirtPathBlock;
#[async_trait]
impl BlockBehaviour for DirtPathBlock {
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
// TODO: push up entities
args.world
.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 {
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
// TODO: push up entities
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;
}
args.state_id
})
}
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
can_place_at(args.block_accessor, args.position).await
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
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::HorizontalFacingExt;
use pumpkin_data::block_properties::Axis;
@ -20,6 +19,7 @@ use pumpkin_world::world::BlockFlags;
use std::sync::Arc;
use crate::block::BlockBehaviour;
use crate::block::BlockFuture;
use crate::block::CanPlaceAtArgs;
use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::NormalUseArgs;
@ -163,125 +163,136 @@ async fn get_hinge(
#[pumpkin_block_from_tag("minecraft:doors")]
pub struct DoorBlock;
#[async_trait]
impl BlockBehaviour for DoorBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let powered = block_receives_redstone_power(args.world, args.position).await
|| block_receives_redstone_power(args.world, &args.position.up()).await;
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
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 hinge = get_hinge(args.world, args.position, args.use_item_on, direction).await;
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 mut door_props = DoorProperties::default(args.block);
door_props.half = DoubleBlockHalf::Lower;
door_props.facing = direction;
door_props.hinge = hinge;
door_props.powered = powered;
door_props.open = powered;
let mut door_props = DoorProperties::default(args.block);
door_props.half = DoubleBlockHalf::Lower;
door_props.facing = direction;
door_props.hinge = hinge;
door_props.powered = 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 {
can_place_at(args.block_accessor, args.position).await
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 })
}
async fn placed(&self, args: PlacedArgs<'_>) {
let mut door_props = DoorProperties::from_state_id(args.state_id, args.block);
door_props.half = DoubleBlockHalf::Upper;
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
let mut door_props = DoorProperties::from_state_id(args.state_id, args.block);
door_props.half = DoubleBlockHalf::Upper;
args.world
.set_block_state(
&args.position.offset(BlockDirection::Up.to_offset()),
door_props.to_state_id(args.block),
BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK,
)
.await;
args.world
.set_block_state(
&args.position.offset(BlockDirection::Up.to_offset()),
door_props.to_state_id(args.block),
BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK,
)
.await;
})
}
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
if !can_open_door(args.block) {
return BlockActionResult::Pass;
}
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
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<'_>) {
let block_state = args.world.get_block_state(args.position).await;
let mut door_props = DoorProperties::from_state_id(block_state.id, args.block);
fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
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 {
DoubleBlockHalf::Upper => BlockDirection::Down,
DoubleBlockHalf::Lower => BlockDirection::Up,
};
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_half = match door_props.half {
DoubleBlockHalf::Upper => BlockDirection::Down,
DoubleBlockHalf::Lower => BlockDirection::Up,
};
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 powered = block_receives_redstone_power(args.world, args.position).await
|| block_receives_redstone_power(args.world, &other_pos).await;
let powered = block_receives_redstone_power(args.world, args.position).await
|| block_receives_redstone_power(args.world, &other_pos).await;
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);
door_props.powered = !door_props.powered;
other_door_props.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);
door_props.powered = !door_props.powered;
other_door_props.powered = door_props.powered;
if powered != door_props.open {
door_props.open = door_props.powered;
other_door_props.open = other_door_props.powered;
if powered != door_props.open {
door_props.open = 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
.play_block_sound(
get_sound(args.block, powered),
SoundCategory::Blocks,
*args.position,
.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;
}
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(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
let lv = DoorProperties::from_state_id(args.state_id, args.block).half;
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
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let lv = DoorProperties::from_state_id(args.state_id, args.block).half;
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
{
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;
}
} 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;
}
args.state_id
args.state_id
})
}
}

View File

@ -1,9 +1,9 @@
use std::sync::Arc;
use crate::block::BlockBehaviour;
use crate::block::BlockFuture;
use crate::block::OnEntityCollisionArgs;
use crate::block::PlacedArgs;
use async_trait::async_trait;
use pumpkin_macros::pumpkin_block;
use pumpkin_registry::VanillaDimensionType;
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")]
pub struct EndPortalBlock;
#[async_trait]
impl BlockBehaviour for EndPortalBlock {
async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) {
let world = if args.world.dimension_type == VanillaDimensionType::TheEnd {
args.server
.get_world_from_dimension(VanillaDimensionType::Overworld)
.await
} else {
args.server
.get_world_from_dimension(VanillaDimensionType::TheEnd)
.await
};
args.entity
.get_entity()
.try_use_portal(0, world, *args.position)
.await;
fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
let world = if args.world.dimension_type == VanillaDimensionType::TheEnd {
args.server
.get_world_from_dimension(VanillaDimensionType::Overworld)
.await
} else {
args.server
.get_world_from_dimension(VanillaDimensionType::TheEnd)
.await
};
args.entity
.get_entity()
.try_use_portal(0, world, *args.position)
.await;
})
}
async fn placed(&self, args: PlacedArgs<'_>) {
args.world
.add_block_entity(Arc::new(EndPortalBlockEntity::new(*args.position)))
.await;
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
args.world
.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_macros::pumpkin_block;
use pumpkin_world::BlockStateId;
use crate::{
block::{BlockBehaviour, OnPlaceArgs},
block::{BlockBehaviour, BlockFuture, OnPlaceArgs},
entity::EntityBase,
};
@ -13,12 +12,14 @@ type EndPortalFrameProperties = pumpkin_data::block_properties::EndPortalFrameLi
#[pumpkin_block("minecraft:end_portal_frame")]
pub struct EndPortalFrameBlock;
#[async_trait]
impl BlockBehaviour for EndPortalFrameBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut end_portal_frame_props = EndPortalFrameProperties::default(args.block);
end_portal_frame_props.facing = args.player.get_entity().get_horizontal_facing().opposite();
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
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 async_trait::async_trait;
use pumpkin_data::Block;
use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::block_properties::EndRodLikeProperties;
@ -9,25 +9,26 @@ use pumpkin_world::BlockStateId;
#[pumpkin_block("minecraft:end_rod")]
pub struct EndRodBlock;
#[async_trait]
impl BlockBehaviour for EndRodBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut props = EndRodLikeProperties::default(args.block);
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut props = EndRodLikeProperties::default(args.block);
let blockstate = args
.world
.get_block_state_id(&args.position.offset(args.direction.to_offset()))
.await;
let blockstate = args
.world
.get_block_state_id(&args.position.offset(args.direction.to_offset()))
.await;
if Block::from_state_id(blockstate).eq(args.block)
&& EndRodLikeProperties::from_state_id(blockstate, args.block).facing
== args.direction.to_facing().opposite()
{
props.facing = args.direction.to_facing();
} else {
props.facing = args.direction.to_facing().opposite();
}
if Block::from_state_id(blockstate).eq(args.block)
&& EndRodLikeProperties::from_state_id(blockstate, args.block).facing
== args.direction.to_facing().opposite()
{
props.facing = args.direction.to_facing();
} else {
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 crate::block::{
BlockBehaviour, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs,
BlockBehaviour, BlockFuture, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs,
registry::BlockActionResult,
};
use async_trait::async_trait;
@ -41,46 +41,56 @@ impl ScreenHandlerFactory for EnderChestScreenFactory {
#[pumpkin_block("minecraft:ender_chest")]
pub struct EnderChestBlock;
#[async_trait]
impl BlockBehaviour for EnderChestBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut props = LadderLikeProperties::default(args.block);
props.facing = args
.player
.living_entity
.entity
.get_horizontal_facing()
.opposite();
props.to_state_id(args.block)
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut props = LadderLikeProperties::default(args.block);
props.facing = args
.player
.living_entity
.entity
.get_horizontal_facing()
.opposite();
props.to_state_id(args.block)
})
}
async fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool {
// 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
fn on_synced_block_event<'a>(
&'a self,
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 {
if let Some(block_entity) = args.world.get_block_entity(args.position).await
&& let Some(block_entity) = block_entity
.as_any()
.downcast_ref::<EnderChestBlockEntity>()
{
let inventory = args.player.ender_chest_inventory();
inventory.set_tracker(block_entity.get_tracker()).await;
args.player
.open_handled_screen(&EnderChestScreenFactory(inventory.clone()))
.await;
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
if let Some(block_entity) = args.world.get_block_entity(args.position).await
&& let Some(block_entity) = block_entity
.as_any()
.downcast_ref::<EnderChestBlockEntity>()
{
let inventory = args.player.ender_chest_inventory();
inventory.set_tracker(block_entity.get_tracker()).await;
args.player
.open_handled_screen(&EnderChestScreenFactory(inventory.clone()))
.await;
// TODO: player.incrementStat(Stats.OPEN_ENDERCHEST);
// TODO: PiglinBrain.onGuardedBlockInteracted(serverWorld, player, true);
}
// TODO: player.incrementStat(Stats.OPEN_ENDERCHEST);
// TODO: PiglinBrain.onGuardedBlockInteracted(serverWorld, player, true);
}
BlockActionResult::Success
BlockActionResult::Success
})
}
async fn placed(&self, args: PlacedArgs<'_>) {
let block_entity = EnderChestBlockEntity::new(*args.position);
args.world.add_block_entity(Arc::new(block_entity)).await;
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
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::{
block::{
BlockBehaviour, BlockMetadata, GetStateForNeighborUpdateArgs, OnScheduledTickArgs,
PlacedArgs,
BlockBehaviour, BlockFuture, BlockMetadata, GetStateForNeighborUpdateArgs,
OnScheduledTickArgs, PlacedArgs,
},
entity::falling::FallingEntity,
};
use async_trait::async_trait;
use pumpkin_data::{
Block, BlockState,
tag::{self, Taggable},
@ -33,30 +32,36 @@ impl BlockMetadata for FallingBlock {
}
}
#[async_trait]
impl BlockBehaviour for FallingBlock {
async fn placed(&self, args: PlacedArgs<'_>) {
// TODO: make delay configurable
args.world
.schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal)
.await;
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
// TODO: make delay configurable
args.world
.schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal)
.await;
})
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
// TODO: make delay configurable
args.world
.schedule_block_tick(args.block, *args.position, 2, 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 {
// TODO: make delay configurable
args.world
.schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal)
.await;
args.state_id
})
}
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
let (block, state) = args.world.get_block_and_state(&args.position.down()).await;
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;
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
let (block, state) = args.world.get_block_and_state(&args.position.down()).await;
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;
})
}
}

View File

@ -1,13 +1,13 @@
use std::sync::Arc;
use crate::block::BlockBehaviour;
use crate::block::BlockFuture;
use crate::block::CanPlaceAtArgs;
use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs;
use crate::block::OnScheduledTickArgs;
use crate::block::RandomTickArgs;
use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::Block;
use pumpkin_data::BlockDirection;
use pumpkin_data::block_properties::BlockProperties;
@ -29,75 +29,55 @@ type FarmlandProperties = FarmlandLikeProperties;
#[pumpkin_block("minecraft:farmland")]
pub struct FarmlandBlock;
#[async_trait]
impl BlockBehaviour for FarmlandBlock {
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
// TODO: push up entities
args.world
.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;
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
// TODO: push up entities
args.world
.set_block_state(
args.position,
props.to_state_id(args.block),
BlockFlags::NOTIFY_NEIGHBORS,
Block::DIRT.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.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);
})
}
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
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 })
}
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
.set_block_state(
args.position,
@ -105,8 +85,37 @@ impl BlockBehaviour for FarmlandBlock {
BlockFlags::NOTIFY_NEIGHBORS,
)
.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 crate::block::BlockFuture;
use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::NormalUseArgs;
use crate::block::OnPlaceArgs;
use crate::entity::player::Player;
use async_trait::async_trait;
use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::tag;
use pumpkin_data::tag::RegistryKey;
@ -57,26 +57,31 @@ pub async fn toggle_fence_gate(
#[pumpkin_block_from_tag("minecraft:fence_gates")]
pub struct FenceGateBlock;
#[async_trait]
impl BlockBehaviour for FenceGateBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut fence_gate_props = FenceGateProperties::default(args.block);
fence_gate_props.facing = args.player.living_entity.entity.get_horizontal_facing();
fence_gate_props.to_state_id(args.block)
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut fence_gate_props = FenceGateProperties::default(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(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
let fence_props = is_in_wall(&args).await;
fence_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 fence_props = is_in_wall(&args).await;
fence_props.to_state_id(args.block)
})
}
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
toggle_fence_gate(args.world, args.position, args.player).await;
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
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::OnPlaceArgs;
use async_trait::async_trait;
use pumpkin_data::BlockDirection;
use pumpkin_data::BlockState;
use pumpkin_data::block_properties::BlockProperties;
@ -28,21 +28,24 @@ impl BlockMetadata for FenceBlock {
}
}
#[async_trait]
impl BlockBehaviour for FenceBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut fence_props = FenceProperties::default(args.block);
fence_props.waterlogged = args.replacing.water_source();
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
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(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
let fence_props = FenceProperties::from_state_id(args.state_id, args.block);
compute_fence_state(fence_props, args.world, args.block, args.position).await
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
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::atomic::Ordering;
use async_trait::async_trait;
use pumpkin_data::{Block, BlockDirection, BlockState};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
@ -17,7 +16,7 @@ use pumpkin_world::tick::TickPriority;
use crate::block::blocks::tnt::TNTBlock;
use crate::block::{
BlockBehaviour, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
BlockBehaviour, BlockFuture, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
OnEntityCollisionArgs, OnScheduledTickArgs, PlacedArgs,
};
use crate::world::World;
@ -165,144 +164,125 @@ impl FireBlock {
}
}
#[async_trait]
impl BlockBehaviour for FireBlock {
async fn placed(&self, args: PlacedArgs<'_>) {
if args.old_state_id == args.state_id {
// 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);
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
if args.old_state_id == args.state_id {
// Already a fire
return;
}
if base_entity.fire_ticks.load(Ordering::Relaxed) >= 0 {
base_entity.set_on_fire_for(8.0);
}
}
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
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)
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;
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 {
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
fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
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);
}
}
})
}
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)]
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
let (world, block, pos) = (args.world, args.block, args.position);
world
.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
{
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
let (world, block, pos) = (args.world, args.block, args.position);
world
.set_block_state(
pos,
Block::AIR.default_state.id,
BlockFlags::NOTIFY_NEIGHBORS,
.schedule_block_tick(
block,
*pos,
Self::get_fire_tick_delay() as u8,
TickPriority::Normal,
)
.await;
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;
let random = rand::rng().random_range(0..3) / 2;
let new_age = (age + random).min(15);
if new_age != age {
fire_props.age = EnumVariants::from_index(new_age);
let new_state_id = fire_props.to_state_id(&Block::FIRE);
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) {
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
.set_block_state(
pos,
@ -310,100 +290,132 @@ impl BlockBehaviour for FireBlock {
BlockFlags::NOTIFY_NEIGHBORS,
)
.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
&& rand::rng().random_range(0..4) == 0
&& !Self::is_flammable(world.get_block_state(&pos.down()).await)
{
world
.set_block_state(
pos,
Block::AIR.default_state.id,
BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
return;
}
let random = rand::rng().random_range(0..3) / 2;
let new_age = (age + random).min(15);
if new_age != age {
fire_props.age = EnumVariants::from_index(new_age);
let new_state_id = fire_props.to_state_id(&Block::FIRE);
world
.set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS)
.await;
}
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)
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
.set_block_state(
pos,
Block::AIR.default_state.id,
BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
}
return;
}
if age == 15
&& rand::rng().random_range(0..4) == 0
&& !Self::is_flammable(world.get_block_state(&pos.down()).await)
{
world
.set_block_state(
pos,
Block::AIR.default_state.id,
BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
return;
}
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;
Self.try_spreading_fire(
world,
&pos.offset(BlockDirection::Down.to_offset()),
250,
age,
)
.await;
for l in -1..=1 {
for m in -1..=1 {
for n in -1..=4 {
if l != 0 || n != 0 || m != 0 {
let offset_pos = pos.offset(Vector3::new(l, n, m));
let burn_chance = Self.get_burn_chance(world, &offset_pos).await;
if burn_chance > 0 {
let o = 100 + if n > 1 { (n - 1) * 100 } else { 0 };
let p: i32 = burn_chance
+ 40
+ (world.level_info.read().await.difficulty as i32) * 7
/ i32::from(age + 30);
for l in -1..=1 {
for m in -1..=1 {
for n in -1..=4 {
if l != 0 || n != 0 || m != 0 {
let offset_pos = pos.offset(Vector3::new(l, n, m));
let burn_chance = Self.get_burn_chance(world, &offset_pos).await;
if burn_chance > 0 {
let o = 100 + if n > 1 { (n - 1) * 100 } else { 0 };
let p: i32 = burn_chance
+ 40
+ (world.level_info.read().await.difficulty as i32) * 7
/ i32::from(age + 30);
if p > 0 && rand::rng().random_range(0..o) <= p {
let new_age = (age + rand::rng().random_range(0..5) / 4).min(15);
let fire_state_id =
self.get_state_for_position(world, block, &offset_pos).await;
let mut new_fire_props =
FireProperties::from_state_id(fire_state_id, &Block::FIRE);
new_fire_props.age = EnumVariants::from_index(new_age);
if p > 0 && rand::rng().random_range(0..o) <= p {
let new_age =
(age + rand::rng().random_range(0..5) / 4).min(15);
let fire_state_id = self
.get_state_for_position(world, block, &offset_pos)
.await;
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
world
.set_block_state(
&offset_pos,
new_fire_props.to_state_id(&Block::FIRE),
BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
//TODO drop items for burned blocks
world
.set_block_state(
&offset_pos,
new_fire_props.to_state_id(&Block::FIRE),
BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
}
}
}
}
}
}
}
})
}
async fn broken(&self, args: BrokenArgs<'_>) {
FireBlockBase::broken(args.world.clone(), *args.position).await;
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
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::{Block, tag};
use pumpkin_macros::pumpkin_block;
use pumpkin_world::BlockStateId;
use crate::block::{BlockBehaviour, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs};
use crate::block::{
BlockBehaviour, BlockFuture, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
};
use super::FireBlockBase;
@ -18,24 +19,29 @@ impl SoulFireBlock {
}
}
#[async_trait]
impl BlockBehaviour for SoulFireBlock {
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
if !Self::is_soul_base(args.world.get_block(&args.position.down()).await) {
return Block::AIR.default_state.id;
}
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
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 {
Self::is_soul_base(args.block_accessor.get_block(&args.position.down()).await)
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
Self::is_soul_base(args.block_accessor.get_block(&args.position.down()).await)
})
}
async fn broken(&self, args: BrokenArgs<'_>) {
FireBlockBase::broken(args.world.clone(), *args.position).await;
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
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::{BlockBehaviour, RandomTickArgs, UseWithItemArgs};
use async_trait::async_trait;
use crate::block::{BlockBehaviour, BlockFuture, RandomTickArgs, UseWithItemArgs};
use pumpkin_data::Block;
use pumpkin_data::flower_pot_transformations::get_potted_item;
use pumpkin_data::tag::{RegistryKey, get_tag_values};
@ -11,69 +10,75 @@ use pumpkin_world::world::BlockFlags;
#[pumpkin_block_from_tag("minecraft:flower_pots")]
pub struct FlowerPotBlock;
#[async_trait]
impl BlockBehaviour for FlowerPotBlock {
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
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 {
fn use_with_item<'a>(
&'a self,
args: UseWithItemArgs<'a>,
) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
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
.set_block_state(
args.position,
Block::from_id(potted_block_id).default_state.id,
Block::POTTED_OPEN_EYEBLOSSOM.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
}
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;
}
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_util::text::TextComponent;
use pumpkin_world::{
BlockStateId,
block::entities::{BlockEntity, furnace::FurnaceBlockEntity},
inventory::Inventory,
};
use tokio::sync::Mutex;
use crate::block::BlockBehaviour;
use crate::block::{
BlockBehaviour, BlockFuture, BrokenArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs,
registry::BlockActionResult,
};
struct FurnaceScreenFactory {
inventory: Arc<dyn Inventory>,
@ -55,109 +59,47 @@ impl ScreenHandlerFactory for FurnaceScreenFactory {
#[pumpkin_block("minecraft:furnace")]
pub struct FurnaceBlock;
#[async_trait]
impl BlockBehaviour for FurnaceBlock {
async fn normal_use(
&self,
args: crate::block::NormalUseArgs<'_>,
) -> crate::block::registry::BlockActionResult {
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
.open_handled_screen(&furnace_screen_factory)
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
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
.open_handled_screen(&furnace_screen_factory)
.await;
}
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;
}
crate::block::registry::BlockActionResult::Consume
})
}
//Same to normal_use
async fn use_with_item(
&self,
_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
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
args.world.remove_block_entity(args.position).await;
})
}
}

View File

@ -1,6 +1,6 @@
use crate::block::BlockFuture;
use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs;
use async_trait::async_trait;
use pumpkin_data::BlockDirection;
use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::tag::RegistryKey;
@ -26,21 +26,24 @@ impl BlockMetadata for GlassPaneBlock {
}
}
#[async_trait]
impl BlockBehaviour for GlassPaneBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut pane_props = GlassPaneProperties::default(args.block);
pane_props.waterlogged = args.replacing.water_source();
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
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(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
let pane_props = GlassPaneProperties::from_state_id(args.state_id, args.block);
compute_pane_state(pane_props, args.world, args.block, args.position).await
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
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 async_trait::async_trait;
use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, OnPlaceArgs};
use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties};
use pumpkin_data::tag::{RegistryKey, get_tag_values};
use pumpkin_world::BlockStateId;
@ -15,16 +14,17 @@ impl BlockMetadata for GlazedTerracottaBlock {
}
}
#[async_trait]
impl BlockBehaviour for GlazedTerracottaBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut prop = WallTorchLikeProperties::default(args.block);
prop.facing = args
.player
.living_entity
.entity
.get_horizontal_facing()
.opposite();
prop.to_state_id(args.block)
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut prop = WallTorchLikeProperties::default(args.block);
prop.facing = args
.player
.living_entity
.entity
.get_horizontal_facing()
.opposite();
prop.to_state_id(args.block)
})
}
}

View File

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

View File

@ -1,7 +1,7 @@
use std::sync::Arc;
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::{
registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs},
@ -47,52 +47,59 @@ pub struct HopperBlock;
type HopperLikeProperties = pumpkin_data::block_properties::HopperLikeProperties;
#[async_trait]
impl BlockBehaviour for HopperBlock {
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
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))
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
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))
.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;
}
BlockActionResult::Success
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_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
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)
}
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))
fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
check_powered_state(
args.world,
args.position,
args.world.get_block_state_id(args.position).await,
args.block,
)
.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::OnPlaceArgs;
use async_trait::async_trait;
use pumpkin_data::BlockDirection;
use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::tag::Taggable;
@ -17,21 +17,24 @@ use crate::world::World;
#[pumpkin_block("minecraft:iron_bars")]
pub struct IronBarsBlock;
#[async_trait]
impl BlockBehaviour for IronBarsBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut bars_props = IronBarsProperties::default(args.block);
bars_props.waterlogged = args.replacing.water_source();
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
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(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
let bars_props = IronBarsProperties::from_state_id(args.state_id, args.block);
compute_bars_state(bars_props, args.world, args.block, args.position).await
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
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 crate::block::registry::BlockActionResult;
use crate::block::{BlockBehaviour, BrokenArgs, UseWithItemArgs};
use crate::block::{BlockBehaviour, BlockFuture, BrokenArgs, UseWithItemArgs};
use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::data_component_impl::JukeboxPlayableImpl;
use pumpkin_data::world::WorldEvent;
use pumpkin_data::{
@ -45,56 +44,62 @@ impl JukeboxBlock {
}
}
#[async_trait]
impl BlockBehaviour for JukeboxBlock {
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
let world = &args.player.living_entity.entity.world;
fn use_with_item<'a>(
&'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 self.has_record(args.block, args.position, world).await {
self.stop_music(args.block, args.position, world).await;
return BlockActionResult::Success;
}
// if the jukebox already has a record, stop playing
if self.has_record(args.block, args.position, world).await {
self.stop_music(args.block, args.position, world).await;
return BlockActionResult::Success;
}
let jukebox_playable = args
.item_stack
.lock()
.await
.get_data_component::<JukeboxPlayableImpl>()
.map(|i| i.song);
let jukebox_playable = args
.item_stack
.lock()
.await
.get_data_component::<JukeboxPlayableImpl>()
.map(|i| i.song);
let Some(jukebox_playable) = jukebox_playable else {
return BlockActionResult::Pass;
};
let Some(jukebox_playable) = jukebox_playable else {
return BlockActionResult::Pass;
};
let Some(song) = jukebox_playable.split(':').nth(1) else {
return BlockActionResult::Pass;
};
let Some(song) = jukebox_playable.split(':').nth(1) else {
return BlockActionResult::Pass;
};
let Some(jukebox_song) = SYNCED_REGISTRIES.get_jukebox_song_index(song) else {
log::error!("Jukebox playable song not registered!");
return BlockActionResult::Pass;
};
let Some(jukebox_song) = SYNCED_REGISTRIES.get_jukebox_song_index(song) else {
log::error!("Jukebox playable song not registered!");
return BlockActionResult::Pass;
};
// TODO: Update block nbt
// TODO: Update block nbt
self.set_record(true, args.block, args.position, world)
.await;
world
.sync_world_event(
WorldEvent::JukeboxStartsPlaying,
*args.position,
jukebox_song as i32,
)
.await;
self.set_record(true, args.block, args.position, world)
.await;
world
.sync_world_event(
WorldEvent::JukeboxStartsPlaying,
*args.position,
jukebox_song as i32,
)
.await;
BlockActionResult::Success
BlockActionResult::Success
})
}
async fn broken(&self, args: BrokenArgs<'_>) {
// For now just stop the music at this position
args.world
.sync_world_event(WorldEvent::JukeboxStopsPlaying, *args.position, 0)
.await;
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
// For now just stop the music at this position
args.world
.sync_world_event(WorldEvent::JukeboxStopsPlaying, *args.position, 0)
.await;
})
}
}

View File

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

View File

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

View File

@ -1,5 +1,4 @@
use crate::block::{BlockBehaviour, OnPlaceArgs};
use async_trait::async_trait;
use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs};
use pumpkin_data::block_properties::{BlockProperties, LecternLikeProperties};
use pumpkin_macros::pumpkin_block;
use pumpkin_world::BlockStateId;
@ -7,16 +6,17 @@ use pumpkin_world::BlockStateId;
#[pumpkin_block("minecraft:lectern")]
pub struct LecternBlock;
#[async_trait]
impl BlockBehaviour for LecternBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut props = LecternLikeProperties::default(args.block);
props.facing = args
.player
.living_entity
.entity
.get_horizontal_facing()
.opposite();
props.to_state_id(args.block)
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut props = LecternLikeProperties::default(args.block);
props.facing = args
.player
.living_entity
.entity
.get_horizontal_facing()
.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::tag::{RegistryKey, get_tag_values};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_world::BlockStateId;
use crate::block::BlockBehaviour;
use crate::block::OnPlaceArgs;
use crate::block::{BlockBehaviour, BlockFuture};
type LogProperties = pumpkin_data::block_properties::PaleOakWoodLikeProperties;
#[pumpkin_block_from_tag("minecraft:logs")]
pub struct LogBlock;
#[async_trait]
impl BlockBehaviour for LogBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut log_props = LogProperties::default(args.block);
log_props.axis = args.direction.to_axis();
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
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 async_trait::async_trait;
use crate::block::{BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs};
use pumpkin_data::block_properties::{BlockProperties, MangroveRootsLikeProperties};
use pumpkin_data::fluid::Fluid;
use pumpkin_macros::pumpkin_block;
@ -9,29 +8,32 @@ use pumpkin_world::tick::TickPriority;
#[pumpkin_block("minecraft:mangrove_roots")]
pub struct MangroveRootsBlock;
#[async_trait]
impl BlockBehaviour for MangroveRootsBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut props = MangroveRootsLikeProperties::default(args.block);
props.waterlogged = args.replacing.water_source();
props.to_state_id(args.block)
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut props = MangroveRootsLikeProperties::default(args.block);
props.waterlogged = args.replacing.water_source();
props.to_state_id(args.block)
})
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
let props = MangroveRootsLikeProperties::from_state_id(args.state_id, args.block);
if props.waterlogged {
args.world
.schedule_fluid_tick(
&Fluid::WATER,
*args.position,
Fluid::WATER.flow_speed as u8,
TickPriority::Normal,
)
.await;
}
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 props = MangroveRootsLikeProperties::from_state_id(args.state_id, args.block);
if props.waterlogged {
args.world
.schedule_fluid_tick(
&Fluid::WATER,
*args.position,
Fluid::WATER.flow_speed as u8,
TickPriority::Normal,
)
.await;
}
props.to_state_id(args.block)
})
}
}

View File

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

View File

@ -1,9 +1,8 @@
use crate::block::registry::BlockActionResult;
use crate::block::{
GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs,
BlockFuture, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs,
UseWithItemArgs,
};
use async_trait::async_trait;
use pumpkin_data::block_properties::Axis;
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::{
@ -61,18 +60,40 @@ impl NoteBlock {
}
}
#[async_trait]
impl BlockBehaviour for NoteBlock {
async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) {
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 powered = block_receives_redstone_power(args.world, args.position).await;
// check if powered state changed
if note_props.powered != powered {
if powered {
Self::play_note(&note_props, args.world, args.position).await;
fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
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 powered = block_receives_redstone_power(args.world, args.position).await;
// check if powered state changed
if note_props.powered != powered {
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
.set_block_state(
args.position,
@ -80,83 +101,78 @@ impl BlockBehaviour for NoteBlock {
BlockFlags::NOTIFY_ALL,
)
.await;
}
Self::play_note(&note_props, args.world, args.position).await;
BlockActionResult::Success
})
}
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
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
.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
fn use_with_item<'a>(
&'a self,
_args: UseWithItemArgs<'a>,
) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
// TODO
BlockActionResult::PassToDefaultBlockAction
})
}
async fn use_with_item(&self, _args: UseWithItemArgs<'_>) -> BlockActionResult {
// TODO
BlockActionResult::PassToDefaultBlockAction
fn on_synced_block_event<'a>(
&'a self,
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 {
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_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(
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
Self::get_state_with_instrument(
args.world,
args.position,
args.state_id,
Block::NOTE_BLOCK.default_state.id,
args.block,
)
.await;
}
args.state_id
.await
})
}
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 async_trait::async_trait;
use pumpkin_data::{
Block, BlockDirection, BlockState, FacingExt,
block_properties::{
@ -17,11 +16,8 @@ use pumpkin_world::{
use crate::{
block::{
blocks::redstone::is_emitting_redstone_power,
{
BlockBehaviour, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs,
OnSyncedBlockEventArgs, PlacedArgs,
},
BlockBehaviour, BlockFuture, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs,
OnSyncedBlockEventArgs, PlacedArgs, blocks::redstone::is_emitting_redstone_power,
},
world::World,
};
@ -84,162 +80,172 @@ impl PistonBlock {
}
}
#[async_trait]
impl BlockBehaviour for PistonBlock {
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut props = PistonProps::default(args.block);
props.extended = false;
props.facing = args.player.living_entity.entity.get_facing().opposite();
props.to_state_id(args.block)
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut props = PistonProps::default(args.block);
props.extended = false;
props.facing = args.player.living_entity.entity.get_facing().opposite();
props.to_state_id(args.block)
})
}
async fn placed(&self, args: PlacedArgs<'_>) {
if args.old_state_id == args.state_id {
return;
}
try_move(args.world, args.block, args.position).await;
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
if args.old_state_id == args.state_id {
return;
}
try_move(args.world, args.block, args.position).await;
})
}
async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) {
try_move(args.world, args.block, args.position).await;
fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
try_move(args.world, args.block, args.position).await;
})
}
#[expect(clippy::too_many_lines)]
async fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool {
let (block, world, pos, r#type, data) = (
args.block,
args.world,
args.position,
args.r#type,
args.data,
);
fn on_synced_block_event<'a>(
&'a self,
args: OnSyncedBlockEventArgs<'a>,
) -> BlockFuture<'a, bool> {
Box::pin(async move {
let (block, world, pos, r#type, data) = (
args.block,
args.world,
args.position,
args.r#type,
args.data,
);
let state = world.get_block_state(pos).await;
let mut props = PistonProps::from_state_id(state.id, block);
let dir = props.facing.to_block_direction();
let state = world.get_block_state(pos).await;
let mut props = PistonProps::from_state_id(state.id, block);
let dir = props.facing.to_block_direction();
// I don't think this is optimal ?
let sticky = block == &Block::STICKY_PISTON;
// I don't think this is optimal ?
let sticky = block == &Block::STICKY_PISTON;
let should_extend = should_extend(world, pos, dir).await;
if should_extend && (r#type == 1 || r#type == 2) {
props.extended = true;
world
.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS)
.await;
return false;
}
// This may prevents when something happens in the one tick before this function got called
if !should_extend && r#type == 0 {
return false;
}
// Extend Piston
if r#type == 0 {
if !move_piston(world, dir, pos, true, sticky).await {
let should_extend = should_extend(world, pos, dir).await;
if should_extend && (r#type == 1 || r#type == 2) {
props.extended = true;
world
.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS)
.await;
return false;
}
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
.set_block_state(
pos,
props.to_state_id(block),
BlockFlags::NOTIFY_ALL | BlockFlags::MOVED,
props.to_state_id(&Block::MOVING_PISTON),
BlockFlags::FORCE_STATE,
)
.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
.set_block_state(
&extended_pos,
Block::AIR.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.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;
}
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, FacingExt};
use pumpkin_macros::pumpkin_block;
use pumpkin_world::world::BlockFlags;
use crate::block::BlockBehaviour;
use crate::block::BrokenArgs;
use crate::block::{BlockBehaviour, BlockFuture};
use super::piston::PistonProps;
@ -14,22 +13,23 @@ pub(crate) type MovingPistonProps = pumpkin_data::block_properties::MovingPiston
#[pumpkin_block("minecraft:moving_piston")]
pub struct PistonExtensionBlock;
#[async_trait]
impl BlockBehaviour for PistonExtensionBlock {
async fn broken(&self, args: BrokenArgs<'_>) {
let props = MovingPistonProps::from_state_id(args.state.id, &Block::MOVING_PISTON);
let pos = args
.position
.offset(props.facing.opposite().to_block_direction().to_offset());
let (new_block, new_state) = args.world.get_block_and_state_id(&pos).await;
if &Block::PISTON == new_block || &Block::STICKY_PISTON == new_block {
let props = PistonProps::from_state_id(new_state, new_block);
if props.extended {
// TODO: use player
args.world
.break_block(&pos, None, BlockFlags::SKIP_DROPS)
.await;
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
let props = MovingPistonProps::from_state_id(args.state.id, &Block::MOVING_PISTON);
let pos = args
.position
.offset(props.facing.opposite().to_block_direction().to_offset());
let (new_block, new_state) = args.world.get_block_and_state_id(&pos).await;
if &Block::PISTON == new_block || &Block::STICKY_PISTON == new_block {
let props = PistonProps::from_state_id(new_state, new_block);
if props.extended {
// TODO: use player
args.world
.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, FacingExt};
use pumpkin_macros::pumpkin_block;
use pumpkin_world::world::BlockFlags;
use crate::block::BlockBehaviour;
use crate::block::BrokenArgs;
use crate::block::{BlockBehaviour, BlockFuture};
use super::piston::PistonProps;
@ -14,22 +13,23 @@ pub(crate) type PistonHeadProperties = pumpkin_data::block_properties::PistonHea
#[pumpkin_block("minecraft:piston_head")]
pub struct PistonHeadBlock;
#[async_trait]
impl BlockBehaviour for PistonHeadBlock {
async fn broken(&self, args: BrokenArgs<'_>) {
let props = PistonHeadProperties::from_state_id(args.state.id, &Block::PISTON_HEAD);
let pos = args
.position
.offset(props.facing.opposite().to_block_direction().to_offset());
let (new_block, new_state) = args.world.get_block_and_state_id(&pos).await;
if &Block::PISTON == new_block || &Block::STICKY_PISTON == new_block {
let props = PistonProps::from_state_id(new_state, new_block);
if props.extended {
// TODO: use player
args.world
.break_block(&pos, None, BlockFlags::SKIP_DROPS)
.await;
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
let props = PistonHeadProperties::from_state_id(args.state.id, &Block::PISTON_HEAD);
let pos = args
.position
.offset(props.facing.opposite().to_block_direction().to_offset());
let (new_block, new_state) = args.world.get_block_and_state_id(&pos).await;
if &Block::PISTON == new_block || &Block::STICKY_PISTON == new_block {
let props = PistonProps::from_state_id(new_state, new_block);
if props.extended {
// TODO: use player
args.world
.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_world::BlockStateId;
use crate::block::{
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
blocks::plant::PlantBlockBase,
};
@ -19,23 +18,26 @@ impl BlockMetadata for BushBlock {
}
}
#[async_trait]
impl BlockBehaviour for BushBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
<Self as PlantBlockBase>::get_state_for_neighbor_update(
self,
args.world,
args.position,
args.state_id,
)
.await
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
<Self as PlantBlockBase>::get_state_for_neighbor_update(
self,
args.world,
args.position,
args.state_id,
)
.await
})
}
}

View File

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

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::{
Block,
block_properties::{
@ -9,7 +8,7 @@ use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{BlockStateId, world::BlockAccessor};
use crate::block::{
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
blocks::plant::PlantBlockBase,
};
@ -49,31 +48,34 @@ impl AttachedStemBlock {
}
}
#[async_trait]
impl BlockBehaviour for AttachedStemBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
let props = AttachedStemProperties::from_state_id(args.state_id, args.block);
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);
return props.to_state_id(Self::get_stem(args.block));
}
<Self as PlantBlockBase>::get_state_for_neighbor_update(
self,
args.world,
args.position,
args.state_id,
)
.await
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let props = AttachedStemProperties::from_state_id(args.state_id, args.block);
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);
return props.to_state_id(Self::get_stem(args.block));
}
<Self as PlantBlockBase>::get_state_for_neighbor_update(
self,
args.world,
args.position,
args.state_id,
)
.await
})
}
}

View File

@ -1,8 +1,8 @@
use crate::block::{
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs,
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
RandomTickArgs,
blocks::plant::{PlantBlockBase, crop::get_available_moisture},
};
use async_trait::async_trait;
use pumpkin_data::{
Block, BlockDirection,
block_properties::{
@ -63,66 +63,75 @@ impl StemBlock {
}
}
#[async_trait]
impl BlockBehaviour for StemBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
<Self as PlantBlockBase>::get_state_for_neighbor_update(
self,
args.world,
args.position,
args.state_id,
)
.await
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
<Self as PlantBlockBase>::get_state_for_neighbor_update(
self,
args.world,
args.position,
args.state_id,
)
.await
})
}
async fn random_tick(&self, args: RandomTickArgs<'_>) {
// TODO add light level check
let f: f32 = get_available_moisture(args.world, args.position, args.block).await;
if rand::rng().random_range(0..=(25.0 / f).floor() as i32) == 0 {
let (block, state) = args.world.get_block_and_state_id(args.position).await;
let props = StemProperties::from_state_id(state, block);
let age = i32::from(props.age.to_index());
if age < 7 {
args.world
.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);
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
// TODO add light level check
let f: f32 = get_available_moisture(args.world, args.position, args.block).await;
if rand::rng().random_range(0..=(25.0 / f).floor() as i32) == 0 {
let (block, state) = args.world.get_block_and_state_id(args.position).await;
let props = StemProperties::from_state_id(state, block);
let age = i32::from(props.age.to_index());
if age < 7 {
args.world
.set_block_state(
&plant_block_pos,
gourd.default_state.id,
args.position,
Self::state_with_age(block, state, age + 1),
BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
args.world
.set_block_state(args.position, attached_stem, 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
.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_world::BlockStateId;
use crate::block::blocks::plant::PlantBlockBase;
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")]
pub struct PotatoBlock;
#[async_trait]
impl BlockBehaviour for PotatoBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
<Self as CropBlockBase>::can_plant_on_top(self, args.block_accessor, &args.position.down())
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
<Self as CropBlockBase>::can_plant_on_top(
self,
args.block_accessor,
&args.position.down(),
)
.await
})
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
<Self as PlantBlockBase>::get_state_for_neighbor_update(
self,
args.world,
args.position,
args.state_id,
)
.await
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
<Self as PlantBlockBase>::get_state_for_neighbor_update(
self,
args.world,
args.position,
args.state_id,
)
.await
})
}
async fn random_tick(&self, args: RandomTickArgs<'_>) {
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
})
}
}

View File

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

View File

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

View File

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

View File

@ -1,10 +1,9 @@
use async_trait::async_trait;
use pumpkin_data::{Block, BlockDirection};
use pumpkin_world::BlockStateId;
use crate::block::{
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs,
OnPlaceArgs,
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, CanUpdateAtArgs,
GetStateForNeighborUpdateArgs, OnPlaceArgs,
};
use super::segmented::Segmented;
@ -23,35 +22,38 @@ impl BlockMetadata for LeafLitterBlock {
}
}
#[async_trait]
impl BlockBehaviour for LeafLitterBlock {
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
let block_below = args
.block_accessor
.get_block_state(&args.position.down())
.await;
block_below.is_side_solid(BlockDirection::Up)
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
let block_below = args
.block_accessor
.get_block_state(&args.position.down())
.await;
block_below.is_side_solid(BlockDirection::Up)
})
}
async fn can_update_at(&self, args: CanUpdateAtArgs<'_>) -> bool {
Segmented::can_update_at(self, args).await
fn can_update_at<'a>(&'a self, args: CanUpdateAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move { Segmented::can_update_at(self, args).await })
}
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
Segmented::on_place(self, args).await
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move { Segmented::on_place(self, args).await })
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
if args.direction == BlockDirection::Down {
let block_below_state = args.world.get_block_state(&args.position.down()).await;
if !block_below_state.is_side_solid(BlockDirection::Up) {
return Block::AIR.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::Down {
let block_below_state = args.world.get_block_state(&args.position.down()).await;
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_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
@ -7,45 +6,50 @@ use pumpkin_world::{
world::{BlockAccessor, BlockFlags},
};
use crate::block::{GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase};
use crate::block::{BlockFuture, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase};
use crate::block::{BlockBehaviour, CanPlaceAtArgs, OnEntityCollisionArgs};
#[pumpkin_block("minecraft:lily_pad")]
pub struct LilyPadBlock;
#[async_trait]
impl BlockBehaviour for LilyPadBlock {
async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) {
// Proberbly not the best solution, but works
if args
.entity
.get_entity()
.entity_type
.resource_name
.ends_with("_boat")
{
args.world
.break_block(args.position, None, BlockFlags::empty())
.await;
}
fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
// Proberbly not the best solution, but works
if args
.entity
.get_entity()
.entity_type
.resource_name
.ends_with("_boat")
{
args.world
.break_block(args.position, None, BlockFlags::empty())
.await;
}
})
}
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
}
async fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
<Self as PlantBlockBase>::get_state_for_neighbor_update(
self,
args.world,
args.position,
args.state_id,
)
.await
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
<Self as PlantBlockBase>::get_state_for_neighbor_update(
self,
args.world,
args.position,
args.state_id,
)
.await
})
}
}

View File

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

View File

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

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