diff --git a/pumpkin-data/build/block.rs b/pumpkin-data/build/block.rs index f1e916427..81139a158 100644 --- a/pumpkin-data/build/block.rs +++ b/pumpkin-data/build/block.rs @@ -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::>(); + 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}"), } } - } }); } diff --git a/pumpkin-inventory/src/crafting/crafting_inventory.rs b/pumpkin-inventory/src/crafting/crafting_inventory.rs index b1ad9ad52..8b482b27c 100644 --- a/pumpkin-inventory/src/crafting/crafting_inventory.rs +++ b/pumpkin-inventory/src/crafting/crafting_inventory.rs @@ -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 + Send + '_>> { + Box::pin(async move { + for item in self.items.iter() { + *item.lock().await = ItemStack::EMPTY.clone(); + } + }) } } diff --git a/pumpkin-inventory/src/crafting/crafting_screen_handler.rs b/pumpkin-inventory/src/crafting/crafting_screen_handler.rs index c7ffc4914..f415c1eb3 100644 --- a/pumpkin-inventory/src/crafting/crafting_screen_handler.rs +++ b/pumpkin-inventory/src/crafting/crafting_screen_handler.rs @@ -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 + 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: RecipeFinderScreenHandler + ScreenHandler { - async fn add_recipe_slots(&mut self, crafing_inventory: Arc) { - 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, + ) -> Pin + 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() + }) } } diff --git a/pumpkin-inventory/src/double.rs b/pumpkin-inventory/src/double.rs index d0c604335..88549214a 100644 --- a/pumpkin-inventory/src/double.rs +++ b/pumpkin-inventory/src/double.rs @@ -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 + Send + '_>> { + Box::pin(async move { + self.first.clear().await; + self.second.clear().await; + }) } } diff --git a/pumpkin-inventory/src/furnace/furnace_screen_handler.rs b/pumpkin-inventory/src/furnace/furnace_screen_handler.rs index 3c9ede651..aa70e4e87 100644 --- a/pumpkin-inventory/src/furnace/furnace_screen_handler.rs +++ b/pumpkin-inventory/src/furnace/furnace_screen_handler.rs @@ -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 + 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 + }) } } diff --git a/pumpkin-inventory/src/generic_container_screen_handler.rs b/pumpkin-inventory/src/generic_container_screen_handler.rs index 8a1fe04e2..30f6e7543 100644 --- a/pumpkin-inventory/src/generic_container_screen_handler.rs +++ b/pumpkin-inventory/src/generic_container_screen_handler.rs @@ -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 + }) } } diff --git a/pumpkin-inventory/src/player/ender_chest_inventory.rs b/pumpkin-inventory/src/player/ender_chest_inventory.rs index 882aaa224..5d8a6845c 100644 --- a/pumpkin-inventory/src/player/ender_chest_inventory.rs +++ b/pumpkin-inventory/src/player/ender_chest_inventory.rs @@ -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 + Send + '_>> { + Box::pin(async move { + for item in self.items.iter() { + *item.lock().await = ItemStack::EMPTY.clone(); + } + }) } } diff --git a/pumpkin-inventory/src/player/player_inventory.rs b/pumpkin-inventory/src/player/player_inventory.rs index 086e3db5b..54970e92f 100644 --- a/pumpkin-inventory/src/player/player_inventory.rs +++ b/pumpkin-inventory/src/player/player_inventory.rs @@ -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 + 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(); + }) } } diff --git a/pumpkin-inventory/src/player/player_screen_handler.rs b/pumpkin-inventory/src/player/player_screen_handler.rs index d8550bb2c..5cd5ded7d 100644 --- a/pumpkin-inventory/src/player/player_screen_handler.rs +++ b/pumpkin-inventory/src/player/player_screen_handler.rs @@ -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::() - .map_or(&EquipmentSlot::MAIN_HAND, |equippable| equippable.slot); + let equipment_slot = slot_stack + .get_data_component::() + .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() + }) } } diff --git a/pumpkin-inventory/src/screen_handler.rs b/pumpkin-inventory/src/screen_handler.rs index eb03c4813..b47ce365b 100644 --- a/pumpkin-inventory/src/screen_handler.rs +++ b/pumpkin-inventory/src/screen_handler.rs @@ -26,9 +26,9 @@ use pumpkin_world::{ block::entities::PropertyDelegate, inventory::{ComparableInventory, Inventory}, }; -use std::cmp::max; use std::sync::atomic::{AtomicU32, Ordering}; use std::{any::Any, collections::HashMap, sync::Arc}; +use std::{cmp::max, pin::Pin}; use tokio::sync::Mutex; const SLOT_INDEX_OUTSIDE: i32 = -999; @@ -64,18 +64,36 @@ impl ScreenProperty { } } -#[async_trait] +pub type PlayerFuture<'a, T> = Pin + Send + 'a>>; + pub trait InventoryPlayer: Send + Sync { - async fn drop_item(&self, item: ItemStack, retain_ownership: bool); + fn drop_item<'a>(&'a self, item: ItemStack, retain_ownership: bool) -> PlayerFuture<'a, ()>; fn get_inventory(&self) -> Arc; fn has_infinite_materials(&self) -> bool; - async fn enqueue_inventory_packet(&self, packet: &CSetContainerContent); - async fn enqueue_slot_packet(&self, packet: &CSetContainerSlot); - async fn enqueue_cursor_packet(&self, packet: &CSetCursorItem); - async fn enqueue_property_packet(&self, packet: &CSetContainerProperty); - async fn enqueue_slot_set_packet(&self, packet: &CSetPlayerInventory); - async fn enqueue_set_held_item_packet(&self, packet: &CSetSelectedSlot); - async fn enqueue_equipment_change(&self, slot: &EquipmentSlot, stack: &ItemStack); + + fn enqueue_inventory_packet<'a>( + &'a self, + packet: &'a CSetContainerContent, + ) -> PlayerFuture<'a, ()>; + fn enqueue_slot_packet<'a>(&'a self, packet: &'a CSetContainerSlot) -> PlayerFuture<'a, ()>; + fn enqueue_cursor_packet<'a>(&'a self, packet: &'a CSetCursorItem) -> PlayerFuture<'a, ()>; + fn enqueue_property_packet<'a>( + &'a self, + packet: &'a CSetContainerProperty, + ) -> PlayerFuture<'a, ()>; + fn enqueue_slot_set_packet<'a>( + &'a self, + packet: &'a CSetPlayerInventory, + ) -> PlayerFuture<'a, ()>; + fn enqueue_set_held_item_packet<'a>( + &'a self, + packet: &'a CSetSelectedSlot, + ) -> PlayerFuture<'a, ()>; + fn enqueue_equipment_change<'a>( + &'a self, + slot: &'a EquipmentSlot, + stack: &'a ItemStack, + ) -> PlayerFuture<'a, ()>; } pub async fn offer_or_drop_stack(player: &dyn InventoryPlayer, stack: ItemStack) { @@ -86,11 +104,19 @@ pub async fn offer_or_drop_stack(player: &dyn InventoryPlayer, stack: ItemStack) .await; } +pub type ScreenHandlerFuture<'a, T> = Pin + Send + 'a>>; + +// Future returning ItemStack (used by quick_move) +pub type ItemStackFuture<'a> = ScreenHandlerFuture<'a, ItemStack>; + +// Future returning Option +pub type OptionUsizeFuture<'a> = ScreenHandlerFuture<'a, Option>; + //ScreenHandler.java // TODO: Fully implement this -#[async_trait] pub trait ScreenHandler: Send + Sync { - /// Get the window type of the screen handler, otherwise panics + // --- Synchronous Methods (Unchanged) --- + fn window_type(&self) -> Option { self.get_behaviour().window_type } @@ -101,28 +127,10 @@ pub trait ScreenHandler: Send + Sync { self.get_behaviour().sync_id } - async fn on_closed(&mut self, player: &dyn InventoryPlayer) { - self.default_on_closed(player).await; - } - - async fn default_on_closed(&mut self, player: &dyn InventoryPlayer) { - let behaviour = self.get_behaviour_mut(); - if !behaviour.cursor_stack.lock().await.is_empty() { - offer_or_drop_stack(player, behaviour.cursor_stack.lock().await.clone()).await; - *behaviour.cursor_stack.lock().await = ItemStack::EMPTY.clone(); - } - } - fn can_use(&self, _player: &dyn InventoryPlayer) -> bool { true } - async fn drop_inventory(&self, player: &dyn InventoryPlayer, inventory: Arc) { - for i in 0..inventory.size() { - offer_or_drop_stack(player, inventory.remove_stack(i).await).await; - } - } - fn get_behaviour(&self) -> &ScreenHandlerBehaviour; fn get_behaviour_mut(&mut self) -> &mut ScreenHandlerBehaviour; @@ -159,37 +167,6 @@ pub trait ScreenHandler: Send + Sync { self.add_player_hotbar_slots(player_inventory); } - async fn copy_shared_slots(&mut self, other: Arc>) { - let mut table: HashMap> = HashMap::new(); - let other_binding = other.lock().await; - let other_behaviour = other_binding.get_behaviour(); - - for i in 0..other_behaviour.slots.len() { - let other_slot = other_behaviour.slots[i].clone(); - let mut hash_map = HashMap::new(); - hash_map.insert(other_slot.get_index(), i); - table.insert( - ComparableInventory(other_slot.get_inventory().clone()), - hash_map, - ); - } - - for i in 0..self.get_behaviour().slots.len() { - let slot = self.get_behaviour().slots[i].clone(); - let inventory = slot.get_inventory(); - let index = slot.get_index(); - - if let Some(hash_map) = table.get(&ComparableInventory(inventory.clone())) { - if let Some(other_index) = hash_map.get(&index) { - self.get_behaviour_mut().tracked_stacks[i] = - other_behaviour.tracked_stacks[*other_index].clone(); - self.get_behaviour_mut().previous_tracked_stacks[i] = - other_behaviour.previous_tracked_stacks[*other_index].clone(); - } - } - } - } - fn set_received_hash(&mut self, slot: usize, hash: OptionalItemStackHash) { let behaviour = self.get_behaviour_mut(); if slot < behaviour.previous_tracked_stacks.len() { @@ -213,52 +190,6 @@ pub trait ScreenHandler: Send + Sync { behaviour.previous_cursor_stack.set_received_hash(hash); } - async fn sync_state(&mut self) { - let behaviour = self.get_behaviour_mut(); - let mut previous_tracked_stacks = Vec::new(); - - for i in 0..behaviour.slots.len() { - let stack = behaviour.slots[i].get_cloned_stack().await; - previous_tracked_stacks.push(stack.clone()); - behaviour.previous_tracked_stacks[i].set_received_stack(stack); - } - - let cursor_stack = behaviour.cursor_stack.lock().await.clone(); - behaviour - .previous_cursor_stack - .set_received_stack(cursor_stack.clone()); - - for i in 0..behaviour.properties.len() { - let property_val = behaviour.properties[i].get(); - behaviour.tracked_property_values[i] = property_val; - } - - let next_revision = behaviour.next_revision(); - - if let Some(sync_handler) = behaviour.sync_handler.as_ref() { - sync_handler - .update_state( - behaviour, - &previous_tracked_stacks, - &cursor_stack, - behaviour.tracked_property_values.clone(), - next_revision, - ) - .await; - } - } - - async fn add_listener(&mut self, listener: Arc) { - self.get_behaviour_mut().listeners.push(listener); - self.send_content_updates().await; - } - - async fn update_sync_handler(&mut self, sync_handler: Arc) { - let behaviour = self.get_behaviour_mut(); - behaviour.sync_handler = Some(sync_handler.clone()); - self.sync_state().await; - } - fn add_property(&mut self, property: ScreenProperty) { let behaviour = self.get_behaviour_mut(); behaviour.properties.push(property); @@ -271,606 +202,803 @@ pub trait ScreenHandler: Send + Sync { } } - async fn update_to_client(&mut self) { - for i in 0..self.get_behaviour().slots.len() { + // --- Asynchronous Methods (Refactored) --- + + fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + self.default_on_closed(player).await; + }) + } + + fn default_on_closed<'a>( + &'a mut self, + player: &'a dyn InventoryPlayer, + ) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { let behaviour = self.get_behaviour_mut(); - let slot = behaviour.slots[i].clone(); - let stack = slot.get_cloned_stack().await; - self.update_tracked_slot(i, stack).await; - } - let behaviour = self.get_behaviour_mut(); - let mut prop_vec = vec![]; - for (idx, prop) in behaviour.properties.iter_mut().enumerate() { - let value = prop.get(); - if prop.has_changed() { - prop_vec.push((idx, value)); + // Lock and clone are performed inside the async block + let mut cursor_stack_lock = behaviour.cursor_stack.lock().await; + + if !cursor_stack_lock.is_empty() { + offer_or_drop_stack(player, cursor_stack_lock.clone()).await; + *cursor_stack_lock = ItemStack::EMPTY.clone(); } - } - - for (idx, value) in prop_vec { - self.update_tracked_properties(idx as i32, value).await; - self.check_property_updates(idx as i32, value).await; - } - - self.sync_state().await; + }) } - async fn update_tracked_properties(&mut self, idx: i32, value: i32) { - let behaviour = self.get_behaviour_mut(); - if idx <= behaviour.tracked_property_values.len() as i32 { - behaviour.tracked_property_values[idx as usize] = value; - for listener in behaviour.listeners.iter() { - listener - .on_property_update(behaviour, idx as u8, value) - .await; + fn drop_inventory<'a>( + &'a self, + player: &'a dyn InventoryPlayer, + inventory: Arc, + ) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + for i in 0..inventory.size() { + offer_or_drop_stack(player, inventory.remove_stack(i).await).await; } - } + }) } - async fn check_property_updates(&mut self, idx: i32, value: i32) { - let behaviour = self.get_behaviour_mut(); - if !behaviour.disable_sync { - if let Some(old_value) = behaviour.tracked_property_values.get(idx as usize) { - let old_value = *old_value; - if old_value != value { - behaviour - .tracked_property_values - .insert(idx as usize, value); - if let Some(ref sync_handler) = behaviour.sync_handler { - sync_handler.update_property(behaviour, idx, value).await; + fn copy_shared_slots<'a>( + &'a mut self, + other: Arc>, + ) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + let mut table: HashMap> = HashMap::new(); + let other_binding = other.lock().await; + let other_behaviour = other_binding.get_behaviour(); + + for i in 0..other_behaviour.slots.len() { + let other_slot = other_behaviour.slots[i].clone(); + let mut hash_map = HashMap::new(); + hash_map.insert(other_slot.get_index(), i); + table.insert( + ComparableInventory(other_slot.get_inventory().clone()), + hash_map, + ); + } + + for i in 0..self.get_behaviour().slots.len() { + let slot = self.get_behaviour().slots[i].clone(); + let inventory = slot.get_inventory(); + let index = slot.get_index(); + + if let Some(hash_map) = table.get(&ComparableInventory(inventory.clone())) { + if let Some(other_index) = hash_map.get(&index) { + self.get_behaviour_mut().tracked_stacks[i] = + other_behaviour.tracked_stacks[*other_index].clone(); + self.get_behaviour_mut().previous_tracked_stacks[i] = + other_behaviour.previous_tracked_stacks[*other_index].clone(); } } } - } + }) } - async fn update_tracked_slot(&mut self, slot: usize, stack: ItemStack) { - let behaviour = self.get_behaviour_mut(); - let other_stack = &behaviour.tracked_stacks[slot]; - if !other_stack.are_equal(&stack) { - behaviour.tracked_stacks[slot] = stack.clone(); + fn sync_state<'a>(&'a mut self) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + let behaviour = self.get_behaviour_mut(); + let mut previous_tracked_stacks = Vec::new(); - for listener in behaviour.listeners.iter() { - listener - .on_slot_update(behaviour, slot as u8, stack.clone()) + for i in 0..behaviour.slots.len() { + let stack = behaviour.slots[i].get_cloned_stack().await; + previous_tracked_stacks.push(stack.clone()); + behaviour.previous_tracked_stacks[i].set_received_stack(stack); + } + + let cursor_stack = behaviour.cursor_stack.lock().await.clone(); + behaviour + .previous_cursor_stack + .set_received_stack(cursor_stack.clone()); + + for i in 0..behaviour.properties.len() { + let property_val = behaviour.properties[i].get(); + behaviour.tracked_property_values[i] = property_val; + } + + let next_revision = behaviour.next_revision(); + + if let Some(sync_handler) = behaviour.sync_handler.as_ref() { + sync_handler + .update_state( + behaviour, + &previous_tracked_stacks, + &cursor_stack, + behaviour.tracked_property_values.clone(), + next_revision, + ) .await; } - } + }) } - async fn check_slot_updates(&mut self, slot: usize, stack: ItemStack) { - let behaviour = self.get_behaviour_mut(); - if !behaviour.disable_sync { - let prev_stack = &mut behaviour.previous_tracked_stacks[slot]; + fn add_listener<'a>( + &'a mut self, + listener: Arc, + ) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + self.get_behaviour_mut().listeners.push(listener); + self.send_content_updates().await; + }) + } - if !prev_stack.is_in_sync(&stack) { - prev_stack.set_received_stack(stack.clone()); - let next_revision = behaviour.next_revision(); - if let Some(sync_handler) = behaviour.sync_handler.as_ref() { - sync_handler - .update_slot(behaviour, slot, &stack, next_revision) + fn update_sync_handler<'a>( + &'a mut self, + sync_handler: Arc, + ) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + let behaviour = self.get_behaviour_mut(); + behaviour.sync_handler = Some(sync_handler.clone()); + self.sync_state().await; + }) + } + + fn update_to_client<'a>(&'a mut self) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + for i in 0..self.get_behaviour().slots.len() { + let behaviour = self.get_behaviour_mut(); + let slot = behaviour.slots[i].clone(); + let stack = slot.get_cloned_stack().await; + self.update_tracked_slot(i, stack).await; + } + + let behaviour = self.get_behaviour_mut(); + let mut prop_vec = vec![]; + for (idx, prop) in behaviour.properties.iter_mut().enumerate() { + let value = prop.get(); + if prop.has_changed() { + prop_vec.push((idx, value)); + } + } + + for (idx, value) in prop_vec { + self.update_tracked_properties(idx as i32, value).await; + self.check_property_updates(idx as i32, value).await; + } + + self.sync_state().await; + }) + } + + fn update_tracked_properties<'a>( + &'a mut self, + idx: i32, + value: i32, + ) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + let behaviour = self.get_behaviour_mut(); + if idx <= behaviour.tracked_property_values.len() as i32 { + behaviour.tracked_property_values[idx as usize] = value; + for listener in behaviour.listeners.iter() { + listener + .on_property_update(behaviour, idx as u8, value) .await; } } - } + }) } - async fn check_cursor_stack_updates(&mut self) { - let behaviour = self.get_behaviour_mut(); - if !behaviour.disable_sync { - let cursor_stack = behaviour.cursor_stack.lock().await; - if !behaviour.previous_cursor_stack.is_in_sync(&cursor_stack) { - behaviour - .previous_cursor_stack - .set_received_stack(cursor_stack.clone()); - if let Some(sync_handler) = behaviour.sync_handler.as_ref() { - sync_handler - .update_cursor_stack(behaviour, &cursor_stack) + fn check_property_updates<'a>( + &'a mut self, + idx: i32, + value: i32, + ) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + let behaviour = self.get_behaviour_mut(); + if !behaviour.disable_sync { + if let Some(old_value) = behaviour.tracked_property_values.get(idx as usize) { + let old_value = *old_value; + if old_value != value { + behaviour + .tracked_property_values + .insert(idx as usize, value); + if let Some(ref sync_handler) = behaviour.sync_handler { + sync_handler.update_property(behaviour, idx, value).await; + } + } + } + } + }) + } + + fn update_tracked_slot<'a>( + &'a mut self, + slot: usize, + stack: ItemStack, + ) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + let behaviour = self.get_behaviour_mut(); + let other_stack = &behaviour.tracked_stacks[slot]; + if !other_stack.are_equal(&stack) { + behaviour.tracked_stacks[slot] = stack.clone(); + + for listener in behaviour.listeners.iter() { + listener + .on_slot_update(behaviour, slot as u8, stack.clone()) .await; } } - } + }) } - async fn send_content_updates(&mut self) { - let slots_len = self.get_behaviour().slots.len(); + fn check_slot_updates<'a>( + &'a mut self, + slot: usize, + stack: ItemStack, + ) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + let behaviour = self.get_behaviour_mut(); + if !behaviour.disable_sync { + let prev_stack = &mut behaviour.previous_tracked_stacks[slot]; - for i in 0..slots_len { - let slot = self.get_behaviour().slots[i].clone(); - let stack = slot.get_cloned_stack().await; + if !prev_stack.is_in_sync(&stack) { + prev_stack.set_received_stack(stack.clone()); + let next_revision = behaviour.next_revision(); + if let Some(sync_handler) = behaviour.sync_handler.as_ref() { + sync_handler + .update_slot(behaviour, slot, &stack, next_revision) + .await; + } + } + } + }) + } - self.update_tracked_slot(i, stack.clone()).await; - self.check_slot_updates(i, stack).await; - } + fn check_cursor_stack_updates<'a>(&'a mut self) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + let behaviour = self.get_behaviour_mut(); + if !behaviour.disable_sync { + let cursor_stack = behaviour.cursor_stack.lock().await; + if !behaviour.previous_cursor_stack.is_in_sync(&cursor_stack) { + behaviour + .previous_cursor_stack + .set_received_stack(cursor_stack.clone()); + if let Some(sync_handler) = behaviour.sync_handler.as_ref() { + sync_handler + .update_cursor_stack(behaviour, &cursor_stack) + .await; + } + } + } + }) + } - self.check_cursor_stack_updates().await; + fn send_content_updates<'a>(&'a mut self) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + let slots_len = self.get_behaviour().slots.len(); + for i in 0..slots_len { + let slot = self.get_behaviour().slots[i].clone(); + let stack = slot.get_cloned_stack().await; + + self.update_tracked_slot(i, stack.clone()).await; + self.check_slot_updates(i, stack).await; + } + + self.check_cursor_stack_updates().await; + + let behaviour = self.get_behaviour_mut(); + let mut prop_vec = vec![]; + for (idx, prop) in behaviour.properties.iter_mut().enumerate() { + let value = prop.get(); + if prop.has_changed() { + prop_vec.push((idx, value)); + } + } + + for (idx, value) in prop_vec { + self.update_tracked_properties(idx as i32, value).await; + self.check_property_updates(idx as i32, value).await; + } + }) + } + + fn is_slot_valid<'a>(&'a self, slot: i32) -> ScreenHandlerFuture<'a, bool> { + Box::pin(async move { + slot == -1 || slot == -999 || slot < self.get_behaviour().slots.len() as i32 + }) + } + + fn disable_sync(&mut self) { let behaviour = self.get_behaviour_mut(); - let mut prop_vec = vec![]; - for (idx, prop) in behaviour.properties.iter_mut().enumerate() { - let value = prop.get(); - if prop.has_changed() { - prop_vec.push((idx, value)); - } - } - - for (idx, value) in prop_vec { - self.update_tracked_properties(idx as i32, value).await; - self.check_property_updates(idx as i32, value).await; - } + behaviour.disable_sync = true; } - async fn is_slot_valid(&self, slot: i32) -> bool { - slot == -1 || slot == -999 || slot < self.get_behaviour().slots.len() as i32 + fn enable_sync(&mut self) { + let behaviour = self.get_behaviour_mut(); + behaviour.disable_sync = false; } - async fn get_slot_index(&self, inventory: &Arc, slot: usize) -> Option { - for i in 0..self.get_behaviour().slots.len() { - if Arc::ptr_eq(&self.get_behaviour().slots[i].get_inventory(), inventory) - && self.get_behaviour().slots[i].get_index() == slot - { - return Some(i); - } - } - - None + fn get_slot_index<'a>( + &'a self, + inventory: &'a Arc, + slot: usize, + ) -> OptionUsizeFuture<'a> { + Box::pin(async move { + (0..self.get_behaviour().slots.len()).find(|&i| { + Arc::ptr_eq(&self.get_behaviour().slots[i].get_inventory(), inventory) + && self.get_behaviour().slots[i].get_index() == slot + }) + }) } - async fn quick_move(&mut self, player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack; + fn quick_move<'a>( + &'a mut self, + player: &'a dyn InventoryPlayer, + slot_index: i32, + ) -> ItemStackFuture<'a>; - async fn insert_item( - &mut self, - stack: &mut ItemStack, + fn insert_item<'a>( + &'a mut self, + stack: &'a mut ItemStack, start_index: i32, end_index: i32, from_last: bool, - ) -> bool { - let mut success = false; - let mut current_index = if from_last { - end_index - 1 - } else { - start_index - }; + ) -> ScreenHandlerFuture<'a, bool> { + Box::pin(async move { + let mut success = false; + let mut current_index = if from_last { + end_index - 1 + } else { + start_index + }; - if stack.is_stackable() { - while !stack.is_empty() - && (if from_last { + if stack.is_stackable() { + while !stack.is_empty() + && (if from_last { + current_index >= start_index + } else { + current_index < end_index + }) + { + let slot = self.get_behaviour().slots[current_index as usize].clone(); + let slot_stack_lock = slot.get_stack().await; + let mut slot_stack = slot_stack_lock.lock().await; + + if !slot_stack.is_empty() && slot_stack.are_items_and_components_equal(stack) { + let combined_count = slot_stack.item_count + stack.item_count; + let max_slot_count = slot.get_max_item_count_for_stack(&slot_stack).await; + if combined_count <= max_slot_count { + stack.set_count(0); + slot_stack.set_count(combined_count); + drop(slot_stack); + slot.mark_dirty().await; + success = true; + } else if slot_stack.item_count < max_slot_count { + stack.decrement(max_slot_count - slot_stack.item_count); + slot_stack.set_count(max_slot_count); + drop(slot_stack); + slot.mark_dirty().await; + success = true; + } + } + + if from_last { + current_index -= 1; + } else { + current_index += 1; + } + } + } + + if !stack.is_empty() { + if from_last { + current_index = end_index - 1; + } else { + current_index = start_index; + } + + while if from_last { current_index >= start_index } else { current_index < end_index - }) - { - let slot = self.get_behaviour().slots[current_index as usize].clone(); - let slot_stack = slot.get_stack().await; - let mut slot_stack = slot_stack.lock().await; + } { + let slot = self.get_behaviour().slots[current_index as usize].clone(); + let slot_stack = slot.get_stack().await; + let slot_stack = slot_stack.lock().await; - if !slot_stack.is_empty() && slot_stack.are_items_and_components_equal(stack) { - let combined_count = slot_stack.item_count + stack.item_count; - let max_slot_count = slot.get_max_item_count_for_stack(&slot_stack).await; - if combined_count <= max_slot_count { - stack.set_count(0); - slot_stack.set_count(combined_count); - drop(slot_stack); - slot.mark_dirty().await; - success = true; - } else if slot_stack.item_count < max_slot_count { - stack.decrement(max_slot_count - slot_stack.item_count); - slot_stack.set_count(max_slot_count); + if slot_stack.is_empty() && slot.can_insert(stack).await { + let max_count = slot.get_max_item_count_for_stack(stack).await; drop(slot_stack); + slot.set_stack(stack.split(max_count.min(stack.item_count))) + .await; slot.mark_dirty().await; success = true; + break; + } + + if from_last { + current_index -= 1; + } else { + current_index += 1; } } - - if from_last { - current_index -= 1; - } else { - current_index += 1; - } - } - } - - if !stack.is_empty() { - if from_last { - current_index = end_index - 1; - } else { - current_index = start_index; } - while if from_last { - current_index >= start_index - } else { - current_index < end_index - } { - let slot = self.get_behaviour().slots[current_index as usize].clone(); - let slot_stack = slot.get_stack().await; - let slot_stack = slot_stack.lock().await; - - if slot_stack.is_empty() && slot.can_insert(stack).await { - let max_count = slot.get_max_item_count_for_stack(stack).await; - drop(slot_stack); - slot.set_stack(stack.split(max_count.min(stack.item_count))) - .await; - slot.mark_dirty().await; - success = true; - break; - } - - if from_last { - current_index -= 1; - } else { - current_index += 1; - } - } - } - - success + success + }) } - async fn handle_slot_click( - &self, - _player: &dyn InventoryPlayer, + fn handle_slot_click<'a>( + &'a self, + _player: &'a dyn InventoryPlayer, _click_type: MouseClick, _slot: Arc, _slot_stack: ItemStack, _cursor_stack: ItemStack, - ) -> bool { - // TODO: required for bundle in the future - false + ) -> ScreenHandlerFuture<'a, bool> { + Box::pin(async { + // TODO: required for bundle in the future + false + }) } - async fn on_slot_click( - &mut self, + fn on_slot_click<'a>( + &'a mut self, slot_index: i32, button: i32, action_type: SlotActionType, - player: &dyn InventoryPlayer, - ) { - self.internal_on_slot_click(slot_index, button, action_type, player) - .await; + player: &'a dyn InventoryPlayer, + ) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + self.internal_on_slot_click(slot_index, button, action_type, player) + .await; + }) } - async fn internal_on_slot_click( - &mut self, + fn internal_on_slot_click<'a>( + &'a mut self, slot_index: i32, button: i32, action_type: SlotActionType, - player: &dyn InventoryPlayer, - ) { - if action_type == SlotActionType::PickupAll && button == 0 { - let behavior = self.get_behaviour_mut(); - let mut cursor_stack = behavior.cursor_stack.lock().await; - let mut to_pick_up = cursor_stack.get_max_stack_size() - cursor_stack.item_count; + player: &'a dyn InventoryPlayer, + ) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + if action_type == SlotActionType::PickupAll && button == 0 { + let behavior = self.get_behaviour_mut(); + let mut cursor_stack = behavior.cursor_stack.lock().await; + let mut to_pick_up = cursor_stack.get_max_stack_size() - cursor_stack.item_count; - for slot in behavior.slots.iter() { - if to_pick_up == 0 { - break; - } + for slot in behavior.slots.iter() { + if to_pick_up == 0 { + break; + } - let item_stack = slot.get_cloned_stack().await; - if !item_stack.are_items_and_components_equal(&cursor_stack) { - continue; - } + let item_stack = slot.get_cloned_stack().await; + if !item_stack.are_items_and_components_equal(&cursor_stack) { + continue; + } - if !slot.allow_modification(player).await { - continue; - } + if !slot.allow_modification(player).await { + continue; + } - let taken_stack = slot - .safe_take( - item_stack.item_count.min(to_pick_up), - cursor_stack.get_max_stack_size() - cursor_stack.item_count, - player, - ) - .await; - to_pick_up -= taken_stack.item_count; - cursor_stack.increment(taken_stack.item_count); - } - } else if action_type == SlotActionType::QuickCraft { - let drag_type = button & 3; - let drag_button = (button >> 2) & 3; - let behaviour = self.get_behaviour_mut(); - if drag_type == 0 { - behaviour.drag_slots.clear(); - } else if drag_type == 1 { - if slot_index < 0 { - warn!("Invalid slot index for drag action: {slot_index}. Must be >= 0"); - return; - } - let cursor_stack = behaviour.cursor_stack.lock().await; - - let slot = &behaviour.slots[slot_index as usize]; - let stack_lock = slot.get_stack().await; - let stack = stack_lock.lock().await; - if !cursor_stack.is_empty() - && slot.can_insert(&cursor_stack).await - && (stack.are_items_and_components_equal(&cursor_stack) || stack.is_empty()) - && slot.get_max_item_count_for_stack(&stack).await > stack.item_count - { - behaviour.drag_slots.push(slot_index as u32); - } - } else if drag_type == 2 && !behaviour.drag_slots.is_empty() { - // process drag end - if behaviour.drag_slots.len() == 1 { - let slot = behaviour.drag_slots[0] as i32; - behaviour.drag_slots.clear(); - let _ = behaviour; - self.internal_on_slot_click(slot, drag_button, SlotActionType::Pickup, player) + let taken_stack = slot + .safe_take( + item_stack.item_count.min(to_pick_up), + cursor_stack.get_max_stack_size() - cursor_stack.item_count, + player, + ) .await; - - return; - } - if drag_button == 2 && !player.has_infinite_materials() { - return; // Only creative + to_pick_up -= taken_stack.item_count; + cursor_stack.increment(taken_stack.item_count); } + } else if action_type == SlotActionType::QuickCraft { + let drag_type = button & 3; + let drag_button = (button >> 2) & 3; + let behaviour = self.get_behaviour_mut(); + if drag_type == 0 { + behaviour.drag_slots.clear(); + } else if drag_type == 1 { + if slot_index < 0 { + warn!("Invalid slot index for drag action: {slot_index}. Must be >= 0"); + return; + } + let cursor_stack = behaviour.cursor_stack.lock().await; - let mut cursor_stack = behaviour.cursor_stack.lock().await; - let initial_count = cursor_stack.item_count; - for slot_index in behaviour.drag_slots.iter() { - let slot = behaviour.slots[*slot_index as usize].clone(); + let slot = &behaviour.slots[slot_index as usize]; let stack_lock = slot.get_stack().await; let stack = stack_lock.lock().await; - - if (stack.are_items_and_components_equal(&cursor_stack) || stack.is_empty()) + if !cursor_stack.is_empty() && slot.can_insert(&cursor_stack).await + && (stack.are_items_and_components_equal(&cursor_stack) || stack.is_empty()) + && slot.get_max_item_count_for_stack(&stack).await > stack.item_count { - let mut inserting_count = if drag_button == 0 { - initial_count / behaviour.drag_slots.len() as u8 - } else if drag_button == 1 { - 1 - } else if drag_button == 2 { - cursor_stack.get_max_stack_size() - } else { - panic!("Invalid drag button: {drag_button}"); - }; - inserting_count = inserting_count - .min(max( - 0, - slot.get_max_item_count_for_stack(&stack).await - stack.item_count, - )) - .min(cursor_stack.item_count); - if inserting_count > 0 { - let mut stack_clone = stack.clone(); - drop(stack); - if stack_clone.is_empty() { - stack_clone = cursor_stack.copy_with_count(0); - } - stack_clone.increment(inserting_count); - slot.set_stack(stack_clone).await; - if drag_button != 2 { - cursor_stack.decrement(inserting_count); - } - if cursor_stack.is_empty() { - break; - } - } + behaviour.drag_slots.push(slot_index as u32); } - } + } else if drag_type == 2 && !behaviour.drag_slots.is_empty() { + // process drag end + if behaviour.drag_slots.len() == 1 { + let slot = behaviour.drag_slots[0] as i32; + behaviour.drag_slots.clear(); + let _ = behaviour; + self.internal_on_slot_click( + slot, + drag_button, + SlotActionType::Pickup, + player, + ) + .await; - behaviour.drag_slots.clear(); - } - } else if action_type == SlotActionType::Throw { - if slot_index >= 0 && self.get_behaviour().cursor_stack.lock().await.is_empty() { - let slot = self.get_behaviour().slots[slot_index as usize].clone(); - let prev_stack = slot.get_cloned_stack().await; - if !prev_stack.is_empty() { - if button == 1 { - // Throw all - while slot - .get_cloned_stack() - .await - .are_items_and_components_equal(&prev_stack) + return; + } + if drag_button == 2 && !player.has_infinite_materials() { + return; // Only creative + } + + let mut cursor_stack = behaviour.cursor_stack.lock().await; + let initial_count = cursor_stack.item_count; + for slot_index in behaviour.drag_slots.iter() { + let slot = behaviour.slots[*slot_index as usize].clone(); + let stack_lock = slot.get_stack().await; + let stack = stack_lock.lock().await; + + if (stack.are_items_and_components_equal(&cursor_stack) || stack.is_empty()) + && slot.can_insert(&cursor_stack).await { - let drop_stack = - slot.safe_take(prev_stack.item_count, u8::MAX, player).await; - player.drop_item(drop_stack, true).await; - // player.handleCreativeModeItemDrop(itemStack); + let mut inserting_count = if drag_button == 0 { + initial_count / behaviour.drag_slots.len() as u8 + } else if drag_button == 1 { + 1 + } else if drag_button == 2 { + cursor_stack.get_max_stack_size() + } else { + panic!("Invalid drag button: {drag_button}"); + }; + inserting_count = inserting_count + .min(max( + 0, + slot.get_max_item_count_for_stack(&stack).await + - stack.item_count, + )) + .min(cursor_stack.item_count); + if inserting_count > 0 { + let mut stack_clone = stack.clone(); + drop(stack); + if stack_clone.is_empty() { + stack_clone = cursor_stack.copy_with_count(0); + } + stack_clone.increment(inserting_count); + slot.set_stack(stack_clone).await; + if drag_button != 2 { + cursor_stack.decrement(inserting_count); + } + if cursor_stack.is_empty() { + break; + } + } } - } else { - let drop_stack = slot.safe_take(1, u8::MAX, player).await; - if !drop_stack.is_empty() { - slot.on_take_item(player, &drop_stack).await; - player.drop_item(drop_stack, true).await; + } + + behaviour.drag_slots.clear(); + } + } else if action_type == SlotActionType::Throw { + if slot_index >= 0 && self.get_behaviour().cursor_stack.lock().await.is_empty() { + let slot = self.get_behaviour().slots[slot_index as usize].clone(); + let prev_stack = slot.get_cloned_stack().await; + if !prev_stack.is_empty() { + if button == 1 { + // Throw all + while slot + .get_cloned_stack() + .await + .are_items_and_components_equal(&prev_stack) + { + let drop_stack = + slot.safe_take(prev_stack.item_count, u8::MAX, player).await; + player.drop_item(drop_stack, true).await; + // player.handleCreativeModeItemDrop(itemStack); + } + } else { + let drop_stack = slot.safe_take(1, u8::MAX, player).await; + if !drop_stack.is_empty() { + slot.on_take_item(player, &drop_stack).await; + player.drop_item(drop_stack, true).await; + } } } } - } - } else if action_type == SlotActionType::Clone { - if player.has_infinite_materials() && slot_index >= 0 { - let behaviour = self.get_behaviour_mut(); - let slot = behaviour.slots[slot_index as usize].clone(); - let stack_lock = slot.get_stack().await; - let stack = stack_lock.lock().await; - let mut cursor_stack = behaviour.cursor_stack.lock().await; - *cursor_stack = stack.copy_with_count(stack.get_max_stack_size()); - } - } else if (action_type == SlotActionType::Pickup - || action_type == SlotActionType::QuickMove) - && (button == 0 || button == 1) - { - let click_type = if button == 0 { - MouseClick::Left - } else { - MouseClick::Right - }; - - // Drop item if outside inventory - if slot_index == SLOT_INDEX_OUTSIDE { - let mut cursor_stack = self.get_behaviour().cursor_stack.lock().await; - if !cursor_stack.is_empty() { - if click_type == MouseClick::Left { - player.drop_item(cursor_stack.clone(), true).await; - *cursor_stack = ItemStack::EMPTY.clone(); - } else { - player.drop_item(cursor_stack.split(1), true).await; - } - } - } else if action_type == SlotActionType::QuickMove { - if slot_index < 0 { - return; + } else if action_type == SlotActionType::Clone { + if player.has_infinite_materials() && slot_index >= 0 { + let behaviour = self.get_behaviour_mut(); + let slot = behaviour.slots[slot_index as usize].clone(); + let stack_lock = slot.get_stack().await; + let stack = stack_lock.lock().await; + let mut cursor_stack = behaviour.cursor_stack.lock().await; + *cursor_stack = stack.copy_with_count(stack.get_max_stack_size()); } + } else if (action_type == SlotActionType::Pickup + || action_type == SlotActionType::QuickMove) + && (button == 0 || button == 1) + { + let click_type = if button == 0 { + MouseClick::Left + } else { + MouseClick::Right + }; - let slot = self.get_behaviour().slots[slot_index as usize].clone(); - - if !slot.can_take_items(player).await { - return; - } - - let mut moved_stack = self.quick_move(player, slot_index).await; - - while !moved_stack.is_empty() - && ItemStack::are_items_and_components_equal( - &slot.get_cloned_stack().await, - &moved_stack, - ) - { - moved_stack = self.quick_move(player, slot_index).await; - } - } else { - // Pickup - if slot_index < 0 { - return; - } - - let slot = self.get_behaviour().slots[slot_index as usize].clone(); - - if click_type == MouseClick::Left { - slot.on_click(player).await; - } - - let slot_stack = slot.get_cloned_stack().await; - let mut cursor_stack = self.get_behaviour().cursor_stack.lock().await; - - let equipment_slot = cursor_stack - .get_data_component::() - .map_or(&EquipmentSlot::MAIN_HAND, |equippable| equippable.slot); - - if self - .handle_slot_click( - player, - click_type.clone(), - slot.clone(), - slot_stack.clone(), - cursor_stack.clone(), - ) - .await - { - return; - } - - if slot_stack.is_empty() { + // Drop item if outside inventory + if slot_index == SLOT_INDEX_OUTSIDE { + let mut cursor_stack = self.get_behaviour().cursor_stack.lock().await; if !cursor_stack.is_empty() { - if equipment_slot.slot_type() == EquipmentType::HumanoidArmor - && (5..9).contains(&slot_index) - { - player - .enqueue_equipment_change(equipment_slot, &cursor_stack) - .await; + if click_type == MouseClick::Left { + player.drop_item(cursor_stack.clone(), true).await; + *cursor_stack = ItemStack::EMPTY.clone(); + } else { + player.drop_item(cursor_stack.split(1), true).await; } - - let transfer_count = if click_type == MouseClick::Left { - cursor_stack.item_count - } else { - 1 - }; - *cursor_stack = slot - .insert_stack_count(cursor_stack.clone(), transfer_count) - .await; } - } else if slot.can_take_items(player).await { - if cursor_stack.is_empty() { - let take_count = if click_type == MouseClick::Left { - slot_stack.item_count - } else { - slot_stack.item_count.div_ceil(2) - }; - let taken = slot.try_take_stack_range(take_count, u8::MAX, player).await; - if let Some(taken) = taken { - // Reverse order of operations, shouldn't affect anything - *cursor_stack = taken.clone(); - slot.on_take_item(player, &taken).await; + } else if action_type == SlotActionType::QuickMove { + if slot_index < 0 { + return; + } - if (5..9).contains(&slot_index) { - let equipment_slot = cursor_stack - .get_data_component::() - .map_or(&EquipmentSlot::MAIN_HAND, |equippable| { - equippable.slot - }); + let slot = self.get_behaviour().slots[slot_index as usize].clone(); + + if !slot.can_take_items(player).await { + return; + } + + let mut moved_stack = self.quick_move(player, slot_index).await; + + while !moved_stack.is_empty() + && ItemStack::are_items_and_components_equal( + &slot.get_cloned_stack().await, + &moved_stack, + ) + { + moved_stack = self.quick_move(player, slot_index).await; + } + } else { + // Pickup + if slot_index < 0 { + return; + } + + let slot = self.get_behaviour().slots[slot_index as usize].clone(); + + if click_type == MouseClick::Left { + slot.on_click(player).await; + } + + let slot_stack = slot.get_cloned_stack().await; + let mut cursor_stack = self.get_behaviour().cursor_stack.lock().await; + + let equipment_slot = cursor_stack + .get_data_component::() + .map_or(&EquipmentSlot::MAIN_HAND, |equippable| equippable.slot); + + if self + .handle_slot_click( + player, + click_type.clone(), + slot.clone(), + slot_stack.clone(), + cursor_stack.clone(), + ) + .await + { + return; + } + + if slot_stack.is_empty() { + if !cursor_stack.is_empty() { + if equipment_slot.slot_type() == EquipmentType::HumanoidArmor + && (5..9).contains(&slot_index) + { player - .enqueue_equipment_change(equipment_slot, ItemStack::EMPTY) + .enqueue_equipment_change(equipment_slot, &cursor_stack) .await; } - } - } else if slot.can_insert(&cursor_stack).await { - if equipment_slot.slot_type() == EquipmentType::HumanoidArmor - && (5..9).contains(&slot_index) - { - player - .enqueue_equipment_change(equipment_slot, &cursor_stack) - .await; - } - if ItemStack::are_items_and_components_equal(&slot_stack, &cursor_stack) { - let insert_count = if click_type == MouseClick::Left { + let transfer_count = if click_type == MouseClick::Left { cursor_stack.item_count } else { 1 }; *cursor_stack = slot - .insert_stack_count(cursor_stack.clone(), insert_count) + .insert_stack_count(cursor_stack.clone(), transfer_count) .await; - } else if cursor_stack.item_count - <= slot.get_max_item_count_for_stack(&cursor_stack).await - { - let old_cursor_stack = cursor_stack.clone(); - *cursor_stack = slot_stack.clone(); - slot.set_stack(old_cursor_stack).await; } - } else if ItemStack::are_items_and_components_equal(&slot_stack, &cursor_stack) - { - let taken = slot - .try_take_stack_range( - slot_stack.item_count, - cursor_stack - .get_max_stack_size() - .saturating_sub(cursor_stack.item_count), - player, - ) - .await; + } else if slot.can_take_items(player).await { + if cursor_stack.is_empty() { + let take_count = if click_type == MouseClick::Left { + slot_stack.item_count + } else { + slot_stack.item_count.div_ceil(2) + }; + let taken = + slot.try_take_stack_range(take_count, u8::MAX, player).await; + if let Some(taken) = taken { + // Reverse order of operations, shouldn't affect anything + *cursor_stack = taken.clone(); + slot.on_take_item(player, &taken).await; - if let Some(taken) = taken { - cursor_stack.increment(taken.item_count); - slot.on_take_item(player, &taken).await; + if (5..9).contains(&slot_index) { + let equipment_slot = cursor_stack + .get_data_component::() + .map_or(&EquipmentSlot::MAIN_HAND, |equippable| { + equippable.slot + }); + player + .enqueue_equipment_change(equipment_slot, ItemStack::EMPTY) + .await; + } + } + } else if slot.can_insert(&cursor_stack).await { + if equipment_slot.slot_type() == EquipmentType::HumanoidArmor + && (5..9).contains(&slot_index) + { + player + .enqueue_equipment_change(equipment_slot, &cursor_stack) + .await; + } + + if ItemStack::are_items_and_components_equal(&slot_stack, &cursor_stack) + { + let insert_count = if click_type == MouseClick::Left { + cursor_stack.item_count + } else { + 1 + }; + *cursor_stack = slot + .insert_stack_count(cursor_stack.clone(), insert_count) + .await; + } else if cursor_stack.item_count + <= slot.get_max_item_count_for_stack(&cursor_stack).await + { + let old_cursor_stack = cursor_stack.clone(); + *cursor_stack = slot_stack.clone(); + slot.set_stack(old_cursor_stack).await; + } + } else if ItemStack::are_items_and_components_equal( + &slot_stack, + &cursor_stack, + ) { + let taken = slot + .try_take_stack_range( + slot_stack.item_count, + cursor_stack + .get_max_stack_size() + .saturating_sub(cursor_stack.item_count), + player, + ) + .await; + + if let Some(taken) = taken { + cursor_stack.increment(taken.item_count); + slot.on_take_item(player, &taken).await; + } } } + + slot.mark_dirty().await; } + } else if action_type == SlotActionType::Swap && (0..9).contains(&button) + || button == 40 + { + let mut button_stack = player + .get_inventory() + .get_stack(button as usize) + .await + .lock() + .await + .clone(); + let source_slot = self.get_behaviour().slots[slot_index as usize].clone(); + let source_stack = source_slot.get_cloned_stack().await; - slot.mark_dirty().await; - } - } else if action_type == SlotActionType::Swap && (0..9).contains(&button) || button == 40 { - let mut button_stack = player - .get_inventory() - .get_stack(button as usize) - .await - .lock() - .await - .clone(); - let source_slot = self.get_behaviour().slots[slot_index as usize].clone(); - let source_stack = source_slot.get_cloned_stack().await; - - if !button_stack.is_empty() || !source_stack.is_empty() { - if button_stack.is_empty() { - if source_slot.can_take_items(player).await { - player - .get_inventory() - .set_stack(button as usize, source_stack.clone()) - .await; - source_slot.set_stack(ItemStack::EMPTY.clone()).await; - source_slot.on_take_item(player, &source_stack).await; - } - } else if source_stack.is_empty() { - if source_slot.can_insert(&button_stack).await { + if !button_stack.is_empty() || !source_stack.is_empty() { + if button_stack.is_empty() { + if source_slot.can_take_items(player).await { + player + .get_inventory() + .set_stack(button as usize, source_stack.clone()) + .await; + source_slot.set_stack(ItemStack::EMPTY.clone()).await; + source_slot.on_take_item(player, &source_stack).await; + } + } else if source_stack.is_empty() && source_slot.can_insert(&button_stack).await + { let max_count = source_slot .get_max_item_count_for_stack(&button_stack) .await; @@ -885,61 +1013,28 @@ pub trait ScreenHandler: Send + Sync { source_slot.set_stack(button_stack).await; } } - } else if source_slot.can_take_items(player).await - && source_slot.can_insert(&button_stack).await - { - let max_count = source_slot - .get_max_item_count_for_stack(&button_stack) - .await; - if button_stack.item_count > max_count { - source_slot.set_stack(button_stack.split(max_count)).await; - source_slot.on_take_item(player, &button_stack).await; - if !player - .get_inventory() - .insert_stack_anywhere(&mut button_stack) - .await - { - player.drop_item(button_stack, true).await; - } - } else { - player - .get_inventory() - .set_stack(button as usize, source_stack) - .await; - source_slot.set_stack(button_stack.clone()).await; - source_slot.on_take_item(player, &button_stack).await; - } } } - } - } - - async fn disable_sync(&mut self) { - let behaviour = self.get_behaviour_mut(); - behaviour.disable_sync = true; - } - - async fn enable_sync(&mut self) { - let behaviour = self.get_behaviour_mut(); - behaviour.disable_sync = false; + }) } } -#[async_trait] pub trait ScreenHandlerListener: Send + Sync { - async fn on_slot_update( - &self, - _screen_handler: &ScreenHandlerBehaviour, + fn on_slot_update<'a>( + &'a self, + _screen_handler: &'a ScreenHandlerBehaviour, _slot: u8, _stack: ItemStack, - ) { + ) -> Pin + Send + 'a>> { + Box::pin(async move {}) } - async fn on_property_update( - &self, - _screen_handler: &ScreenHandlerBehaviour, + fn on_property_update<'a>( + &'a self, + _screen_handler: &'a ScreenHandlerBehaviour, _property: u8, _value: i32, - ) { + ) -> Pin + Send + 'a>> { + Box::pin(async move {}) } } diff --git a/pumpkin-world/src/block/entities/barrel.rs b/pumpkin-world/src/block/entities/barrel.rs index a87adec94..535b780d8 100644 --- a/pumpkin-world/src/block/entities/barrel.rs +++ b/pumpkin-world/src/block/entities/barrel.rs @@ -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 + 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) { - self.viewers - .update_viewer_count::(self, world, &self.position) - .await; + fn tick<'a>( + &'a self, + world: Arc, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + self.viewers + .update_viewer_count::(self, world, &self.position) + .await; + }) } fn get_inventory(self: Arc) -> Option> { @@ -88,16 +99,27 @@ impl BlockEntity for BarrelBlockEntity { } } -#[async_trait] impl ViewerCountListener for BarrelBlockEntity { - async fn on_container_open(&self, world: &Arc, _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, + _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, _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, + _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 + Send + '_>> { + Box::pin(async move { + for slot in self.items.iter() { + *slot.lock().await = ItemStack::EMPTY.clone(); + } + }) } } diff --git a/pumpkin-world/src/block/entities/bed.rs b/pumpkin-world/src/block/entities/bed.rs index 63f592d37..bdbac7364 100644 --- a/pumpkin-world/src/block/entities/bed.rs +++ b/pumpkin-world/src/block/entities/bed.rs @@ -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 + Send + 'a>> { + Box::pin(async move {}) + } fn as_any(&self) -> &dyn std::any::Any { self diff --git a/pumpkin-world/src/block/entities/chest.rs b/pumpkin-world/src/block/entities/chest.rs index 8f84ae0f0..8c5245e80 100644 --- a/pumpkin-world/src/block/entities/chest.rs +++ b/pumpkin-world/src/block/entities/chest.rs @@ -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 + 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) { - self.viewers - .update_viewer_count::(self, world, &self.position) - .await; + fn tick<'a>( + &'a self, + world: Arc, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + self.viewers + .update_viewer_count::(self, world, &self.position) + .await; + }) } fn get_inventory(self: Arc) -> Option> { @@ -89,29 +100,41 @@ impl BlockEntity for ChestBlockEntity { } } -#[async_trait] impl ViewerCountListener for ChestBlockEntity { - async fn on_container_open(&self, world: &Arc, _position: &BlockPos) { - self.play_sound(world, Sound::BlockChestOpen).await; + fn on_container_open<'a>( + &'a self, + world: &'a Arc, + _position: &'a BlockPos, + ) -> ViewerFuture<'a, ()> { + Box::pin(async move { + self.play_sound(world, Sound::BlockEnderChestOpen).await; + }) } - async fn on_container_close(&self, world: &Arc, _position: &BlockPos) { - self.play_sound(world, Sound::BlockChestClose).await; + fn on_container_close<'a>( + &'a self, + world: &'a Arc, + _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, - position: &BlockPos, + fn on_viewer_count_update<'a>( + &'a self, + world: &'a Arc, + 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 + Send + '_>> { + Box::pin(async move { + for slot in self.items.iter() { + *slot.lock().await = ItemStack::EMPTY.clone(); + } + }) } } diff --git a/pumpkin-world/src/block/entities/chiseled_bookshelf.rs b/pumpkin-world/src/block/entities/chiseled_bookshelf.rs index 3373f8eb3..da423eae8 100644 --- a/pumpkin-world/src/block/entities/chiseled_bookshelf.rs +++ b/pumpkin-world/src/block/entities/chiseled_bookshelf.rs @@ -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 + 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) -> Option> { @@ -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 + Send + '_>> { + Box::pin(async move { + for slot in self.items.iter() { + *slot.lock().await = ItemStack::EMPTY.clone(); + } + }) } } diff --git a/pumpkin-world/src/block/entities/command_block.rs b/pumpkin-world/src/block/entities/command_block.rs index 7276ff201..0d8bb4b4e 100644 --- a/pumpkin-world/src/block/entities/command_block.rs +++ b/pumpkin-world/src/block/entities/command_block.rs @@ -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 + Send + 'a>> { + Box::pin(async move {}) + } fn is_dirty(&self) -> bool { self.dirty.load(Ordering::Relaxed) diff --git a/pumpkin-world/src/block/entities/comparator.rs b/pumpkin-world/src/block/entities/comparator.rs index 54e6480d6..f793754c0 100644 --- a/pumpkin-world/src/block/entities/comparator.rs +++ b/pumpkin-world/src/block/entities/comparator.rs @@ -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 + 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 { diff --git a/pumpkin-world/src/block/entities/dropper.rs b/pumpkin-world/src/block/entities/dropper.rs index c042d9041..d63de65ee 100644 --- a/pumpkin-world/src/block/entities/dropper.rs +++ b/pumpkin-world/src/block/entities/dropper.rs @@ -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 + 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 + Send + '_>> { + Box::pin(async move { + for slot in self.items.iter() { + *slot.lock().await = ItemStack::EMPTY.clone(); + } + }) } } diff --git a/pumpkin-world/src/block/entities/end_portal.rs b/pumpkin-world/src/block/entities/end_portal.rs index a7ff13045..e96bc8f88 100644 --- a/pumpkin-world/src/block/entities/end_portal.rs +++ b/pumpkin-world/src/block/entities/end_portal.rs @@ -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 + Send + 'a>> { + Box::pin(async move {}) + } fn as_any(&self) -> &dyn std::any::Any { self diff --git a/pumpkin-world/src/block/entities/ender_chest.rs b/pumpkin-world/src/block/entities/ender_chest.rs index 872a8ec5f..86cec3150 100644 --- a/pumpkin-world/src/block/entities/ender_chest.rs +++ b/pumpkin-world/src/block/entities/ender_chest.rs @@ -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, } -#[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 + Send + 'a>> { + Box::pin(async move {}) + } - async fn tick(&self, world: Arc) { - self.viewers - .update_viewer_count::(self, world, &self.position) - .await; + fn tick<'a>( + &'a self, + world: Arc, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + self.viewers + .update_viewer_count::(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, _position: &BlockPos) { - self.play_sound(world, Sound::BlockEnderChestOpen).await; + fn on_container_open<'a>( + &'a self, + world: &'a Arc, + _position: &'a BlockPos, + ) -> ViewerFuture<'a, ()> { + Box::pin(async move { + self.play_sound(world, Sound::BlockEnderChestOpen).await; + }) } - async fn on_container_close(&self, world: &Arc, _position: &BlockPos) { - self.play_sound(world, Sound::BlockEnderChestClose).await; + fn on_container_close<'a>( + &'a self, + world: &'a Arc, + _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, - position: &BlockPos, + fn on_viewer_count_update<'a>( + &'a self, + world: &'a Arc, + 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 + }) } } diff --git a/pumpkin-world/src/block/entities/furnace.rs b/pumpkin-world/src/block/entities/furnace.rs index dedce44f1..2b95d5ef2 100644 --- a/pumpkin-world/src/block/entities/furnace.rs +++ b/pumpkin-world/src/block/entities/furnace.rs @@ -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) { - 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, + ) -> Pin + 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 + 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 + Send + '_>> { + Box::pin(async move { + for slot in self.items.iter() { + *slot.lock().await = ItemStack::EMPTY.clone(); + } + }) } } diff --git a/pumpkin-world/src/block/entities/hopper.rs b/pumpkin-world/src/block/entities/hopper.rs index b2553dca8..9093a61f4 100644 --- a/pumpkin-world/src/block/entities/hopper.rs +++ b/pumpkin-world/src/block/entities/hopper.rs @@ -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 { .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 + 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) { - 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, + ) -> Pin + 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 + Send + '_>> { + Box::pin(async move { + for slot in self.items.iter() { + *slot.lock().await = ItemStack::EMPTY.clone(); + } + }) } } diff --git a/pumpkin-world/src/block/entities/mob_spawner.rs b/pumpkin-world/src/block/entities/mob_spawner.rs index 5ab21079a..75c0d55f2 100644 --- a/pumpkin-world/src/block/entities/mob_spawner.rs +++ b/pumpkin-world/src/block/entities/mob_spawner.rs @@ -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) { - 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::() + rand::random::()) * spawn_range as f64 - + 0.5, - (pos.y + rand::random_range(0..3) - 1) as f64, - pos.z as f64 - + (rand::random::() + rand::random::()) * 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, + ) -> Pin + 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::() + rand::random::()) * spawn_range as f64 + + 0.5, + (pos.y + rand::random_range(0..3) - 1) as f64, + pos.z as f64 + + (rand::random::() + rand::random::()) * 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 + 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 { diff --git a/pumpkin-world/src/block/entities/mod.rs b/pumpkin-world/src/block/entities/mod.rs index 81c911062..8b23752f5 100644 --- a/pumpkin-world/src/block/entities/mod.rs +++ b/pumpkin-world/src/block/entities/mod.rs @@ -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 + Send + 'a>>; fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self where Self: Sized; - async fn tick(&self, _world: Arc) {} + fn tick<'a>( + &'a self, + _world: Arc, + ) -> Pin + 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 + 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, world: Arc, position: BlockPos) { - if let Some(inventory) = self.get_inventory() { - world.scatter_inventory(&position, &inventory).await; - } + fn on_block_replaced<'a>( + self: Arc, + world: Arc, + position: BlockPos, + ) -> Pin + 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 diff --git a/pumpkin-world/src/block/entities/piston.rs b/pumpkin-world/src/block/entities/piston.rs index 7c0b4518b..a507ce950 100644 --- a/pumpkin-world/src/block/entities/piston.rs +++ b/pumpkin-world/src/block/entities/piston.rs @@ -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) { - 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, + ) -> Pin + 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 + 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 { diff --git a/pumpkin-world/src/block/entities/shulker_box.rs b/pumpkin-world/src/block/entities/shulker_box.rs index 0f6c576a8..e481e4c4d 100644 --- a/pumpkin-world/src/block/entities/shulker_box.rs +++ b/pumpkin-world/src/block/entities/shulker_box.rs @@ -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 + 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) { - self.viewers - .update_viewer_count::(self, world, &self.position) - .await; + fn tick<'a>( + &'a self, + world: Arc, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + self.viewers + .update_viewer_count::(self, world, &self.position) + .await; + }) } - async fn on_block_replaced(self: Arc, _world: Arc, _position: BlockPos) { - // Do nothing + fn on_block_replaced<'a>( + self: Arc, + _world: Arc, + _position: BlockPos, + ) -> Pin + Send + 'a>> + where + Self: 'a, + { + Box::pin(async move { + // Do nothing + }) } fn get_inventory(self: Arc) -> Option> { @@ -86,30 +106,43 @@ impl BlockEntity for ShulkerBoxBlockEntity { } } -#[async_trait] impl ViewerCountListener for ShulkerBoxBlockEntity { - async fn on_container_open(&self, world: &Arc, 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, + 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, 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, + 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, - position: &BlockPos, + fn on_viewer_count_update<'a>( + &'a self, + world: &'a Arc, + 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 + Send + '_>> { + Box::pin(async move { + for slot in self.items.iter() { + *slot.lock().await = ItemStack::EMPTY.clone(); + } + }) } } diff --git a/pumpkin-world/src/block/entities/sign.rs b/pumpkin-world/src/block/entities/sign.rs index 1fc0f65b8..3a4e93118 100644 --- a/pumpkin-world/src/block/entities/sign.rs +++ b/pumpkin-world/src/block/entities/sign.rs @@ -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 + 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 { diff --git a/pumpkin-world/src/block/viewer.rs b/pumpkin-world/src/block/viewer.rs index ac1063b5e..139101bf7 100644 --- a/pumpkin-world/src/block/viewer.rs +++ b/pumpkin-world/src/block/viewer.rs @@ -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, _position: &BlockPos) {} - async fn on_container_close(&self, _world: &Arc, _position: &BlockPos) {} - async fn on_viewer_count_update( - &self, - _world: &Arc, - _position: &BlockPos, +pub type ViewerFuture<'a, T> = Pin + Send + 'a>>; + +pub trait ViewerCountListener: Send + Sync { + fn on_container_open<'a>( + &'a self, + _world: &'a Arc, + _position: &'a BlockPos, + ) -> ViewerFuture<'a, ()> { + Box::pin(async {}) + } + + fn on_container_close<'a>( + &'a self, + _world: &'a Arc, + _position: &'a BlockPos, + ) -> ViewerFuture<'a, ()> { + Box::pin(async {}) + } + + fn on_viewer_count_update<'a>( + &'a self, + _world: &'a Arc, + _position: &'a BlockPos, _old: u16, _new: u16, - ) { + ) -> ViewerFuture<'a, ()> { + Box::pin(async {}) } } diff --git a/pumpkin-world/src/chunk/format/anvil.rs b/pumpkin-world/src/chunk/format/anvil.rs index 798853b67..1b217a7d5 100644 --- a/pumpkin-world/src/chunk/format/anvil.rs +++ b/pumpkin-world/src/chunk/format/anvil.rs @@ -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 Default for AnvilChunkFile { } } -#[async_trait] pub trait SingleChunkDataSerializer: Send + Sync + Sized + Dirtiable { - async fn to_bytes(&self) -> Result; + fn to_bytes( + &self, + ) -> Pin> + Send + '_>>; fn from_bytes(bytes: Bytes, pos: Vector2) -> Result; fn position(&self) -> &Vector2; } diff --git a/pumpkin-world/src/chunk/format/mod.rs b/pumpkin-world/src/chunk/format/mod.rs index c271a84c9..b54eab32e 100644 --- a/pumpkin-world/src/chunk/format/mod.rs +++ b/pumpkin-world/src/chunk/format/mod.rs @@ -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) -> Result { @@ -38,8 +36,10 @@ impl SingleChunkDataSerializer for ChunkData { } #[inline] - async fn to_bytes(&self) -> Result { - self.internal_to_bytes().await + fn to_bytes( + &self, + ) -> Pin> + 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) -> Result { @@ -255,8 +254,10 @@ impl SingleChunkDataSerializer for ChunkEntityData { } #[inline] - async fn to_bytes(&self) -> Result { - self.internal_to_bytes() + fn to_bytes( + &self, + ) -> Pin> + Send + '_>> { + Box::pin(async move { self.internal_to_bytes() }) } #[inline] diff --git a/pumpkin-world/src/chunk_system.rs b/pumpkin-world/src/chunk_system.rs index 4a2419f4a..124839778 100644 --- a/pumpkin-world/src/chunk_system.rs +++ b/pumpkin-world/src/chunk_system.rs @@ -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 + 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 + 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 + Send + 'a>> { + Box::pin(async move { + let id = GenerationCache::get_block_state(self, &position.0); + (id.to_block(), id.to_state()) + }) } } diff --git a/pumpkin-world/src/generation/feature/placed_features.rs b/pumpkin-world/src/generation/feature/placed_features.rs index 40281636a..93a0bb27f 100644 --- a/pumpkin-world/src/generation/feature/placed_features.rs +++ b/pumpkin-world/src/generation/feature/placed_features.rs @@ -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( &self, @@ -399,7 +397,6 @@ pub struct SurfaceThresholdFilterPlacementModifier { max_inclusive: Option, } -#[async_trait] impl ConditionalPlacementModifier for SurfaceThresholdFilterPlacementModifier { fn should_place( &self, @@ -421,7 +418,6 @@ pub struct RarityFilterPlacementModifier { chance: u32, } -#[async_trait] impl ConditionalPlacementModifier for RarityFilterPlacementModifier { fn should_place( &self, @@ -465,7 +461,6 @@ pub struct SurfaceWaterDepthFilterPlacementModifier { max_water_depth: i32, } -#[async_trait] impl ConditionalPlacementModifier for SurfaceWaterDepthFilterPlacementModifier { fn should_place( &self, @@ -484,7 +479,6 @@ impl ConditionalPlacementModifier for SurfaceWaterDepthFilterPlacementModifier { #[derive(Deserialize)] pub struct BiomePlacementModifier; -#[async_trait] impl ConditionalPlacementModifier for BiomePlacementModifier { fn should_place( &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( &self, diff --git a/pumpkin-world/src/generation/proto_chunk.rs b/pumpkin-world/src/generation/proto_chunk.rs index 230795ffa..501046a66 100644 --- a/pumpkin-world/src/generation/proto_chunk.rs +++ b/pumpkin-world/src/generation/proto_chunk.rs @@ -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 + 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 + 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 + Send + 'a>> { + Box::pin(async move { + let id = self.get_block_state(&position.0); + BlockState::from_id_with_block(id.0) + }) } } diff --git a/pumpkin-world/src/inventory/inventory.rs b/pumpkin-world/src/inventory/inventory.rs index 7405dca39..ab0bfcf13 100644 --- a/pumpkin-world/src/inventory/inventory.rs +++ b/pumpkin-world/src/inventory/inventory.rs @@ -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 + Send + '_>>; } pub struct ComparableInventory(pub Arc); diff --git a/pumpkin-world/src/world.rs b/pumpkin-world/src/world.rs index 2cb645a19..e36b6cf8d 100644 --- a/pumpkin-world/src/world.rs +++ b/pumpkin-world/src/world.rs @@ -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 + Send + 'a>>; + pub trait SimpleWorld: BlockAccessor + Send + Sync { - async fn set_block_state( + fn set_block_state<'a>( self: Arc, - 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, - 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, - block_pos: &BlockPos, + block_pos: &'a BlockPos, except: Option, - ); + ) -> 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, entity_type: &'static EntityType, position: Vector3, - ); + ) -> 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>; - 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); - async fn play_sound_fine( - &self, + fn get_block_entity<'a>( + &'a self, + block_pos: &'a BlockPos, + ) -> WorldFuture<'a, Option>>; + + fn get_world_age<'a>(&'a self) -> WorldFuture<'a, i64>; + + fn play_sound<'a>( + &'a self, sound: Sound, category: SoundCategory, - position: &Vector3, + position: &'a Vector3, + ) -> WorldFuture<'a, ()>; + + fn play_sound_fine<'a>( + &'a self, + sound: Sound, + category: SoundCategory, + position: &'a Vector3, volume: f32, pitch: f32, - ); + ) -> WorldFuture<'a, ()>; /* ItemScatterer */ - async fn scatter_inventory( + fn scatter_inventory<'a>( self: Arc, - position: &BlockPos, - inventory: &Arc, - ); + position: &'a BlockPos, + inventory: &'a Arc, + ) -> 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 + Send + 'a>>; - async fn get_block_state(&self, position: &BlockPos) -> &'static BlockState; + fn get_block_state<'a>( + &'a self, + position: &'a BlockPos, + ) -> Pin + 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 + Send + 'a>>; } diff --git a/pumpkin/src/block/blocks/abstract_wall_mounting.rs b/pumpkin/src/block/blocks/abstract_wall_mounting.rs index ee35f4f70..a48f37b6b 100644 --- a/pumpkin/src/block/blocks/abstract_wall_mounting.rs +++ b/pumpkin/src/block/blocks/abstract_wall_mounting.rs @@ -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 + } + }) } } diff --git a/pumpkin/src/block/blocks/anvil.rs b/pumpkin/src/block/blocks/anvil.rs index b317fc73c..f52507cbd 100644 --- a/pumpkin/src/block/blocks/anvil.rs +++ b/pumpkin/src/block/blocks/anvil.rs @@ -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; + }) } } diff --git a/pumpkin/src/block/blocks/bamboo.rs b/pumpkin/src/block/blocks/bamboo.rs index e5b2fb5bb..ad6438e62 100644 --- a/pumpkin/src/block/blocks/bamboo.rs +++ b/pumpkin/src/block/blocks/bamboo.rs @@ -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) + }) } } diff --git a/pumpkin/src/block/blocks/banners.rs b/pumpkin/src/block/blocks/banners.rs index a8a8f2de1..192d0a67f 100644 --- a/pumpkin/src/block/blocks/banners.rs +++ b/pumpkin/src/block/blocks/banners.rs @@ -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) + }) } } diff --git a/pumpkin/src/block/blocks/barrel.rs b/pumpkin/src/block/blocks/barrel.rs index f0819406a..4454974ae 100644 --- a/pumpkin/src/block/blocks/barrel.rs +++ b/pumpkin/src/block/blocks/barrel.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use crate::block::{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; + }) } } diff --git a/pumpkin/src/block/blocks/barrier.rs b/pumpkin/src/block/blocks/barrier.rs index 5575b58f2..a5beabd6d 100644 --- a/pumpkin/src/block/blocks/barrier.rs +++ b/pumpkin/src/block/blocks/barrier.rs @@ -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) + }) } } diff --git a/pumpkin/src/block/blocks/bed.rs b/pumpkin/src/block/blocks/bed.rs index 221ba0b25..e66015faf 100644 --- a/pumpkin/src/block/blocks/bed.rs +++ b/pumpkin/src/block/blocks/bed.rs @@ -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 + }) } } diff --git a/pumpkin/src/block/blocks/blast_furnace.rs b/pumpkin/src/block/blocks/blast_furnace.rs index 58845786c..113dce59a 100644 --- a/pumpkin/src/block/blocks/blast_furnace.rs +++ b/pumpkin/src/block/blocks/blast_furnace.rs @@ -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) + }) } } diff --git a/pumpkin/src/block/blocks/cactus.rs b/pumpkin/src/block/blocks/cactus.rs index fddd52f3c..102326628 100644 --- a/pumpkin/src/block/blocks/cactus.rs +++ b/pumpkin/src/block/blocks/cactus.rs @@ -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 }) } } diff --git a/pumpkin/src/block/blocks/cake.rs b/pumpkin/src/block/blocks/cake.rs index 19c584d17..5c6b00306 100644 --- a/pumpkin/src/block/blocks/cake.rs +++ b/pumpkin/src/block/blocks/cake.rs @@ -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 + }) } } diff --git a/pumpkin/src/block/blocks/campfire.rs b/pumpkin/src/block/blocks/campfire.rs index 15fa6cda5..61a94fb66 100644 --- a/pumpkin/src/block/blocks/campfire.rs +++ b/pumpkin/src/block/blocks/campfire.rs @@ -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 diff --git a/pumpkin/src/block/blocks/candle_cakes.rs b/pumpkin/src/block/blocks/candle_cakes.rs index 644947805..80c38e907 100644 --- a/pumpkin/src/block/blocks/candle_cakes.rs +++ b/pumpkin/src/block/blocks/candle_cakes.rs @@ -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 + }) } } diff --git a/pumpkin/src/block/blocks/candles.rs b/pumpkin/src/block/blocks/candles.rs index 14495f46d..66e79f4e1 100644 --- a/pumpkin/src/block/blocks/candles.rs +++ b/pumpkin/src/block/blocks/candles.rs @@ -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 + }) } } diff --git a/pumpkin/src/block/blocks/carpet.rs b/pumpkin/src/block/blocks/carpet.rs index f2e41d4e2..85952ae4a 100644 --- a/pumpkin/src/block/blocks/carpet.rs +++ b/pumpkin/src/block/blocks/carpet.rs @@ -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; + } + }) } } diff --git a/pumpkin/src/block/blocks/carved_pumpkin.rs b/pumpkin/src/block/blocks/carved_pumpkin.rs index 1a0709a73..18622f838 100644 --- a/pumpkin/src/block/blocks/carved_pumpkin.rs +++ b/pumpkin/src/block/blocks/carved_pumpkin.rs @@ -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<'_>) {} } diff --git a/pumpkin/src/block/blocks/chain.rs b/pumpkin/src/block/blocks/chain.rs index 9c2ff7aae..b66a81284 100644 --- a/pumpkin/src/block/blocks/chain.rs +++ b/pumpkin/src/block/blocks/chain.rs @@ -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) + }) } } diff --git a/pumpkin/src/block/blocks/chests.rs b/pumpkin/src/block/blocks/chests.rs index 5c4f9204d..a08cc9224 100644 --- a/pumpkin/src/block/blocks/chests.rs +++ b/pumpkin/src/block/blocks/chests.rs @@ -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; + } + }) } } diff --git a/pumpkin/src/block/blocks/chiseled_bookshelf.rs b/pumpkin/src/block/blocks/chiseled_bookshelf.rs index b39ce60ef..b8d4afb85 100644 --- a/pumpkin/src/block/blocks/chiseled_bookshelf.rs +++ b/pumpkin/src/block/blocks/chiseled_bookshelf.rs @@ -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::() + { + 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::() { - 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> { + 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::() { - 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 { - if let Some(block_entity) = args.world.get_block_entity(args.position).await - && let Some(block_entity) = block_entity - .as_any() - .downcast_ref::() - { - return Some((block_entity.last_interacted_slot.load(Ordering::Relaxed) + 1) as u8); - } - None + None + }) } } diff --git a/pumpkin/src/block/blocks/command.rs b/pumpkin/src/block/blocks/command.rs index 019e7a209..75ad3e944 100644 --- a/pumpkin/src/block/blocks/command.rs +++ b/pumpkin/src/block/blocks/command.rs @@ -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::() - .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::() + .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 + }) } } diff --git a/pumpkin/src/block/blocks/composter.rs b/pumpkin/src/block/blocks/composter.rs index 97b8f1905..d8b8db1be 100644 --- a/pumpkin/src/block/blocks/composter.rs +++ b/pumpkin/src/block/blocks/composter.rs @@ -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 { - 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> { + Box::pin(async move { + let props = ComposterLikeProperties::from_state_id(args.state.id, args.block); + Some(props.get_level()) + }) } } diff --git a/pumpkin/src/block/blocks/crafting_table.rs b/pumpkin/src/block/blocks/crafting_table.rs index 985947fe9..36c094f85 100644 --- a/pumpkin/src/block/blocks/crafting_table.rs +++ b/pumpkin/src/block/blocks/crafting_table.rs @@ -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 + }) } } diff --git a/pumpkin/src/block/blocks/dirt_path.rs b/pumpkin/src/block/blocks/dirt_path.rs index 2ef302489..f2f97a9e4 100644 --- a/pumpkin/src/block/blocks/dirt_path.rs +++ b/pumpkin/src/block/blocks/dirt_path.rs @@ -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 }) } } diff --git a/pumpkin/src/block/blocks/doors.rs b/pumpkin/src/block/blocks/doors.rs index bfa42cbce..a5e0d3e5b 100644 --- a/pumpkin/src/block/blocks/doors.rs +++ b/pumpkin/src/block/blocks/doors.rs @@ -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 + }) } } diff --git a/pumpkin/src/block/blocks/end_portal.rs b/pumpkin/src/block/blocks/end_portal.rs index 37a763e11..82b08b5e2 100644 --- a/pumpkin/src/block/blocks/end_portal.rs +++ b/pumpkin/src/block/blocks/end_portal.rs @@ -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; + }) } } diff --git a/pumpkin/src/block/blocks/end_portal_frame.rs b/pumpkin/src/block/blocks/end_portal_frame.rs index 95cc21c85..7f7d1485b 100644 --- a/pumpkin/src/block/blocks/end_portal_frame.rs +++ b/pumpkin/src/block/blocks/end_portal_frame.rs @@ -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) + }) } } diff --git a/pumpkin/src/block/blocks/end_rod.rs b/pumpkin/src/block/blocks/end_rod.rs index 813216623..72a67be71 100644 --- a/pumpkin/src/block/blocks/end_rod.rs +++ b/pumpkin/src/block/blocks/end_rod.rs @@ -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) + }) } } diff --git a/pumpkin/src/block/blocks/ender_chest.rs b/pumpkin/src/block/blocks/ender_chest.rs index 479315cb3..18c4a0698 100644 --- a/pumpkin/src/block/blocks/ender_chest.rs +++ b/pumpkin/src/block/blocks/ender_chest.rs @@ -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::() - { - 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::() + { + 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; + }) } } diff --git a/pumpkin/src/block/blocks/falling.rs b/pumpkin/src/block/blocks/falling.rs index 6f0c9f414..f51b8a722 100644 --- a/pumpkin/src/block/blocks/falling.rs +++ b/pumpkin/src/block/blocks/falling.rs @@ -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; + }) } } diff --git a/pumpkin/src/block/blocks/farmland.rs b/pumpkin/src/block/blocks/farmland.rs index 9547ee328..5872df710 100644 --- a/pumpkin/src/block/blocks/farmland.rs +++ b/pumpkin/src/block/blocks/farmland.rs @@ -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; + } } - } + }) } } diff --git a/pumpkin/src/block/blocks/fence_gates.rs b/pumpkin/src/block/blocks/fence_gates.rs index 36152bd96..960f0967e 100644 --- a/pumpkin/src/block/blocks/fence_gates.rs +++ b/pumpkin/src/block/blocks/fence_gates.rs @@ -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 + }) } } diff --git a/pumpkin/src/block/blocks/fences.rs b/pumpkin/src/block/blocks/fences.rs index ad7ac63e2..fed466fc3 100644 --- a/pumpkin/src/block/blocks/fences.rs +++ b/pumpkin/src/block/blocks/fences.rs @@ -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 + }) } } diff --git a/pumpkin/src/block/blocks/fire/fire.rs b/pumpkin/src/block/blocks/fire/fire.rs index 70dd19b0d..62d6a0d21 100644 --- a/pumpkin/src/block/blocks/fire/fire.rs +++ b/pumpkin/src/block/blocks/fire/fire.rs @@ -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; + }) } } diff --git a/pumpkin/src/block/blocks/fire/soul_fire.rs b/pumpkin/src/block/blocks/fire/soul_fire.rs index 697446390..e57dfa324 100644 --- a/pumpkin/src/block/blocks/fire/soul_fire.rs +++ b/pumpkin/src/block/blocks/fire/soul_fire.rs @@ -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; + }) } } diff --git a/pumpkin/src/block/blocks/flower_pots.rs b/pumpkin/src/block/blocks/flower_pots.rs index 7ba8259aa..6b999c7f2 100644 --- a/pumpkin/src/block/blocks/flower_pots.rs +++ b/pumpkin/src/block/blocks/flower_pots.rs @@ -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; + } + }) } } diff --git a/pumpkin/src/block/blocks/furnace.rs b/pumpkin/src/block/blocks/furnace.rs index ee53b0c7d..1a8997c81 100644 --- a/pumpkin/src/block/blocks/furnace.rs +++ b/pumpkin/src/block/blocks/furnace.rs @@ -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, @@ -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 { - None + fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + args.world.remove_block_entity(args.position).await; + }) } } diff --git a/pumpkin/src/block/blocks/glass_panes.rs b/pumpkin/src/block/blocks/glass_panes.rs index e733b6ee5..eaefba45a 100644 --- a/pumpkin/src/block/blocks/glass_panes.rs +++ b/pumpkin/src/block/blocks/glass_panes.rs @@ -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 + }) } } diff --git a/pumpkin/src/block/blocks/glazed_terracotta.rs b/pumpkin/src/block/blocks/glazed_terracotta.rs index 27c054bbc..c27de8c70 100644 --- a/pumpkin/src/block/blocks/glazed_terracotta.rs +++ b/pumpkin/src/block/blocks/glazed_terracotta.rs @@ -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) + }) } } diff --git a/pumpkin/src/block/blocks/grindstone.rs b/pumpkin/src/block/blocks/grindstone.rs index 52d670904..5a898e02c 100644 --- a/pumpkin/src/block/blocks/grindstone.rs +++ b/pumpkin/src/block/blocks/grindstone.rs @@ -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 { diff --git a/pumpkin/src/block/blocks/hopper.rs b/pumpkin/src/block/blocks/hopper.rs index e00a10bec..50df90814 100644 --- a/pumpkin/src/block/blocks/hopper.rs +++ b/pumpkin/src/block/blocks/hopper.rs @@ -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; + }) } } diff --git a/pumpkin/src/block/blocks/iron_bars.rs b/pumpkin/src/block/blocks/iron_bars.rs index 8bc64c01a..86c5824bc 100644 --- a/pumpkin/src/block/blocks/iron_bars.rs +++ b/pumpkin/src/block/blocks/iron_bars.rs @@ -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 + }) } } diff --git a/pumpkin/src/block/blocks/jukebox.rs b/pumpkin/src/block/blocks/jukebox.rs index ccaa0b984..c3661df56 100644 --- a/pumpkin/src/block/blocks/jukebox.rs +++ b/pumpkin/src/block/blocks/jukebox.rs @@ -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::() - .map(|i| i.song); + let jukebox_playable = args + .item_stack + .lock() + .await + .get_data_component::() + .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; + }) } } diff --git a/pumpkin/src/block/blocks/ladder.rs b/pumpkin/src/block/blocks/ladder.rs index 3ae796dda..ea98e79ea 100644 --- a/pumpkin/src/block/blocks/ladder.rs +++ b/pumpkin/src/block/blocks/ladder.rs @@ -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; + } + }) } } diff --git a/pumpkin/src/block/blocks/lanterns.rs b/pumpkin/src/block/blocks/lanterns.rs index 0aaa144eb..ebf6a95ca 100644 --- a/pumpkin/src/block/blocks/lanterns.rs +++ b/pumpkin/src/block/blocks/lanterns.rs @@ -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; + } + }) } } diff --git a/pumpkin/src/block/blocks/lectern.rs b/pumpkin/src/block/blocks/lectern.rs index b5880680c..7aa205ae8 100644 --- a/pumpkin/src/block/blocks/lectern.rs +++ b/pumpkin/src/block/blocks/lectern.rs @@ -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) + }) } } diff --git a/pumpkin/src/block/blocks/logs.rs b/pumpkin/src/block/blocks/logs.rs index 9e74e4c93..794e2b0d8 100644 --- a/pumpkin/src/block/blocks/logs.rs +++ b/pumpkin/src/block/blocks/logs.rs @@ -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) + }) } } diff --git a/pumpkin/src/block/blocks/mangrove_roots.rs b/pumpkin/src/block/blocks/mangrove_roots.rs index d4c8cb4d6..1c496dc02 100644 --- a/pumpkin/src/block/blocks/mangrove_roots.rs +++ b/pumpkin/src/block/blocks/mangrove_roots.rs @@ -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) + }) } } diff --git a/pumpkin/src/block/blocks/nether_portal.rs b/pumpkin/src/block/blocks/nether_portal.rs index 3cdcb483a..9f2fc76af 100644 --- a/pumpkin/src/block/blocks/nether_portal.rs +++ b/pumpkin/src/block/blocks/nether_portal.rs @@ -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; + }) } } diff --git a/pumpkin/src/block/blocks/note.rs b/pumpkin/src/block/blocks/note.rs index 8108236cf..95486c45c 100644 --- a/pumpkin/src/block/blocks/note.rs +++ b/pumpkin/src/block/blocks/note.rs @@ -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(¬e_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(¬e_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(¬e_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(¬e_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 + }) } } diff --git a/pumpkin/src/block/blocks/piston/piston.rs b/pumpkin/src/block/blocks/piston/piston.rs index ee953997c..524d116d5 100644 --- a/pumpkin/src/block/blocks/piston/piston.rs +++ b/pumpkin/src/block/blocks/piston/piston.rs @@ -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::() + .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::() - .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::().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::().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 + }) } } diff --git a/pumpkin/src/block/blocks/piston/piston_extension.rs b/pumpkin/src/block/blocks/piston/piston_extension.rs index 3fce3222c..8277c2093 100644 --- a/pumpkin/src/block/blocks/piston/piston_extension.rs +++ b/pumpkin/src/block/blocks/piston/piston_extension.rs @@ -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; + } } - } + }) } } diff --git a/pumpkin/src/block/blocks/piston/piston_head.rs b/pumpkin/src/block/blocks/piston/piston_head.rs index 1aa8244bb..896b91983 100644 --- a/pumpkin/src/block/blocks/piston/piston_head.rs +++ b/pumpkin/src/block/blocks/piston/piston_head.rs @@ -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; + } } - } + }) } } diff --git a/pumpkin/src/block/blocks/plant/bush.rs b/pumpkin/src/block/blocks/plant/bush.rs index bf26b1269..3f681a289 100644 --- a/pumpkin/src/block/blocks/plant/bush.rs +++ b/pumpkin/src/block/blocks/plant/bush.rs @@ -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 { - ::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 { + ::can_place_at(self, args.block_accessor, args.position).await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + }) } } diff --git a/pumpkin/src/block/blocks/plant/crop/beetroot.rs b/pumpkin/src/block/blocks/plant/crop/beetroot.rs index f526e5536..73cfc7272 100644 --- a/pumpkin/src/block/blocks/plant/crop/beetroot.rs +++ b/pumpkin/src/block/blocks/plant/crop/beetroot.rs @@ -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 { - ::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 { + ::can_plant_on_top( + self, + args.block_accessor, + &args.position.down(), + ) .await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::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 { - ::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 { + ::random_tick(self, args.world, args.position).await; + } + }) } } diff --git a/pumpkin/src/block/blocks/plant/crop/carrot.rs b/pumpkin/src/block/blocks/plant/crop/carrot.rs index 8a0acdcc1..4dfbed95a 100644 --- a/pumpkin/src/block/blocks/plant/crop/carrot.rs +++ b/pumpkin/src/block/blocks/plant/crop/carrot.rs @@ -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 { - ::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 { + ::can_plant_on_top( + self, + args.block_accessor, + &args.position.down(), + ) .await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + }) } - async fn random_tick(&self, args: RandomTickArgs<'_>) { - ::random_tick(self, args.world, args.position).await; + fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + ::random_tick(self, args.world, args.position).await; + }) } } diff --git a/pumpkin/src/block/blocks/plant/crop/gourds/attached_stem.rs b/pumpkin/src/block/blocks/plant/crop/gourds/attached_stem.rs index c39929c6d..0db90ea3c 100644 --- a/pumpkin/src/block/blocks/plant/crop/gourds/attached_stem.rs +++ b/pumpkin/src/block/blocks/plant/crop/gourds/attached_stem.rs @@ -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 { - ::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 { + ::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)); - } - ::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)); + } + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + }) } } diff --git a/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs b/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs index 5045e193e..520278e28 100644 --- a/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs +++ b/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs @@ -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 { - ::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 { + ::can_place_at(self, args.block_accessor, args.position).await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::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; + } } } - } + }) } } diff --git a/pumpkin/src/block/blocks/plant/crop/potatoes.rs b/pumpkin/src/block/blocks/plant/crop/potatoes.rs index 1e28868e0..2590b9dd2 100644 --- a/pumpkin/src/block/blocks/plant/crop/potatoes.rs +++ b/pumpkin/src/block/blocks/plant/crop/potatoes.rs @@ -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 { - ::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 { + ::can_plant_on_top( + self, + args.block_accessor, + &args.position.down(), + ) .await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + }) } - async fn random_tick(&self, args: RandomTickArgs<'_>) { - ::random_tick(self, args.world, args.position).await; + fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + ::random_tick(self, args.world, args.position).await; + }) } } diff --git a/pumpkin/src/block/blocks/plant/crop/torch_flower.rs b/pumpkin/src/block/blocks/plant/crop/torch_flower.rs index ead8ab448..13fd89333 100644 --- a/pumpkin/src/block/blocks/plant/crop/torch_flower.rs +++ b/pumpkin/src/block/blocks/plant/crop/torch_flower.rs @@ -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 { - ::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 { + ::can_plant_on_top( + self, + args.block_accessor, + &args.position.down(), + ) .await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::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 { - ::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 { + ::random_tick(self, args.world, args.position).await; + } + }) } } diff --git a/pumpkin/src/block/blocks/plant/crop/wheat.rs b/pumpkin/src/block/blocks/plant/crop/wheat.rs index e8d00d421..a795d4f94 100644 --- a/pumpkin/src/block/blocks/plant/crop/wheat.rs +++ b/pumpkin/src/block/blocks/plant/crop/wheat.rs @@ -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 { - ::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 { + ::can_plant_on_top( + self, + args.block_accessor, + &args.position.down(), + ) .await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + }) } - async fn random_tick(&self, args: RandomTickArgs<'_>) { - ::random_tick(self, args.world, args.position).await; + fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + ::random_tick(self, args.world, args.position).await; + }) } } diff --git a/pumpkin/src/block/blocks/plant/dry_vegetation.rs b/pumpkin/src/block/blocks/plant/dry_vegetation.rs index 9b196c1bf..3a762ad4a 100644 --- a/pumpkin/src/block/blocks/plant/dry_vegetation.rs +++ b/pumpkin/src/block/blocks/plant/dry_vegetation.rs @@ -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 { - ::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 { + ::can_place_at(self, args.block_accessor, args.position).await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + }) } } diff --git a/pumpkin/src/block/blocks/plant/flower.rs b/pumpkin/src/block/blocks/plant/flower.rs index 59eb2c5f2..9802178df 100644 --- a/pumpkin/src/block/blocks/plant/flower.rs +++ b/pumpkin/src/block/blocks/plant/flower.rs @@ -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 { - ::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 { + ::can_place_at(self, args.block_accessor, args.position).await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::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; + } + }) } } diff --git a/pumpkin/src/block/blocks/plant/flowerbed.rs b/pumpkin/src/block/blocks/plant/flowerbed.rs index 3fb5491d1..80f684091 100644 --- a/pumpkin/src/block/blocks/plant/flowerbed.rs +++ b/pumpkin/src/block/blocks/plant/flowerbed.rs @@ -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 { - ::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 { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + }) } } diff --git a/pumpkin/src/block/blocks/plant/leaf_litter.rs b/pumpkin/src/block/blocks/plant/leaf_litter.rs index 9d12687ae..fce4b6f98 100644 --- a/pumpkin/src/block/blocks/plant/leaf_litter.rs +++ b/pumpkin/src/block/blocks/plant/leaf_litter.rs @@ -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 + }) } } diff --git a/pumpkin/src/block/blocks/plant/lily_pad.rs b/pumpkin/src/block/blocks/plant/lily_pad.rs index 3a2dbfb79..de6f78fdb 100644 --- a/pumpkin/src/block/blocks/plant/lily_pad.rs +++ b/pumpkin/src/block/blocks/plant/lily_pad.rs @@ -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 { - ::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 { + ::can_place_at(self, args.block_accessor, args.position).await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + }) } } diff --git a/pumpkin/src/block/blocks/plant/mushroom_plant.rs b/pumpkin/src/block/blocks/plant/mushroom_plant.rs index e664f69bb..2175d26c0 100644 --- a/pumpkin/src/block/blocks/plant/mushroom_plant.rs +++ b/pumpkin/src/block/blocks/plant/mushroom_plant.rs @@ -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 { - ::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 { + ::can_place_at(self, args.block_accessor, args.position).await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + }) } } diff --git a/pumpkin/src/block/blocks/plant/nether_wart.rs b/pumpkin/src/block/blocks/plant/nether_wart.rs index 8468b966e..e5e98aebf 100644 --- a/pumpkin/src/block/blocks/plant/nether_wart.rs +++ b/pumpkin/src/block/blocks/plant/nether_wart.rs @@ -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 { - ::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 { + ::can_place_at(self, args.block_accessor, args.position).await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + }) } } diff --git a/pumpkin/src/block/blocks/plant/roots.rs b/pumpkin/src/block/blocks/plant/roots.rs index 022c44b57..7ec4f8b97 100644 --- a/pumpkin/src/block/blocks/plant/roots.rs +++ b/pumpkin/src/block/blocks/plant/roots.rs @@ -1,9 +1,9 @@ -use async_trait::async_trait; use pumpkin_data::tag::Taggable; use pumpkin_data::{Block, tag}; use pumpkin_util::math::position::BlockPos; use pumpkin_world::{BlockStateId, world::BlockAccessor}; +use crate::block::BlockFuture; use crate::block::{ BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase, @@ -21,23 +21,26 @@ impl BlockMetadata for RootsBlock { } } -#[async_trait] impl BlockBehaviour for RootsBlock { - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - ::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 { + ::can_place_at(self, args.block_accessor, args.position).await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + }) } } diff --git a/pumpkin/src/block/blocks/plant/sapling.rs b/pumpkin/src/block/blocks/plant/sapling.rs index 03bd57cf0..e1f40b3c2 100644 --- a/pumpkin/src/block/blocks/plant/sapling.rs +++ b/pumpkin/src/block/blocks/plant/sapling.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::block_properties::{BlockProperties, Integer0To1}; use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_macros::pumpkin_block_from_tag; @@ -8,7 +7,9 @@ use pumpkin_world::world::BlockFlags; use std::sync::Arc; use crate::block::blocks::plant::PlantBlockBase; -use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs}; +use crate::block::{ + BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs, +}; use crate::world::World; type SaplingProperties = pumpkin_data::block_properties::OakSaplingLikeProperties; @@ -31,27 +32,32 @@ impl SaplingBlock { } } -#[async_trait] impl BlockBehaviour for SaplingBlock { - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - ::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 { + ::can_place_at(self, args.block_accessor, args.position).await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + }) } - async fn random_tick(&self, args: RandomTickArgs<'_>) { - self.generate(args.world, args.position).await; + fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + self.generate(args.world, args.position).await; + }) } } diff --git a/pumpkin/src/block/blocks/plant/sea_grass.rs b/pumpkin/src/block/blocks/plant/sea_grass.rs index ade7ebc10..a4e71303e 100644 --- a/pumpkin/src/block/blocks/plant/sea_grass.rs +++ b/pumpkin/src/block/blocks/plant/sea_grass.rs @@ -1,33 +1,35 @@ -use async_trait::async_trait; use pumpkin_data::{Block, BlockDirection}; use pumpkin_macros::pumpkin_block; use pumpkin_world::BlockStateId; use crate::block::{ + BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase, - {BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs}, }; #[pumpkin_block("minecraft:seagrass")] pub struct SeaGrassBlock; -#[async_trait] impl BlockBehaviour for SeaGrassBlock { - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - ::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 { + ::can_place_at(self, args.block_accessor, args.position).await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + }) } } diff --git a/pumpkin/src/block/blocks/plant/sea_pickles.rs b/pumpkin/src/block/blocks/plant/sea_pickles.rs index 1e15a966a..467522a0f 100644 --- a/pumpkin/src/block/blocks/plant/sea_pickles.rs +++ b/pumpkin/src/block/blocks/plant/sea_pickles.rs @@ -1,12 +1,11 @@ -use crate::block::BlockIsReplacing; use crate::block::blocks::plant::PlantBlockBase; use crate::block::registry::BlockActionResult; use crate::block::{ BlockBehaviour, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, UseWithItemArgs, }; +use crate::block::{BlockFuture, BlockIsReplacing}; use crate::entity::EntityBase; -use async_trait::async_trait; use pumpkin_data::block_properties::{BlockProperties, Integer1To4}; use pumpkin_data::entity::EntityPose; use pumpkin_data::item::Item; @@ -23,134 +22,149 @@ type SeaPickleProperties = pumpkin_data::block_properties::SeaPickleLikeProperti #[pumpkin_block("minecraft:sea_pickle")] pub struct SeaPickleBlock; -#[async_trait] impl BlockBehaviour for SeaPickleBlock { #[allow(clippy::many_single_char_names)] - async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { - if args.item_stack.lock().await.item != &Item::BONE_MEAL - || !args - .world - .get_block(&args.position.down()) - .await - .has_tag(&tag::Block::MINECRAFT_CORAL_BLOCKS) - || !SeaPickleProperties::from_state_id( - args.world.get_block_state_id(args.position).await, - args.block, - ) - .waterlogged - { - return BlockActionResult::Pass; - } + fn use_with_item<'a>( + &'a self, + args: UseWithItemArgs<'a>, + ) -> BlockFuture<'a, BlockActionResult> { + Box::pin(async move { + if args.item_stack.lock().await.item != &Item::BONE_MEAL + || !args + .world + .get_block(&args.position.down()) + .await + .has_tag(&tag::Block::MINECRAFT_CORAL_BLOCKS) + || !SeaPickleProperties::from_state_id( + args.world.get_block_state_id(args.position).await, + args.block, + ) + .waterlogged + { + return BlockActionResult::Pass; + } - //1:1 vanilla algorithm - //TODO use pumpkin random + //1:1 vanilla algorithm + //TODO use pumpkin random - //let mut j = 1; - let mut count = 0; - let base_x = args.position.0.x - 2; - let mut removed_z = 0; - for added_x in 0..5 { - for added_z in 0..1 { - let temp_y = 2 + args.position.0.y - 1; - for y in (temp_y - 2)..temp_y { - //let mut lv2: BlockState; - let lv = - BlockPos::new(base_x + added_x, y, args.position.0.z - removed_z + added_z); - if &lv == args.position - || rand::rng().random_range(0..6) != 0 - || !args.world.get_block(&lv).await.eq(&Block::WATER) - || !args - .world - .get_block(&lv.down()) - .await - .has_tag(&tag::Block::MINECRAFT_CORAL_BLOCKS) - { - continue; + //let mut j = 1; + let mut count = 0; + let base_x = args.position.0.x - 2; + let mut removed_z = 0; + for added_x in 0..5 { + for added_z in 0..1 { + let temp_y = 2 + args.position.0.y - 1; + for y in (temp_y - 2)..temp_y { + //let mut lv2: BlockState; + let lv = BlockPos::new( + base_x + added_x, + y, + args.position.0.z - removed_z + added_z, + ); + if &lv == args.position + || rand::rng().random_range(0..6) != 0 + || !args.world.get_block(&lv).await.eq(&Block::WATER) + || !args + .world + .get_block(&lv.down()) + .await + .has_tag(&tag::Block::MINECRAFT_CORAL_BLOCKS) + { + continue; + } + let mut sea_pickle_prop = SeaPickleProperties::default(args.block); + + sea_pickle_prop.pickles = match rand::rng().random_range(0..4) + 1 { + 1 => Integer1To4::L1, + 2 => Integer1To4::L2, + 3 => Integer1To4::L3, + _ => Integer1To4::L4, + }; + args.world + .set_block_state( + &lv, + sea_pickle_prop.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ) + .await; } - let mut sea_pickle_prop = SeaPickleProperties::default(args.block); + } + if count < 2 { + //j += 2; + removed_z += 1; + } else { + //j -= 2; + removed_z -= 1; + } + count += 1; + } + let mut sea_pickle_prop = SeaPickleProperties::default(args.block); + sea_pickle_prop.pickles = Integer1To4::L4; + args.world + .set_block_state( + args.position, + sea_pickle_prop.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ) + .await; - sea_pickle_prop.pickles = match rand::rng().random_range(0..4) + 1 { - 1 => Integer1To4::L1, - 2 => Integer1To4::L2, - 3 => Integer1To4::L3, + BlockActionResult::Consume + }) + } + + 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 sea_pickle_prop = SeaPickleProperties::from_state_id(state_id, args.block); + if sea_pickle_prop.pickles != Integer1To4::L4 { + sea_pickle_prop.pickles = match sea_pickle_prop.pickles { + Integer1To4::L1 => Integer1To4::L2, + Integer1To4::L2 => Integer1To4::L3, _ => Integer1To4::L4, }; - args.world - .set_block_state( - &lv, - sea_pickle_prop.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; } + return sea_pickle_prop.to_state_id(args.block); } - if count < 2 { - //j += 2; - removed_z += 1; - } else { - //j -= 2; - removed_z -= 1; - } - count += 1; - } - let mut sea_pickle_prop = SeaPickleProperties::default(args.block); - sea_pickle_prop.pickles = Integer1To4::L4; - args.world - .set_block_state( + + let mut sea_pickle_prop = SeaPickleProperties::default(args.block); + sea_pickle_prop.waterlogged = args.replacing.water_source(); + sea_pickle_prop.to_state_id(args.block) + }) + } + + fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> { + Box::pin(async move { + let support_block = args + .block_accessor + .get_block_state(&args.position.down()) + .await; + support_block.is_center_solid(BlockDirection::Up) + }) + } + + fn can_update_at<'a>(&'a self, args: CanUpdateAtArgs<'a>) -> BlockFuture<'a, bool> { + Box::pin(async move { + args.player.get_entity().pose.load() != EntityPose::Crouching + && SeaPickleProperties::from_state_id(args.state_id, args.block).pickles + != Integer1To4::L4 + }) + } + + fn get_state_for_neighbor_update<'a>( + &'a self, + args: GetStateForNeighborUpdateArgs<'a>, + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + ::get_state_for_neighbor_update( + self, + args.world, args.position, - sea_pickle_prop.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, + args.state_id, ) - .await; - - BlockActionResult::Consume - } - - 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 sea_pickle_prop = SeaPickleProperties::from_state_id(state_id, args.block); - if sea_pickle_prop.pickles != Integer1To4::L4 { - sea_pickle_prop.pickles = match sea_pickle_prop.pickles { - Integer1To4::L1 => Integer1To4::L2, - Integer1To4::L2 => Integer1To4::L3, - _ => Integer1To4::L4, - }; - } - return sea_pickle_prop.to_state_id(args.block); - } - - let mut sea_pickle_prop = SeaPickleProperties::default(args.block); - sea_pickle_prop.waterlogged = args.replacing.water_source(); - sea_pickle_prop.to_state_id(args.block) - } - - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - let support_block = args - .block_accessor - .get_block_state(&args.position.down()) - .await; - support_block.is_center_solid(BlockDirection::Up) - } - - async fn can_update_at(&self, args: CanUpdateAtArgs<'_>) -> bool { - args.player.get_entity().pose.load() != EntityPose::Crouching - && SeaPickleProperties::from_state_id(args.state_id, args.block).pickles - != Integer1To4::L4 - } - - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::get_state_for_neighbor_update( - self, - args.world, - args.position, - args.state_id, - ) - .await + .await + }) } } diff --git a/pumpkin/src/block/blocks/plant/short_plant.rs b/pumpkin/src/block/blocks/plant/short_plant.rs index 7dfa096bb..83d0b2a07 100644 --- a/pumpkin/src/block/blocks/plant/short_plant.rs +++ b/pumpkin/src/block/blocks/plant/short_plant.rs @@ -1,8 +1,7 @@ -use async_trait::async_trait; use pumpkin_world::BlockStateId; use crate::block::{ - BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, + BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase, }; @@ -18,23 +17,26 @@ impl BlockMetadata for ShortPlantBlock { } } -#[async_trait] impl BlockBehaviour for ShortPlantBlock { - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - ::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 { + ::can_place_at(self, args.block_accessor, args.position).await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - ::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 { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + }) } } diff --git a/pumpkin/src/block/blocks/plant/tall_plant.rs b/pumpkin/src/block/blocks/plant/tall_plant.rs index 27ffb8568..f5aca8a3c 100644 --- a/pumpkin/src/block/blocks/plant/tall_plant.rs +++ b/pumpkin/src/block/blocks/plant/tall_plant.rs @@ -1,10 +1,10 @@ -use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::block_properties::{ BlockProperties, DoubleBlockHalf, TallSeagrassLikeProperties, }; use pumpkin_world::BlockStateId; +use crate::block::BlockFuture; use crate::block::{ BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase, @@ -31,40 +31,44 @@ impl BlockMetadata for TallPlantBlock { } } -#[async_trait] impl BlockBehaviour for TallPlantBlock { - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - let upper_state = args - .block_accessor - .get_block_state(&args.position.up()) - .await; - ::can_place_at(self, args.block_accessor, args.position).await - && upper_state.is_air() + fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> { + Box::pin(async move { + let upper_state = args + .block_accessor + .get_block_state(&args.position.up()) + .await; + ::can_place_at(self, args.block_accessor, args.position).await + && upper_state.is_air() + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - let tall_plant_props = TallSeagrassLikeProperties::from_state_id(args.state_id, args.block); - let other_block_pos = match tall_plant_props.half { - DoubleBlockHalf::Upper => args.position.down(), - DoubleBlockHalf::Lower => args.position.up(), - }; - let (other_block, other_state_id) = - args.world.get_block_and_state_id(&other_block_pos).await; - if self.ids().contains(&other_block.name) { - let other_props = - TallSeagrassLikeProperties::from_state_id(other_state_id, other_block); - let opposite_half = match tall_plant_props.half { - DoubleBlockHalf::Upper => DoubleBlockHalf::Lower, - DoubleBlockHalf::Lower => DoubleBlockHalf::Upper, + fn get_state_for_neighbor_update<'a>( + &'a self, + args: GetStateForNeighborUpdateArgs<'a>, + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let tall_plant_props = + TallSeagrassLikeProperties::from_state_id(args.state_id, args.block); + let other_block_pos = match tall_plant_props.half { + DoubleBlockHalf::Upper => args.position.down(), + DoubleBlockHalf::Lower => args.position.up(), }; - if other_props.half == opposite_half { - return args.state_id; + let (other_block, other_state_id) = + args.world.get_block_and_state_id(&other_block_pos).await; + if self.ids().contains(&other_block.name) { + let other_props = + TallSeagrassLikeProperties::from_state_id(other_state_id, other_block); + let opposite_half = match tall_plant_props.half { + DoubleBlockHalf::Upper => DoubleBlockHalf::Lower, + DoubleBlockHalf::Lower => DoubleBlockHalf::Upper, + }; + if other_props.half == opposite_half { + return args.state_id; + } } - } - return Block::AIR.default_state.id; + Block::AIR.default_state.id + }) } } diff --git a/pumpkin/src/block/blocks/pumpkin.rs b/pumpkin/src/block/blocks/pumpkin.rs index 37aed37e8..aa2519742 100644 --- a/pumpkin/src/block/blocks/pumpkin.rs +++ b/pumpkin/src/block/blocks/pumpkin.rs @@ -1,8 +1,7 @@ -use crate::block::UseWithItemArgs; use crate::block::registry::BlockActionResult; +use crate::block::{BlockFuture, UseWithItemArgs}; use crate::entity::Entity; use crate::entity::item::ItemEntity; -use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties}; use pumpkin_data::entity::EntityType; @@ -16,36 +15,40 @@ use uuid::Uuid; #[pumpkin_block("minecraft:pumpkin")] pub struct PumpkinBlock; -#[async_trait] impl crate::block::BlockBehaviour for PumpkinBlock { - async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { - if args.item_stack.lock().await.item != &Item::SHEARS { - return BlockActionResult::Pass; - } - let mut props = WallTorchLikeProperties::default(&Block::CARVED_PUMPKIN); - props.facing = args - .player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - args.world - .set_block_state( - args.position, - props.to_state_id(&Block::CARVED_PUMPKIN), - BlockFlags::NOTIFY_ALL, - ) - .await; - let entity = Entity::new( - Uuid::new_v4(), - args.world.clone(), - args.position.to_f64(), - &EntityType::ITEM, - false, - ); - let item_entity = - Arc::new(ItemEntity::new(entity, ItemStack::new(4, &Item::PUMPKIN_SEEDS)).await); - args.world.spawn_entity(item_entity).await; - BlockActionResult::Consume + fn use_with_item<'a>( + &'a self, + args: UseWithItemArgs<'a>, + ) -> BlockFuture<'a, BlockActionResult> { + Box::pin(async move { + if args.item_stack.lock().await.item != &Item::SHEARS { + return BlockActionResult::Pass; + } + let mut props = WallTorchLikeProperties::default(&Block::CARVED_PUMPKIN); + props.facing = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + args.world + .set_block_state( + args.position, + props.to_state_id(&Block::CARVED_PUMPKIN), + BlockFlags::NOTIFY_ALL, + ) + .await; + let entity = Entity::new( + Uuid::new_v4(), + args.world.clone(), + args.position.to_f64(), + &EntityType::ITEM, + false, + ); + let item_entity = + Arc::new(ItemEntity::new(entity, ItemStack::new(4, &Item::PUMPKIN_SEEDS)).await); + args.world.spawn_entity(item_entity).await; + BlockActionResult::Consume + }) } } diff --git a/pumpkin/src/block/blocks/redstone/abstract_redstone_gate.rs b/pumpkin/src/block/blocks/redstone/abstract_redstone_gate.rs index eae62e094..8e599c44c 100644 --- a/pumpkin/src/block/blocks/redstone/abstract_redstone_gate.rs +++ b/pumpkin/src/block/blocks/redstone/abstract_redstone_gate.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use async_trait::async_trait; use pumpkin_data::{ Block, BlockDirection, BlockState, HorizontalFacingExt, block_properties::{ @@ -16,7 +15,10 @@ use pumpkin_world::{ }; use crate::{ - block::{GetRedstonePowerArgs, OnNeighborUpdateArgs, OnStateReplacedArgs, PlayerPlacedArgs}, + block::{ + BlockFuture, GetRedstonePowerArgs, OnNeighborUpdateArgs, OnStateReplacedArgs, + PlayerPlacedArgs, + }, entity::player::Player, world::World, }; @@ -29,178 +31,247 @@ pub trait RedstoneGateBlockProperties { fn set_facing(&mut self, facing: HorizontalFacing); } -#[async_trait] -pub trait RedstoneGateBlock { - async fn can_place_at(&self, world: &dyn BlockAccessor, pos: BlockPos) -> bool { - let under_pos = pos.down(); - let under_state = world.get_block_state(&under_pos).await; - self.can_place_above(world, under_pos, under_state).await +pub trait RedstoneGateBlock { + // 💡 Converted async fn to fn returning BlockFuture + fn can_place_at<'a>( + &'a self, + world: &'a dyn BlockAccessor, + pos: BlockPos, + ) -> BlockFuture<'a, bool> + where + Self: Send + Sync, + { + Box::pin(async move { + let under_pos = pos.down(); + let under_state = world.get_block_state(&under_pos).await; + self.can_place_above(world, under_pos, under_state).await + }) } - async fn can_place_above( - &self, - _world: &dyn BlockAccessor, + // 💡 Converted async fn to fn returning BlockFuture + fn can_place_above<'a>( + &'a self, + _world: &'a dyn BlockAccessor, _pos: BlockPos, - state: &BlockState, - ) -> bool { - state.is_side_solid(BlockDirection::Up) + state: &'a BlockState, + ) -> BlockFuture<'a, bool> { + Box::pin(async { state.is_side_solid(BlockDirection::Up) }) } - async fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - let props = T::from_state_id(args.state.id, args.block); - if props.is_powered() && props.get_facing().to_block_direction() == args.direction { - self.get_output_level(args.world, *args.position).await - } else { - 0 - } + fn get_weak_redstone_power<'a>(&'a self, args: GetRedstonePowerArgs<'a>) -> BlockFuture<'a, u8> + where + Self: Send + Sync, + { + Box::pin(async move { + let props = T::from_state_id(args.state.id, args.block); + if props.is_powered() && props.get_facing().to_block_direction() == args.direction { + self.get_output_level(args.world, *args.position).await + } else { + 0 + } + }) } - async fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - self.get_weak_redstone_power(args).await + fn get_strong_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> + where + Self: Send + Sync, + { + Box::pin(async move { self.get_weak_redstone_power(args).await }) } - async fn get_output_level(&self, world: &World, pos: BlockPos) -> u8; + // 💡 Converted async fn to fn returning BlockFuture + fn get_output_level<'a>(&'a self, world: &'a World, pos: BlockPos) -> BlockFuture<'a, u8>; - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - let state = args.world.get_block_state(args.position).await; - if RedstoneGateBlock::can_place_at(self, args.world.as_ref(), *args.position).await { - self.update_powered(args.world, *args.position, state, args.block) + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> + where + Self: Send + Sync, + { + Box::pin(async move { + let state = args.world.get_block_state(args.position).await; + if RedstoneGateBlock::can_place_at(self, args.world.as_ref(), *args.position).await { + self.update_powered(args.world, *args.position, state, args.block) + .await; + return; + } + args.world + .set_block_state( + args.position, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_ALL, + ) .await; - return; - } - args.world - .set_block_state( - args.position, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_ALL, + for dir in BlockDirection::all() { + args.world + .update_neighbor(&args.position.offset(dir.to_offset()), args.source_block) + .await; + } + }) + } + + // 💡 Converted async fn to fn returning BlockFuture + fn update_powered<'a>( + &'a self, + world: &'a World, + pos: BlockPos, + state: &'a BlockState, + block: &'a Block, + ) -> BlockFuture<'a, ()>; + + // 💡 Converted async fn to fn returning BlockFuture + fn has_power<'a>( + &'a self, + world: &'a World, + pos: BlockPos, + state: &'a BlockState, + block: &'a Block, + ) -> BlockFuture<'a, bool> + where + Self: Send + Sync, + { + Box::pin(async move { self.get_power(world, pos, state, block).await > 0 }) + } + + // 💡 Converted async fn to fn returning BlockFuture + fn get_power<'a>( + &'a self, + world: &'a World, + pos: BlockPos, + state: &'a BlockState, + block: &'a Block, + ) -> BlockFuture<'a, u8> + where + Self: Send + Sync, + { + Box::pin(async move { get_power::(world, pos, state.id, block).await }) + } + + // 💡 Converted async fn to fn returning BlockFuture + fn get_max_input_level_sides<'a>( + &'a self, + world: &'a World, + pos: BlockPos, + state_id: BlockStateId, + block: &'a Block, + only_gate: bool, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { + let props = T::from_state_id(state_id, block); + let facing = props.get_facing(); + + let power_left = + get_power_on_side(world, &pos, facing.rotate_clockwise(), only_gate).await; + let power_right = + get_power_on_side(world, &pos, facing.rotate_counter_clockwise(), only_gate).await; + + std::cmp::max(power_left, power_right) + }) + } + + // 💡 Converted async fn to fn returning BlockFuture + fn update_target<'a>( + &'a self, + world: &'a Arc, + pos: BlockPos, + state_id: BlockStateId, + block: &'a Block, + ) -> BlockFuture<'a, ()> { + Box::pin(async move { + let props = T::from_state_id(state_id, block); + let facing = props.get_facing(); + let front_pos = pos.offset(facing.opposite().to_offset()); + world.update_neighbor(&front_pos, block).await; + world + .update_neighbors(&front_pos, Some(facing.to_block_direction())) + .await; + }) + } + + // 💡 Converted async fn to fn returning BlockFuture + fn on_place<'a>( + &'a self, + player: &'a Player, + block: &'a Block, + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async { + let mut props = T::default(block); + let dir = player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + props.set_facing(dir); + + props.to_state_id(block) + }) + } + + fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> + where + Self: Send + Sync, + { + Box::pin(async move { + if RedstoneGateBlock::has_power( + self, + args.world, + *args.position, + BlockState::from_id(args.state_id), + args.block, + ) + .await + { + args.world + .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal) + .await; + } + }) + } + + fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> + where + Self: Send + Sync, + { + Box::pin(async move { + if args.moved || Block::from_state_id(args.old_state_id) == args.block { + return; + } + RedstoneGateBlock::update_target( + self, + args.world, + *args.position, + BlockState::from_id(args.old_state_id).id, + args.block, ) .await; - for dir in BlockDirection::all() { - args.world - .update_neighbor(&args.position.offset(dir.to_offset()), args.source_block) + }) + } + + // 💡 Converted async fn to fn returning BlockFuture + fn is_target_not_aligned<'a>( + &'a self, + world: &'a dyn BlockAccessor, + pos: BlockPos, + state: &'a BlockState, + block: &'a Block, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { + let props = T::from_state_id(state.id, block); + let facing = props.get_facing().opposite(); + let (target_block, target_state) = world + .get_block_and_state(&pos.offset(facing.to_offset())) .await; - } - } - - async fn update_powered(&self, world: &World, pos: BlockPos, state: &BlockState, block: &Block); - - async fn has_power( - &self, - world: &World, - pos: BlockPos, - state: &BlockState, - block: &Block, - ) -> bool { - self.get_power(world, pos, state, block).await > 0 - } - - async fn get_power( - &self, - world: &World, - pos: BlockPos, - state: &BlockState, - block: &Block, - ) -> u8 { - get_power::(world, pos, state.id, block).await - } - - async fn get_max_input_level_sides( - &self, - world: &World, - pos: BlockPos, - state_id: BlockStateId, - block: &Block, - only_gate: bool, - ) -> u8 { - let props = T::from_state_id(state_id, block); - let facing = props.get_facing(); - - let power_left = get_power_on_side(world, &pos, facing.rotate_clockwise(), only_gate).await; - let power_right = - get_power_on_side(world, &pos, facing.rotate_counter_clockwise(), only_gate).await; - - std::cmp::max(power_left, power_right) - } - - async fn update_target( - &self, - world: &Arc, - pos: BlockPos, - state_id: BlockStateId, - block: &Block, - ) { - let props = T::from_state_id(state_id, block); - let facing = props.get_facing(); - let front_pos = pos.offset(facing.opposite().to_offset()); - world.update_neighbor(&front_pos, block).await; - world - .update_neighbors(&front_pos, Some(facing.to_block_direction())) - .await; - } - - async fn on_place(&self, player: &Player, block: &Block) -> BlockStateId { - let mut props = T::default(block); - let dir = player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - props.set_facing(dir); - - props.to_state_id(block) - } - - async fn player_placed(&self, args: PlayerPlacedArgs<'_>) { - if RedstoneGateBlock::has_power( - self, - args.world, - *args.position, - BlockState::from_id(args.state_id), - args.block, - ) - .await - { - args.world - .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal) - .await; - } - } - - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - if args.moved || Block::from_state_id(args.old_state_id) == args.block { - return; - } - RedstoneGateBlock::update_target( - self, - args.world, - *args.position, - BlockState::from_id(args.old_state_id).id, - args.block, - ) - .await; - } - - async fn is_target_not_aligned( - &self, - world: &dyn BlockAccessor, - pos: BlockPos, - state: &BlockState, - block: &Block, - ) -> bool { - let props = T::from_state_id(state.id, block); - let facing = props.get_facing().opposite(); - let (target_block, target_state) = world - .get_block_and_state(&pos.offset(facing.to_offset())) - .await; - if target_block == &Block::COMPARATOR { - let props = ComparatorLikeProperties::from_state_id(target_state.id, target_block); - props.facing != facing - } else if target_block == &Block::REPEATER { - let props = RepeaterLikeProperties::from_state_id(target_state.id, target_block); - props.facing != facing - } else { - false - } + if target_block == &Block::COMPARATOR { + let props = ComparatorLikeProperties::from_state_id(target_state.id, target_block); + props.facing != facing + } else if target_block == &Block::REPEATER { + let props = RepeaterLikeProperties::from_state_id(target_state.id, target_block); + props.facing != facing + } else { + false + } + }) } fn get_update_delay_internal(&self, state_id: BlockStateId, block: &Block) -> u8; diff --git a/pumpkin/src/block/blocks/redstone/buttons.rs b/pumpkin/src/block/blocks/redstone/buttons.rs index 1612f0958..84909c39c 100644 --- a/pumpkin/src/block/blocks/redstone/buttons.rs +++ b/pumpkin/src/block/blocks/redstone/buttons.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::BlockDirection; use pumpkin_data::HorizontalFacingExt; @@ -16,6 +15,7 @@ use pumpkin_world::world::BlockFlags; type ButtonLikeProperties = pumpkin_data::block_properties::LeverLikeProperties; +use crate::block::BlockFuture; use crate::block::CanPlaceAtArgs; use crate::block::EmitsRedstonePowerArgs; use crate::block::GetRedstonePowerArgs; @@ -57,78 +57,100 @@ async fn click_button(world: &Arc, block_pos: &BlockPos) { #[pumpkin_block_from_tag("minecraft:buttons")] pub struct ButtonBlock; -#[async_trait] impl BlockBehaviour for ButtonBlock { - async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - click_button(args.world, args.position).await; + fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { + Box::pin(async move { + click_button(args.world, args.position).await; - BlockActionResult::Success + BlockActionResult::Success + }) } - async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { - let state = args.world.get_block_state(args.position).await; - let mut props = ButtonLikeProperties::from_state_id(state.id, args.block); - props.powered = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - Self::update_neighbors(args.world, args.position, &props).await; + fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let state = args.world.get_block_state(args.position).await; + let mut props = ButtonLikeProperties::from_state_id(state.id, args.block); + props.powered = false; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ) + .await; + Self::update_neighbors(args.world, args.position, &props).await; + }) } - async fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { - true + fn emits_redstone_power<'a>( + &'a self, + _args: EmitsRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { true }) } - async fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - let button_props = ButtonLikeProperties::from_state_id(args.state.id, args.block); - if button_props.powered { 15 } else { 0 } + fn get_weak_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { + let button_props = ButtonLikeProperties::from_state_id(args.state.id, args.block); + if button_props.powered { 15 } else { 0 } + }) } - async fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - let button_props = ButtonLikeProperties::from_state_id(args.state.id, args.block); - if button_props.powered && button_props.get_direction() == args.direction { - 15 - } else { - 0 - } - } - - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - if !args.moved { - let button_props = ButtonLikeProperties::from_state_id(args.old_state_id, args.block); - if button_props.powered { - Self::update_neighbors(args.world, args.position, &button_props).await; + fn get_strong_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { + let button_props = ButtonLikeProperties::from_state_id(args.state.id, args.block); + if button_props.powered && button_props.get_direction() == args.direction { + 15 + } else { + 0 } - } + }) } - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut props = - ButtonLikeProperties::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) + fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if !args.moved { + let button_props = + ButtonLikeProperties::from_state_id(args.old_state_id, args.block); + if button_props.powered { + Self::update_neighbors(args.world, args.position, &button_props).await; + } + } + }) } - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - WallMountedBlock::can_place_at(self, args.block_accessor, args.position, args.direction) - .await + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut props = + ButtonLikeProperties::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) + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - WallMountedBlock::get_state_for_neighbor_update(self, args).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 + }) + } + + 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 ButtonBlock { fn get_direction(&self, state_id: BlockStateId, block: &Block) -> BlockDirection { let props = ButtonLikeProperties::from_state_id(state_id, block); diff --git a/pumpkin/src/block/blocks/redstone/comparator.rs b/pumpkin/src/block/blocks/redstone/comparator.rs index 56eb655f0..aa0abc8d2 100644 --- a/pumpkin/src/block/blocks/redstone/comparator.rs +++ b/pumpkin/src/block/blocks/redstone/comparator.rs @@ -1,6 +1,5 @@ use std::sync::{Arc, atomic::Ordering}; -use async_trait::async_trait; use pumpkin_data::{ Block, BlockDirection, BlockState, block_properties::{ @@ -17,13 +16,10 @@ use pumpkin_world::{ use crate::{ block::{ - registry::BlockActionResult, - { - BlockBehaviour, BrokenArgs, CanPlaceAtArgs, EmitsRedstonePowerArgs, - GetComparatorOutputArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, - NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, - OnStateReplacedArgs, PlacedArgs, PlayerPlacedArgs, - }, + BlockBehaviour, BlockFuture, BrokenArgs, CanPlaceAtArgs, EmitsRedstonePowerArgs, + GetComparatorOutputArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, + NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, OnStateReplacedArgs, + PlacedArgs, PlayerPlacedArgs, registry::BlockActionResult, }, world::World, }; @@ -33,89 +29,115 @@ use super::abstract_redstone_gate::{self, RedstoneGateBlock, RedstoneGateBlockPr #[pumpkin_block("minecraft:comparator")] pub struct ComparatorBlock; -#[async_trait] impl BlockBehaviour for ComparatorBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - RedstoneGateBlock::on_place(self, args.player, args.block).await + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { RedstoneGateBlock::on_place(self, args.player, args.block).await }) } - async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - let state = args.world.get_block_state(args.position).await; - let props = ComparatorLikeProperties::from_state_id(state.id, args.block); - self.on_use(props, args.world, *args.position, args.block) - .await; + 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 props = ComparatorLikeProperties::from_state_id(state.id, args.block); + self.on_use(props, args.world, *args.position, args.block) + .await; - BlockActionResult::Success + BlockActionResult::Success + }) } - async fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { - true + fn emits_redstone_power<'a>( + &'a self, + _args: EmitsRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { true }) } - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - RedstoneGateBlock::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 { + RedstoneGateBlock::can_place_at(self, args.block_accessor, *args.position).await + }) } - async fn placed(&self, args: PlacedArgs<'_>) { - let comparator = ComparatorBlockEntity::new(*args.position); - args.world.add_block_entity(Arc::new(comparator)).await; + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let comparator = ComparatorBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(comparator)).await; - RedstoneGateBlock::update_target( - self, - args.world, - *args.position, - BlockState::from_id(args.state_id).id, - args.block, - ) - .await; - } - - async fn player_placed(&self, args: PlayerPlacedArgs<'_>) { - RedstoneGateBlock::player_placed(self, args).await; - } - - async fn broken(&self, args: BrokenArgs<'_>) { - args.world.remove_block_entity(args.position).await; - } - - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - if args.direction == BlockDirection::Down - && !RedstoneGateBlock::can_place_above( + RedstoneGateBlock::update_target( self, args.world, - *args.neighbor_position, - BlockState::from_id(args.neighbor_state_id), + *args.position, + BlockState::from_id(args.state_id).id, + args.block, ) - .await - { - return Block::AIR.default_state.id; - } - args.state_id - } - - async fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - RedstoneGateBlock::get_weak_redstone_power(self, args).await - } - - async fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - RedstoneGateBlock::get_strong_redstone_power(self, args).await - } - - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - RedstoneGateBlock::on_neighbor_update(self, args).await; - } - - async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { - let state = args.world.get_block_state(args.position).await; - self.update(args.world, *args.position, state, args.block) .await; + }) } - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - RedstoneGateBlock::on_state_replaced(self, args).await; + fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + RedstoneGateBlock::player_placed(self, args).await; + }) + } + + fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + args.world.remove_block_entity(args.position).await; + }) + } + + fn get_state_for_neighbor_update<'a>( + &'a self, + args: GetStateForNeighborUpdateArgs<'a>, + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + if args.direction == BlockDirection::Down + && !RedstoneGateBlock::can_place_above( + self, + args.world, + *args.neighbor_position, + BlockState::from_id(args.neighbor_state_id), + ) + .await + { + return Block::AIR.default_state.id; + } + args.state_id + }) + } + + fn get_weak_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { RedstoneGateBlock::get_weak_redstone_power(self, args).await }) + } + + fn get_strong_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { RedstoneGateBlock::get_strong_redstone_power(self, args).await }) + } + + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + RedstoneGateBlock::on_neighbor_update(self, args).await; + }) + } + + fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let state = args.world.get_block_state(args.position).await; + self.update(args.world, *args.position, state, args.block) + .await; + }) + } + + fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + RedstoneGateBlock::on_state_replaced(self, args).await; + }) } } @@ -133,114 +155,129 @@ impl RedstoneGateBlockProperties for ComparatorLikeProperties { } } -#[async_trait] impl RedstoneGateBlock for ComparatorBlock { - async fn get_output_level(&self, world: &World, pos: BlockPos) -> u8 { - if let Some(blockentity) = world.get_block_entity(&pos).await - && blockentity.resource_location() == ComparatorBlockEntity::ID - { - let comparator = blockentity - .as_any() - .downcast_ref::() - .unwrap(); - return comparator.output_signal.load(Ordering::Relaxed); - } - 0 + fn get_output_level<'a>(&'a self, world: &'a World, pos: BlockPos) -> BlockFuture<'a, u8> { + Box::pin(async move { + if let Some(blockentity) = world.get_block_entity(&pos).await + && blockentity.resource_location() == ComparatorBlockEntity::ID + { + let comparator = blockentity + .as_any() + .downcast_ref::() + .unwrap(); + return comparator.output_signal.load(Ordering::Relaxed); + } + 0 + }) } - async fn update_powered( - &self, - world: &World, + fn update_powered<'a>( + &'a self, + world: &'a World, pos: BlockPos, - state: &BlockState, - block: &Block, - ) { - if world.is_block_tick_scheduled(&pos, block).await { - return; - } - let i = self.calculate_output_signal(world, pos, state, block).await; - let j = RedstoneGateBlock::get_output_level(self, world, pos).await; + state: &'a BlockState, + block: &'a Block, + ) -> BlockFuture<'a, ()> { + Box::pin(async move { + if world.is_block_tick_scheduled(&pos, block).await { + return; + } + let i = self.calculate_output_signal(world, pos, state, block).await; - let props = ComparatorLikeProperties::from_state_id(state.id, block); - if i != j || props.powered != self.has_power(world, pos, state, block).await { - world - .schedule_block_tick( - block, - pos, - RedstoneGateBlock::get_update_delay_internal(self, state.id, block), - if RedstoneGateBlock::is_target_not_aligned(self, world, pos, state, block) - .await - { - TickPriority::High - } else { - TickPriority::Normal - }, - ) - .await; - } - } + let j = RedstoneGateBlock::get_output_level(self, world, pos).await; - async fn has_power( - &self, - world: &World, - pos: BlockPos, - state: &BlockState, - block: &Block, - ) -> bool { - let i = self.get_power(world, pos, state, block).await; - if i == 0 { - return false; - } - let j = self - .get_max_input_level_sides(world, pos, state.id, block, false) - .await; - if i > j { - true - } else { let props = ComparatorLikeProperties::from_state_id(state.id, block); - i == j && props.mode == ComparatorMode::Compare - } + + if i != j + || props.powered + != RedstoneGateBlock::has_power(self, world, pos, state, block).await + { + world + .schedule_block_tick( + block, + pos, + RedstoneGateBlock::get_update_delay_internal(self, state.id, block), + if RedstoneGateBlock::is_target_not_aligned(self, world, pos, state, block) + .await + { + TickPriority::High + } else { + TickPriority::Normal + }, + ) + .await; + } + }) } - async fn get_power( - &self, - world: &World, + fn has_power<'a>( + &'a self, + world: &'a World, pos: BlockPos, - state: &BlockState, - block: &Block, - ) -> u8 { - let redstone_level = abstract_redstone_gate::get_power::( - world, pos, state.id, block, - ) - .await; + state: &'a BlockState, + block: &'a Block, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { + let i = RedstoneGateBlock::get_power(self, world, pos, state, block).await; + if i == 0 { + return false; + } + let j = RedstoneGateBlock::get_max_input_level_sides( + self, world, pos, state.id, block, false, + ) + .await; + if i > j { + true + } else { + let props = ComparatorLikeProperties::from_state_id(state.id, block); + i == j && props.mode == ComparatorMode::Compare + } + }) + } - let props = ComparatorLikeProperties::from_state_id(state.id, block); - let facing = props.facing; - let source_pos = pos.offset(facing.to_offset()); - let (source_block, source_state) = world.get_block_and_state(&source_pos).await; + fn get_power<'a>( + &'a self, + world: &'a World, + pos: BlockPos, + state: &'a BlockState, + block: &'a Block, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { + let redstone_level = abstract_redstone_gate::get_power::( + world, pos, state.id, block, + ) + .await; - if let Some(pumpkin_block) = world.block_registry.get_pumpkin_block(source_block) - && let Some(level) = pumpkin_block - .get_comparator_output(GetComparatorOutputArgs { - world, - block: source_block, - state: source_state, - position: &source_pos, - }) - .await - { - return level; - } - - if redstone_level < 15 && source_state.is_solid_block() { - let source_pos = source_pos.offset(facing.to_offset()); + let props = ComparatorLikeProperties::from_state_id(state.id, block); + let facing = props.facing; + let source_pos = pos.offset(facing.to_offset()); let (source_block, source_state) = world.get_block_and_state(&source_pos).await; - let itemframe_level = self - .get_attached_itemframe_level(world, facing, source_pos) - .await; - let block_level = - if let Some(pumpkin_block) = world.block_registry.get_pumpkin_block(source_block) { + // Note: .get_comparator_output is assumed to be an async method returning Option + if let Some(pumpkin_block) = world.block_registry.get_pumpkin_block(source_block) + && let Some(level) = pumpkin_block + .get_comparator_output(GetComparatorOutputArgs { + world, + block: source_block, + state: source_state, + position: &source_pos, + }) + .await + { + return level; + } + + if redstone_level < 15 && source_state.is_solid_block() { + let source_pos = source_pos.offset(facing.to_offset()); + let (source_block, source_state) = world.get_block_and_state(&source_pos).await; + + // Note: self.get_attached_itemframe_level is assumed to be an async method + let itemframe_level = self + .get_attached_itemframe_level(world, facing, source_pos) + .await; + let block_level = if let Some(pumpkin_block) = + world.block_registry.get_pumpkin_block(source_block) + { pumpkin_block .get_comparator_output(GetComparatorOutputArgs { world, @@ -252,11 +289,12 @@ impl RedstoneGateBlock for ComparatorBlock { } else { None }; - if let Some(level) = itemframe_level.max(block_level) { - return level; + if let Some(level) = itemframe_level.max(block_level) { + return level; + } } - } - redstone_level + redstone_level + }) } fn get_update_delay_internal(&self, _state_id: BlockStateId, _block: &Block) -> u8 { diff --git a/pumpkin/src/block/blocks/redstone/copper_bulb.rs b/pumpkin/src/block/blocks/redstone/copper_bulb.rs index 4897682a6..994a27a0b 100644 --- a/pumpkin/src/block/blocks/redstone/copper_bulb.rs +++ b/pumpkin/src/block/blocks/redstone/copper_bulb.rs @@ -1,6 +1,5 @@ use crate::block::blocks::redstone::block_receives_redstone_power; -use crate::block::{BlockBehaviour, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs}; -use async_trait::async_trait; +use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs}; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_world::BlockStateId; @@ -29,52 +28,55 @@ impl BlockMetadata for CopperBulbBlock { } } -#[async_trait] impl BlockBehaviour for CopperBulbBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut props = CopperBulbLikeProperties::default(args.block); - let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; - if is_receiving_power { - props.lit = true; - args.world - .play_block_sound( - Sound::BlockCopperBulbTurnOn, - SoundCategory::Blocks, - *args.position, - ) - .await; - props.powered = true; - } - props.to_state_id(args.block) - } - - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - let state = args.world.get_block_state(args.position).await; - let mut props = CopperBulbLikeProperties::from_state_id(state.id, args.block); - let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; - if props.powered != is_receiving_power { - if !props.powered { - props.lit = !props.lit; + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut props = CopperBulbLikeProperties::default(args.block); + let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; + if is_receiving_power { + props.lit = true; args.world .play_block_sound( - if props.lit { - Sound::BlockCopperBulbTurnOn - } else { - Sound::BlockCopperBulbTurnOff - }, + Sound::BlockCopperBulbTurnOn, SoundCategory::Blocks, *args.position, ) .await; + props.powered = true; } - props.powered = is_receiving_power; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - } + props.to_state_id(args.block) + }) + } + + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let state = args.world.get_block_state(args.position).await; + let mut props = CopperBulbLikeProperties::from_state_id(state.id, args.block); + let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; + if props.powered != is_receiving_power { + if !props.powered { + props.lit = !props.lit; + args.world + .play_block_sound( + if props.lit { + Sound::BlockCopperBulbTurnOn + } else { + Sound::BlockCopperBulbTurnOff + }, + SoundCategory::Blocks, + *args.position, + ) + .await; + } + props.powered = is_receiving_power; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ) + .await; + } + }) } } diff --git a/pumpkin/src/block/blocks/redstone/dispenser.rs b/pumpkin/src/block/blocks/redstone/dispenser.rs index 7b850019b..f14655524 100644 --- a/pumpkin/src/block/blocks/redstone/dispenser.rs +++ b/pumpkin/src/block/blocks/redstone/dispenser.rs @@ -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, DispenserLikeProperties}; use pumpkin_macros::pumpkin_block; use pumpkin_world::BlockStateId; @@ -7,11 +6,12 @@ use pumpkin_world::BlockStateId; #[pumpkin_block("minecraft:dispenser")] pub struct DispenserBlock; -#[async_trait] impl BlockBehaviour for DispenserBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut props = DispenserLikeProperties::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 = DispenserLikeProperties::default(args.block); + props.facing = args.player.living_entity.entity.get_facing().opposite(); + props.to_state_id(args.block) + }) } } diff --git a/pumpkin/src/block/blocks/redstone/dropper.rs b/pumpkin/src/block/blocks/redstone/dropper.rs index c4d6ac211..ce5db08b0 100644 --- a/pumpkin/src/block/blocks/redstone/dropper.rs +++ b/pumpkin/src/block/blocks/redstone/dropper.rs @@ -1,8 +1,8 @@ use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, - PlacedArgs, + BlockBehaviour, BlockFuture, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, + OnScheduledTickArgs, PlacedArgs, }; use crate::entity::Entity; use crate::entity::item::ItemEntity; @@ -79,145 +79,156 @@ const fn to_data3d(facing: Facing) -> i32 { } } -#[async_trait] impl BlockBehaviour for DropperBlock { - 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(&DropperScreenFactory(inventory)) - .await; - } - BlockActionResult::Success - } - - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut props = DispenserLikeProperties::default(args.block); - props.facing = args.player.living_entity.entity.get_facing().opposite(); - props.to_state_id(args.block) - } - - async fn placed(&self, args: PlacedArgs<'_>) { - let dropper_block_entity = DropperBlockEntity::new(*args.position); - args.world - .add_block_entity(Arc::new(dropper_block_entity)) - .await; - } - - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - let powered = block_receives_redstone_power(args.world, args.position).await - || block_receives_redstone_power(args.world, &args.position.up()).await; - let mut props = DispenserLikeProperties::from_state_id( - args.world.get_block_state(args.position).await.id, - args.block, - ); - if powered && !props.triggered { - args.world - .schedule_block_tick(args.block, *args.position, 4, TickPriority::Normal) - .await; - props.triggered = true; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - } else if !powered && props.triggered { - props.triggered = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - } - } - - async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { - if let Some(block_entity) = args.world.get_block_entity(args.position).await { - let dropper = block_entity - .as_any() - .downcast_ref::() - .unwrap(); - if let Some(mut item) = dropper.get_random_slot().await { - let props = DispenserLikeProperties::from_state_id( - args.world.get_block_state(args.position).await.id, - args.block, - ); - if let Some(entity) = args - .world - .get_block_entity( - &args - .position - .offset(props.facing.to_block_direction().to_offset()), - ) - .await - && let Some(container) = entity.get_inventory() - { - // TODO check WorldlyContainer - let mut is_full = true; - for i in 0..container.size() { - let bind = container.get_stack(i).await; - let item = bind.lock().await; - if item.item_count < item.get_max_stack_size() { - is_full = false; - break; - } - } - if is_full { - return; - } - //TODO WorldlyContainer - let backup = item.clone(); - let one_item = item.split(1); - if HopperBlockEntity::add_one_item(dropper, container.as_ref(), one_item).await - { - return; - } - *item = backup; - return; - } - let drop_item = item.split(1); - let facing = to_normal(props.facing); - let mut position = args.position.to_centered_f64().add(&(facing * 0.7)); - position.y -= match props.facing { - Facing::Up | Facing::Down => 0.125, - _ => 0.15625, - }; - let entity = Entity::new( - Uuid::new_v4(), - args.world.clone(), - position, - &EntityType::ITEM, - false, - ); - let rd = rng().random::() * 0.1 + 0.2; - let velocity = Vector3::new( - triangle(&mut rng(), facing.x * rd, 0.017_227_5 * 6.), - triangle(&mut rng(), 0.2, 0.017_227_5 * 6.), - triangle(&mut rng(), facing.z * rd, 0.017_227_5 * 6.), - ); - let item_entity = - Arc::new(ItemEntity::new_with_velocity(entity, drop_item, velocity, 40).await); - args.world.spawn_entity(item_entity).await; - args.world - .sync_world_event(WorldEvent::DispenserDispenses, *args.position, 0) - .await; - args.world - .sync_world_event( - WorldEvent::DispenserActivated, - *args.position, - to_data3d(props.facing), - ) - .await; - } else { - args.world - .sync_world_event(WorldEvent::DispenserFails, *args.position, 0) + 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(&DropperScreenFactory(inventory)) .await; } - } + BlockActionResult::Success + }) + } + + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut props = DispenserLikeProperties::default(args.block); + props.facing = args.player.living_entity.entity.get_facing().opposite(); + props.to_state_id(args.block) + }) + } + + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let dropper_block_entity = DropperBlockEntity::new(*args.position); + args.world + .add_block_entity(Arc::new(dropper_block_entity)) + .await; + }) + } + + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + 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 mut props = DispenserLikeProperties::from_state_id( + args.world.get_block_state(args.position).await.id, + args.block, + ); + if powered && !props.triggered { + args.world + .schedule_block_tick(args.block, *args.position, 4, TickPriority::Normal) + .await; + props.triggered = true; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ) + .await; + } else if !powered && props.triggered { + props.triggered = false; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ) + .await; + } + }) + } + + 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 { + let dropper = block_entity + .as_any() + .downcast_ref::() + .unwrap(); + if let Some(mut item) = dropper.get_random_slot().await { + let props = DispenserLikeProperties::from_state_id( + args.world.get_block_state(args.position).await.id, + args.block, + ); + if let Some(entity) = args + .world + .get_block_entity( + &args + .position + .offset(props.facing.to_block_direction().to_offset()), + ) + .await + && let Some(container) = entity.get_inventory() + { + // TODO check WorldlyContainer + let mut is_full = true; + for i in 0..container.size() { + let bind = container.get_stack(i).await; + let item = bind.lock().await; + if item.item_count < item.get_max_stack_size() { + is_full = false; + break; + } + } + if is_full { + return; + } + //TODO WorldlyContainer + let backup = item.clone(); + let one_item = item.split(1); + if HopperBlockEntity::add_one_item(dropper, container.as_ref(), one_item) + .await + { + return; + } + *item = backup; + return; + } + let drop_item = item.split(1); + let facing = to_normal(props.facing); + let mut position = args.position.to_centered_f64().add(&(facing * 0.7)); + position.y -= match props.facing { + Facing::Up | Facing::Down => 0.125, + _ => 0.15625, + }; + let entity = Entity::new( + Uuid::new_v4(), + args.world.clone(), + position, + &EntityType::ITEM, + false, + ); + let rd = rng().random::() * 0.1 + 0.2; + let velocity = Vector3::new( + triangle(&mut rng(), facing.x * rd, 0.017_227_5 * 6.), + triangle(&mut rng(), 0.2, 0.017_227_5 * 6.), + triangle(&mut rng(), facing.z * rd, 0.017_227_5 * 6.), + ); + let item_entity = Arc::new( + ItemEntity::new_with_velocity(entity, drop_item, velocity, 40).await, + ); + args.world.spawn_entity(item_entity).await; + args.world + .sync_world_event(WorldEvent::DispenserDispenses, *args.position, 0) + .await; + args.world + .sync_world_event( + WorldEvent::DispenserActivated, + *args.position, + to_data3d(props.facing), + ) + .await; + } else { + args.world + .sync_world_event(WorldEvent::DispenserFails, *args.position, 0) + .await; + } + } + }) } } diff --git a/pumpkin/src/block/blocks/redstone/lever.rs b/pumpkin/src/block/blocks/redstone/lever.rs index fecc4f3b7..b5e7731a2 100644 --- a/pumpkin/src/block/blocks/redstone/lever.rs +++ b/pumpkin/src/block/blocks/redstone/lever.rs @@ -1,13 +1,10 @@ use std::sync::Arc; use crate::block::{ + BlockFuture, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, + GetStateForNeighborUpdateArgs, OnPlaceArgs, OnStateReplacedArgs, blocks::abstract_wall_mounting::WallMountedBlock, - { - CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, - GetStateForNeighborUpdateArgs, OnPlaceArgs, OnStateReplacedArgs, - }, }; -use async_trait::async_trait; use pumpkin_data::{ Block, BlockDirection, HorizontalFacingExt, block_properties::{BlockFace, BlockProperties, LeverLikeProperties}, @@ -43,63 +40,83 @@ async fn toggle_lever(world: &Arc, block_pos: &BlockPos) { #[pumpkin_block("minecraft:lever")] pub struct LeverBlock; -#[async_trait] impl BlockBehaviour for LeverBlock { - async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - toggle_lever(args.world, args.position).await; + fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { + Box::pin(async move { + toggle_lever(args.world, args.position).await; - BlockActionResult::Success + BlockActionResult::Success + }) } - async fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { - true + fn emits_redstone_power<'a>( + &'a self, + _args: EmitsRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { true }) } - async fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - let lever_props = LeverLikeProperties::from_state_id(args.state.id, args.block); - if lever_props.powered { 15 } else { 0 } + fn get_weak_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { + let lever_props = LeverLikeProperties::from_state_id(args.state.id, args.block); + if lever_props.powered { 15 } else { 0 } + }) } - async fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - let lever_props = LeverLikeProperties::from_state_id(args.state.id, args.block); - if lever_props.powered && lever_props.get_direction() == args.direction { - 15 - } else { - 0 - } - } - - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - if !args.moved { - let lever_props = LeverLikeProperties::from_state_id(args.old_state_id, args.block); - if lever_props.powered { - Self::update_neighbors(args.world, args.position, &lever_props).await; + fn get_strong_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { + let lever_props = LeverLikeProperties::from_state_id(args.state.id, args.block); + if lever_props.powered && lever_props.get_direction() == args.direction { + 15 + } else { + 0 } - } + }) } - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut props = LeverLikeProperties::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) + fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if !args.moved { + let lever_props = LeverLikeProperties::from_state_id(args.old_state_id, args.block); + if lever_props.powered { + Self::update_neighbors(args.world, args.position, &lever_props).await; + } + } + }) } - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - WallMountedBlock::can_place_at(self, args.block_accessor, args.position, args.direction) - .await + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut props = + LeverLikeProperties::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) + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - WallMountedBlock::get_state_for_neighbor_update(self, args).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 + }) + } + + 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 LeverBlock { fn get_direction(&self, state_id: BlockStateId, block: &Block) -> BlockDirection { let props = LeverLikeProperties::from_state_id(state_id, block); diff --git a/pumpkin/src/block/blocks/redstone/observer.rs b/pumpkin/src/block/blocks/redstone/observer.rs index 07d078766..112fc9572 100644 --- a/pumpkin/src/block/blocks/redstone/observer.rs +++ b/pumpkin/src/block/blocks/redstone/observer.rs @@ -1,10 +1,9 @@ use std::sync::Arc; use crate::block::{ - EmitsRedstonePowerArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, - OnScheduledTickArgs, OnStateReplacedArgs, + BlockFuture, EmitsRedstonePowerArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, + OnPlaceArgs, OnScheduledTickArgs, OnStateReplacedArgs, }; -use async_trait::async_trait; use pumpkin_data::{ Block, FacingExt, block_properties::{BlockProperties, ObserverLikeProperties}, @@ -13,97 +12,112 @@ use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::{BlockStateId, tick::TickPriority, world::BlockFlags}; -use crate::{ - block::{BlockBehaviour, OnNeighborUpdateArgs}, - world::World, -}; +use crate::{block::BlockBehaviour, world::World}; #[pumpkin_block("minecraft:observer")] pub struct ObserverBlock; -#[async_trait] impl BlockBehaviour for ObserverBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut props = ObserverLikeProperties::default(args.block); - props.facing = args.player.living_entity.entity.get_facing(); - 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 = ObserverLikeProperties::default(args.block); + props.facing = args.player.living_entity.entity.get_facing(); + props.to_state_id(args.block) + }) } - async fn on_neighbor_update(&self, _args: OnNeighborUpdateArgs<'_>) {} + fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let state = args.world.get_block_state(args.position).await; + let mut props = ObserverLikeProperties::from_state_id(state.id, args.block); - async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { - let state = args.world.get_block_state(args.position).await; - let mut props = ObserverLikeProperties::from_state_id(state.id, args.block); - - if props.powered { - props.powered = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - } else { - props.powered = true; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - args.world - .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal) - .await; - } - - Self::update_neighbors(args.world, args.block, args.position, &props).await; - } - - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - let props = ObserverLikeProperties::from_state_id(args.state_id, args.block); - - if props.facing.to_block_direction() == args.direction && !props.powered { - Self::schedule_tick(args.world, args.position).await; - } - - args.state_id - } - - async fn emits_redstone_power(&self, args: EmitsRedstonePowerArgs<'_>) -> bool { - let props = ObserverLikeProperties::from_state_id(args.state.id, args.block); - props.facing.to_block_direction() == args.direction - } - - async fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - let props = ObserverLikeProperties::from_state_id(args.state.id, args.block); - if props.facing.to_block_direction() == args.direction && props.powered { - 15 - } else { - 0 - } - } - - async fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - self.get_weak_redstone_power(args).await - } - - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - if !args.moved { - let props = ObserverLikeProperties::from_state_id(args.old_state_id, args.block); - if props.powered - && args - .world - .is_block_tick_scheduled(args.position, &Block::OBSERVER) - .await - { - Self::update_neighbors(args.world, args.block, args.position, &props).await; + if props.powered { + props.powered = false; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ) + .await; + } else { + props.powered = true; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ) + .await; + args.world + .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal) + .await; } - } + + Self::update_neighbors(args.world, args.block, args.position, &props).await; + }) + } + + fn get_state_for_neighbor_update<'a>( + &'a self, + args: GetStateForNeighborUpdateArgs<'a>, + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let props = ObserverLikeProperties::from_state_id(args.state_id, args.block); + + if props.facing.to_block_direction() == args.direction && !props.powered { + Self::schedule_tick(args.world, args.position).await; + } + + args.state_id + }) + } + + fn emits_redstone_power<'a>( + &'a self, + args: EmitsRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { + let props = ObserverLikeProperties::from_state_id(args.state.id, args.block); + props.facing.to_block_direction() == args.direction + }) + } + + fn get_weak_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { + let props = ObserverLikeProperties::from_state_id(args.state.id, args.block); + if props.facing.to_block_direction() == args.direction && props.powered { + 15 + } else { + 0 + } + }) + } + + fn get_strong_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { self.get_weak_redstone_power(args).await }) + } + + fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if !args.moved { + let props = ObserverLikeProperties::from_state_id(args.old_state_id, args.block); + if props.powered + && args + .world + .is_block_tick_scheduled(args.position, &Block::OBSERVER) + .await + { + Self::update_neighbors(args.world, args.block, args.position, &props).await; + } + } + }) } } diff --git a/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs b/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs index d15ff4203..bd307574f 100644 --- a/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs +++ b/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::{ Block, BlockDirection, BlockState, block_properties::BlockProperties, @@ -9,7 +8,7 @@ use pumpkin_world::{BlockStateId, world::BlockFlags}; use crate::{ block::{ - BlockBehaviour, BlockMetadata, CanPlaceAtArgs, EmitsRedstonePowerArgs, + BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, OnEntityCollisionArgs, OnNeighborUpdateArgs, OnScheduledTickArgs, OnStateReplacedArgs, }, @@ -40,46 +39,65 @@ impl BlockMetadata for PressurePlateBlock { } } -#[async_trait] impl BlockBehaviour for PressurePlateBlock { - async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { - self.on_entity_collision_pp(args).await; + fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + self.on_entity_collision_pp(args).await; + }) } - async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { - self.on_scheduled_tick_pp(args).await; + fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + self.on_scheduled_tick_pp(args).await; + }) } - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - self.on_state_replaced_pp(args).await; + fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + self.on_state_replaced_pp(args).await; + }) } - async fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - self.get_redstone_output(args.block, args.state.id) + fn get_weak_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { self.get_redstone_output(args.block, args.state.id) }) } - async fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - if args.direction == BlockDirection::Up { - return self.get_redstone_output(args.block, args.state.id); - } - 0 + fn get_strong_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { + if args.direction == BlockDirection::Up { + return self.get_redstone_output(args.block, args.state.id); + } + 0 + }) } - async fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { - true + fn emits_redstone_power<'a>( + &'a self, + _args: EmitsRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { true }) } - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - if !Self::can_pressure_plate_place_at(args.world, args.position).await { - args.world - .break_block(args.position, None, BlockFlags::NOTIFY_ALL) - .await; - return; - } + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if !Self::can_pressure_plate_place_at(args.world, args.position).await { + args.world + .break_block(args.position, None, BlockFlags::NOTIFY_ALL) + .await; + } + }) } - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - Self::can_pressure_plate_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 { + Self::can_pressure_plate_place_at(args.world.unwrap(), args.position).await + }) } } diff --git a/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs b/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs index b83cfd165..4487a6fe5 100644 --- a/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs +++ b/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::{ Block, BlockDirection, BlockState, block_properties::{BlockProperties, EnumVariants, Integer0To15}, @@ -8,7 +7,7 @@ use pumpkin_world::{BlockStateId, world::BlockFlags}; use crate::{ block::{ - BlockBehaviour, BlockMetadata, CanPlaceAtArgs, EmitsRedstonePowerArgs, + BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, OnEntityCollisionArgs, OnNeighborUpdateArgs, OnScheduledTickArgs, OnStateReplacedArgs, }, @@ -37,46 +36,65 @@ impl BlockMetadata for WeightedPressurePlateBlock { } } -#[async_trait] impl BlockBehaviour for WeightedPressurePlateBlock { - async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { - self.on_entity_collision_pp(args).await; + fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + self.on_entity_collision_pp(args).await; + }) } - async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { - self.on_scheduled_tick_pp(args).await; + fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + self.on_scheduled_tick_pp(args).await; + }) } - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - self.on_state_replaced_pp(args).await; + fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + self.on_state_replaced_pp(args).await; + }) } - async fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - self.get_redstone_output(args.block, args.state.id) + fn get_weak_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { self.get_redstone_output(args.block, args.state.id) }) } - async fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - if args.direction == BlockDirection::Up { - return self.get_redstone_output(args.block, args.state.id); - } - 0 + fn get_strong_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { + if args.direction == BlockDirection::Up { + return self.get_redstone_output(args.block, args.state.id); + } + 0 + }) } - async fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { - true + fn emits_redstone_power<'a>( + &'a self, + _args: EmitsRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { true }) } - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - if !Self::can_pressure_plate_place_at(args.world, args.position).await { - args.world - .break_block(args.position, None, BlockFlags::NOTIFY_ALL) - .await; - return; - } + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if !Self::can_pressure_plate_place_at(args.world, args.position).await { + args.world + .break_block(args.position, None, BlockFlags::NOTIFY_ALL) + .await; + } + }) } - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - Self::can_pressure_plate_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 { + Self::can_pressure_plate_place_at(args.world.unwrap(), args.position).await + }) } } diff --git a/pumpkin/src/block/blocks/redstone/rails/activator_rail.rs b/pumpkin/src/block/blocks/redstone/rails/activator_rail.rs index 820726ce1..9358e3e9b 100644 --- a/pumpkin/src/block/blocks/redstone/rails/activator_rail.rs +++ b/pumpkin/src/block/blocks/redstone/rails/activator_rail.rs @@ -1,10 +1,10 @@ -use async_trait::async_trait; use pumpkin_macros::pumpkin_block; use pumpkin_world::BlockStateId; use pumpkin_world::world::BlockFlags; use std::sync::Arc; use crate::block::BlockBehaviour; +use crate::block::BlockFuture; use crate::block::CanPlaceAtArgs; use crate::block::OnNeighborUpdateArgs; use crate::block::OnPlaceArgs; @@ -37,154 +37,186 @@ use super::common::{ #[pumpkin_block("minecraft:activator_rail")] pub struct ActivatorRailBlock; -#[async_trait] impl BlockBehaviour for ActivatorRailBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut rail_props = RailProperties::default(args.block); - let player_facing = args.player.living_entity.entity.get_horizontal_facing(); + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut rail_props = RailProperties::default(args.block); + let player_facing = args.player.living_entity.entity.get_horizontal_facing(); - rail_props.set_waterlogged(args.replacing.water_source()); - rail_props.set_straight_shape( - compute_placed_rail_shape(args.world, args.position, player_facing).await, - ); + rail_props.set_waterlogged(args.replacing.water_source()); + rail_props.set_straight_shape( + compute_placed_rail_shape(args.world, args.position, player_facing).await, + ); - rail_props.to_state_id(args.block) + rail_props.to_state_id(args.block) + }) } - async fn placed(&self, args: PlacedArgs<'_>) { - update_flanking_rails_shape(args.world, args.block, args.state_id, args.position).await; + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + update_flanking_rails_shape(args.world, args.block, args.state_id, args.position).await; - self.update_powered_state(args.world, args.block, args.position) - .await; - - let final_state_id = args.world.get_block_state_id(args.position).await; - let rail_props = RailProperties::new(final_state_id, args.block); - - self.update_connected_rails(args.world, args.position, &rail_props, true, 0) - .await; - self.update_connected_rails(args.world, args.position, &rail_props, false, 0) - .await; - - for direction in rail_props.directions() { - let neighbor_pos = args.position.offset(direction.to_offset()); - - if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &neighbor_pos).await - { - self.update_powered_state_internal( - args.world, - neighbor_rail.0, - &neighbor_pos, - false, - ) + self.update_powered_state(args.world, args.block, args.position) .await; - self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, false, 0) - .await; - } - let up_pos = neighbor_pos.up(); - if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &up_pos).await { - self.update_powered_state_internal(args.world, neighbor_rail.0, &up_pos, false) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0) - .await; - } + let final_state_id = args.world.get_block_state_id(args.position).await; + let rail_props = RailProperties::new(final_state_id, args.block); - let down_pos = neighbor_pos.down(); - if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &down_pos).await { - self.update_powered_state_internal(args.world, neighbor_rail.0, &down_pos, false) + self.update_connected_rails(args.world, args.position, &rail_props, true, 0) + .await; + self.update_connected_rails(args.world, args.position, &rail_props, false, 0) + .await; + + for direction in rail_props.directions() { + let neighbor_pos = args.position.offset(direction.to_offset()); + + if let Some(neighbor_rail) = + self.find_rail_at_position(args.world, &neighbor_pos).await + { + self.update_powered_state_internal( + args.world, + neighbor_rail.0, + &neighbor_pos, + false, + ) .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0) + self.update_connected_rails( + args.world, + &neighbor_pos, + &neighbor_rail.1, + true, + 0, + ) .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0) + self.update_connected_rails( + args.world, + &neighbor_pos, + &neighbor_rail.1, + false, + 0, + ) .await; + } + + let up_pos = neighbor_pos.up(); + if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &up_pos).await { + self.update_powered_state_internal(args.world, neighbor_rail.0, &up_pos, false) + .await; + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0) + .await; + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0) + .await; + } + + let down_pos = neighbor_pos.down(); + if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &down_pos).await + { + self.update_powered_state_internal( + args.world, + neighbor_rail.0, + &down_pos, + false, + ) + .await; + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0) + .await; + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0) + .await; + } } - } + }) } - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - if !rail_placement_is_valid(args.world, args.block, args.position).await { + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if !rail_placement_is_valid(args.world, args.block, args.position).await { + args.world + .break_block(args.position, None, BlockFlags::NOTIFY_ALL) + .await; + return; + } + + self.update_powered_state(args.world, args.block, args.position) + .await; + + let state_id = args.world.get_block_state_id(args.position).await; + let rail_props = RailProperties::new(state_id, args.block); + + self.update_connected_rails(args.world, args.position, &rail_props, true, 0) + .await; + self.update_connected_rails(args.world, args.position, &rail_props, false, 0) + .await; + }) + } + + fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let rail_props = RailProperties::new(args.old_state_id, args.block); + + if rail_props.shape().is_ascending() { + args.world + .update_neighbor(&args.position.up(), args.block) + .await; + } + + args.world.update_neighbor(args.position, args.block).await; args.world - .break_block(args.position, None, BlockFlags::NOTIFY_ALL) + .update_neighbor(&args.position.down(), args.block) .await; - return; - } - self.update_powered_state(args.world, args.block, args.position) - .await; + let directions = rail_props.directions(); + for direction in directions { + let neighbor_pos = args.position.offset(direction.to_offset()); - let state_id = args.world.get_block_state_id(args.position).await; - let rail_props = RailProperties::new(state_id, args.block); - - self.update_connected_rails(args.world, args.position, &rail_props, true, 0) - .await; - self.update_connected_rails(args.world, args.position, &rail_props, false, 0) - .await; - } - - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - let rail_props = RailProperties::new(args.old_state_id, args.block); - - if rail_props.shape().is_ascending() { - args.world - .update_neighbor(&args.position.up(), args.block) - .await; - } - - args.world.update_neighbor(args.position, args.block).await; - args.world - .update_neighbor(&args.position.down(), args.block) - .await; - - let directions = rail_props.directions(); - for direction in directions { - let neighbor_pos = args.position.offset(direction.to_offset()); - - if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &neighbor_pos).await - { - self.update_powered_state(args.world, neighbor_rail.0, &neighbor_pos) + if let Some(neighbor_rail) = + self.find_rail_at_position(args.world, &neighbor_pos).await + { + self.update_powered_state(args.world, neighbor_rail.0, &neighbor_pos) + .await; + self.update_connected_rails( + args.world, + &neighbor_pos, + &neighbor_rail.1, + true, + 0, + ) .await; - self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, false, 0) + self.update_connected_rails( + args.world, + &neighbor_pos, + &neighbor_rail.1, + false, + 0, + ) .await; + } + + let up_pos = neighbor_pos.up(); + if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &up_pos).await { + self.update_powered_state(args.world, neighbor_rail.0, &up_pos) + .await; + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0) + .await; + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0) + .await; + } + + let down_pos = neighbor_pos.down(); + if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &down_pos).await + { + self.update_powered_state(args.world, neighbor_rail.0, &down_pos) + .await; + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0) + .await; + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0) + .await; + } } - - let up_pos = neighbor_pos.up(); - if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &up_pos).await { - self.update_powered_state(args.world, neighbor_rail.0, &up_pos) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0) - .await; - } - - let down_pos = neighbor_pos.down(); - if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &down_pos).await { - self.update_powered_state(args.world, neighbor_rail.0, &down_pos) - .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0) - .await; - } - } + }) } - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - can_place_rail_at(args.block_accessor, args.position).await - } - - 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 + fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> { + Box::pin(async move { can_place_rail_at(args.block_accessor, args.position).await }) } } diff --git a/pumpkin/src/block/blocks/redstone/rails/detector_rail.rs b/pumpkin/src/block/blocks/redstone/rails/detector_rail.rs index c77b8d865..2becf95fb 100644 --- a/pumpkin/src/block/blocks/redstone/rails/detector_rail.rs +++ b/pumpkin/src/block/blocks/redstone/rails/detector_rail.rs @@ -1,9 +1,9 @@ -use async_trait::async_trait; use pumpkin_macros::pumpkin_block; use pumpkin_world::BlockStateId; use pumpkin_world::world::BlockFlags; use crate::block::BlockBehaviour; +use crate::block::BlockFuture; use crate::block::CanPlaceAtArgs; use crate::block::OnNeighborUpdateArgs; use crate::block::OnPlaceArgs; @@ -18,34 +18,38 @@ use super::common::{ #[pumpkin_block("minecraft:detector_rail")] pub struct DetectorRailBlock; -#[async_trait] impl BlockBehaviour for DetectorRailBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut rail_props = RailProperties::default(args.block); - let player_facing = args.player.living_entity.entity.get_horizontal_facing(); + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut rail_props = RailProperties::default(args.block); + let player_facing = args.player.living_entity.entity.get_horizontal_facing(); - rail_props.set_waterlogged(args.replacing.water_source()); - rail_props.set_straight_shape( - compute_placed_rail_shape(args.world, args.position, player_facing).await, - ); + rail_props.set_waterlogged(args.replacing.water_source()); + rail_props.set_straight_shape( + compute_placed_rail_shape(args.world, args.position, player_facing).await, + ); - rail_props.to_state_id(args.block) + rail_props.to_state_id(args.block) + }) } - async fn placed(&self, args: PlacedArgs<'_>) { - update_flanking_rails_shape(args.world, args.block, args.state_id, args.position).await; + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + update_flanking_rails_shape(args.world, args.block, args.state_id, args.position).await; + }) } - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - if !rail_placement_is_valid(args.world, args.block, args.position).await { - args.world - .break_block(args.position, None, BlockFlags::NOTIFY_ALL) - .await; - return; - } + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if !rail_placement_is_valid(args.world, args.block, args.position).await { + args.world + .break_block(args.position, None, BlockFlags::NOTIFY_ALL) + .await; + } + }) } - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - can_place_rail_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_rail_at(args.block_accessor, args.position).await }) } } diff --git a/pumpkin/src/block/blocks/redstone/rails/powered_rail.rs b/pumpkin/src/block/blocks/redstone/rails/powered_rail.rs index 5210aff7b..5a875ee63 100644 --- a/pumpkin/src/block/blocks/redstone/rails/powered_rail.rs +++ b/pumpkin/src/block/blocks/redstone/rails/powered_rail.rs @@ -1,10 +1,10 @@ -use async_trait::async_trait; use pumpkin_macros::pumpkin_block; use pumpkin_world::BlockStateId; use pumpkin_world::world::BlockFlags; use std::sync::Arc; use crate::block::BlockBehaviour; +use crate::block::BlockFuture; use crate::block::CanPlaceAtArgs; use crate::block::OnNeighborUpdateArgs; use crate::block::OnPlaceArgs; @@ -37,155 +37,187 @@ use super::common::{ #[pumpkin_block("minecraft:powered_rail")] pub struct PoweredRailBlock; -#[async_trait] impl BlockBehaviour for PoweredRailBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut rail_props = RailProperties::default(args.block); - let player_facing = args.player.living_entity.entity.get_horizontal_facing(); + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut rail_props = RailProperties::default(args.block); + let player_facing = args.player.living_entity.entity.get_horizontal_facing(); - rail_props.set_waterlogged(args.replacing.water_source()); - rail_props.set_straight_shape( - compute_placed_rail_shape(args.world, args.position, player_facing).await, - ); + rail_props.set_waterlogged(args.replacing.water_source()); + rail_props.set_straight_shape( + compute_placed_rail_shape(args.world, args.position, player_facing).await, + ); - rail_props.to_state_id(args.block) + rail_props.to_state_id(args.block) + }) } - async fn placed(&self, args: PlacedArgs<'_>) { - update_flanking_rails_shape(args.world, args.block, args.state_id, args.position).await; + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + update_flanking_rails_shape(args.world, args.block, args.state_id, args.position).await; - self.update_powered_state(args.world, args.block, args.position) - .await; - - let final_state_id = args.world.get_block_state_id(args.position).await; - let rail_props = RailProperties::new(final_state_id, args.block); - - self.update_connected_rails(args.world, args.position, &rail_props, true, 0) - .await; - self.update_connected_rails(args.world, args.position, &rail_props, false, 0) - .await; - - for direction in rail_props.directions() { - let neighbor_pos = args.position.offset(direction.to_offset()); - - if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &neighbor_pos).await - { - self.update_powered_state_internal( - args.world, - neighbor_rail.0, - &neighbor_pos, - false, - ) + self.update_powered_state(args.world, args.block, args.position) .await; - self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, false, 0) - .await; - } - let up_pos = neighbor_pos.up(); - if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &up_pos).await { - self.update_powered_state_internal(args.world, neighbor_rail.0, &up_pos, false) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0) - .await; - } + let final_state_id = args.world.get_block_state_id(args.position).await; + let rail_props = RailProperties::new(final_state_id, args.block); - let down_pos = neighbor_pos.down(); - if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &down_pos).await { - self.update_powered_state_internal(args.world, neighbor_rail.0, &down_pos, false) + self.update_connected_rails(args.world, args.position, &rail_props, true, 0) + .await; + self.update_connected_rails(args.world, args.position, &rail_props, false, 0) + .await; + + for direction in rail_props.directions() { + let neighbor_pos = args.position.offset(direction.to_offset()); + + if let Some(neighbor_rail) = + self.find_rail_at_position(args.world, &neighbor_pos).await + { + self.update_powered_state_internal( + args.world, + neighbor_rail.0, + &neighbor_pos, + false, + ) .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0) + self.update_connected_rails( + args.world, + &neighbor_pos, + &neighbor_rail.1, + true, + 0, + ) .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0) + self.update_connected_rails( + args.world, + &neighbor_pos, + &neighbor_rail.1, + false, + 0, + ) .await; + } + + let up_pos = neighbor_pos.up(); + if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &up_pos).await { + self.update_powered_state_internal(args.world, neighbor_rail.0, &up_pos, false) + .await; + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0) + .await; + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0) + .await; + } + + let down_pos = neighbor_pos.down(); + if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &down_pos).await + { + self.update_powered_state_internal( + args.world, + neighbor_rail.0, + &down_pos, + false, + ) + .await; + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0) + .await; + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0) + .await; + } } - } + }) } - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - if !rail_placement_is_valid(args.world, args.block, args.position).await { + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if !rail_placement_is_valid(args.world, args.block, args.position).await { + args.world + .break_block(args.position, None, BlockFlags::NOTIFY_ALL) + .await; + return; + } + + self.update_powered_state(args.world, args.block, args.position) + .await; + + let state_id = args.world.get_block_state_id(args.position).await; + let rail_props = RailProperties::new(state_id, args.block); + + self.update_connected_rails(args.world, args.position, &rail_props, true, 0) + .await; + self.update_connected_rails(args.world, args.position, &rail_props, false, 0) + .await; + }) + } + + fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let state_id = args.old_state_id; + let rail_props = RailProperties::new(state_id, args.block); + + if rail_props.shape().is_ascending() { + args.world + .update_neighbor(&args.position.up(), args.block) + .await; + } + + args.world.update_neighbor(args.position, args.block).await; args.world - .break_block(args.position, None, BlockFlags::NOTIFY_ALL) + .update_neighbor(&args.position.down(), args.block) .await; - return; - } - self.update_powered_state(args.world, args.block, args.position) - .await; + let directions = rail_props.directions(); + for direction in directions { + let neighbor_pos = args.position.offset(direction.to_offset()); - let state_id = args.world.get_block_state_id(args.position).await; - let rail_props = RailProperties::new(state_id, args.block); - - self.update_connected_rails(args.world, args.position, &rail_props, true, 0) - .await; - self.update_connected_rails(args.world, args.position, &rail_props, false, 0) - .await; - } - - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - let state_id = args.old_state_id; - let rail_props = RailProperties::new(state_id, args.block); - - if rail_props.shape().is_ascending() { - args.world - .update_neighbor(&args.position.up(), args.block) - .await; - } - - args.world.update_neighbor(args.position, args.block).await; - args.world - .update_neighbor(&args.position.down(), args.block) - .await; - - let directions = rail_props.directions(); - for direction in directions { - let neighbor_pos = args.position.offset(direction.to_offset()); - - if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &neighbor_pos).await - { - self.update_powered_state(args.world, neighbor_rail.0, &neighbor_pos) + if let Some(neighbor_rail) = + self.find_rail_at_position(args.world, &neighbor_pos).await + { + self.update_powered_state(args.world, neighbor_rail.0, &neighbor_pos) + .await; + self.update_connected_rails( + args.world, + &neighbor_pos, + &neighbor_rail.1, + true, + 0, + ) .await; - self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &neighbor_pos, &neighbor_rail.1, false, 0) + self.update_connected_rails( + args.world, + &neighbor_pos, + &neighbor_rail.1, + false, + 0, + ) .await; + } + + let up_pos = neighbor_pos.up(); + if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &up_pos).await { + self.update_powered_state(args.world, neighbor_rail.0, &up_pos) + .await; + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0) + .await; + self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0) + .await; + } + + let down_pos = neighbor_pos.down(); + if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &down_pos).await + { + self.update_powered_state(args.world, neighbor_rail.0, &down_pos) + .await; + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0) + .await; + self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0) + .await; + } } - - let up_pos = neighbor_pos.up(); - if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &up_pos).await { - self.update_powered_state(args.world, neighbor_rail.0, &up_pos) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &up_pos, &neighbor_rail.1, false, 0) - .await; - } - - let down_pos = neighbor_pos.down(); - if let Some(neighbor_rail) = self.find_rail_at_position(args.world, &down_pos).await { - self.update_powered_state(args.world, neighbor_rail.0, &down_pos) - .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, true, 0) - .await; - self.update_connected_rails(args.world, &down_pos, &neighbor_rail.1, false, 0) - .await; - } - } + }) } - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - can_place_rail_at(args.block_accessor, args.position).await - } - - 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 + fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> { + Box::pin(async move { can_place_rail_at(args.block_accessor, args.position).await }) } } diff --git a/pumpkin/src/block/blocks/redstone/rails/rail.rs b/pumpkin/src/block/blocks/redstone/rails/rail.rs index 020ea1119..e2778cc0d 100644 --- a/pumpkin/src/block/blocks/redstone/rails/rail.rs +++ b/pumpkin/src/block/blocks/redstone/rails/rail.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::block_properties::HorizontalFacing; use pumpkin_data::block_properties::RailShape; use pumpkin_macros::pumpkin_block; @@ -6,6 +5,7 @@ use pumpkin_world::BlockStateId; use pumpkin_world::world::BlockFlags; use crate::block::BlockBehaviour; +use crate::block::BlockFuture; use crate::block::CanPlaceAtArgs; use crate::block::OnNeighborUpdateArgs; use crate::block::OnPlaceArgs; @@ -18,107 +18,111 @@ use super::{HorizontalFacingRailExt, Rail, RailElevation, RailProperties}; #[pumpkin_block("minecraft:rail")] pub struct RailBlock; -#[async_trait] impl BlockBehaviour for RailBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let world = args.world; - let block_pos = args.position; - let mut rail_props = RailProperties::default(args.block); - rail_props.set_waterlogged(args.replacing.water_source()); + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let world = args.world; + let block_pos = args.position; + let mut rail_props = RailProperties::default(args.block); + rail_props.set_waterlogged(args.replacing.water_source()); - let shape = if let Some(east_rail) = - Rail::find_if_unlocked(world, block_pos, HorizontalFacing::East).await - { - if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::South) - .await - .is_some() + let shape = if let Some(east_rail) = + Rail::find_if_unlocked(world, block_pos, HorizontalFacing::East).await { - RailShape::SouthEast - } else if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North) - .await - .is_some() - { - RailShape::NorthEast - } else { - match Rail::find_if_unlocked(world, block_pos, HorizontalFacing::West).await { - Some(west_rail) if west_rail.elevation == RailElevation::Up => { - RailShape::AscendingWest - } - _ => { - if east_rail.elevation == RailElevation::Up { - RailShape::AscendingEast - } else { - RailShape::EastWest + if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::South) + .await + .is_some() + { + RailShape::SouthEast + } else if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North) + .await + .is_some() + { + RailShape::NorthEast + } else { + match Rail::find_if_unlocked(world, block_pos, HorizontalFacing::West).await { + Some(west_rail) if west_rail.elevation == RailElevation::Up => { + RailShape::AscendingWest + } + _ => { + if east_rail.elevation == RailElevation::Up { + RailShape::AscendingEast + } else { + RailShape::EastWest + } } } } - } - } else if let Some(south_rail) = - Rail::find_if_unlocked(world, block_pos, HorizontalFacing::South).await - { - if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::West) - .await - .is_some() + } else if let Some(south_rail) = + Rail::find_if_unlocked(world, block_pos, HorizontalFacing::South).await { - RailShape::SouthWest - } else if south_rail.elevation == RailElevation::Up { - RailShape::AscendingSouth - } else { - match Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North).await { - Some(north_rail) if north_rail.elevation == RailElevation::Up => { - RailShape::AscendingNorth + if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::West) + .await + .is_some() + { + RailShape::SouthWest + } else if south_rail.elevation == RailElevation::Up { + RailShape::AscendingSouth + } else { + match Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North).await { + Some(north_rail) if north_rail.elevation == RailElevation::Up => { + RailShape::AscendingNorth + } + _ => RailShape::NorthSouth, } - _ => RailShape::NorthSouth, } - } - } else if let Some(west_rail) = - Rail::find_if_unlocked(world, block_pos, HorizontalFacing::West).await - { - if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North) - .await - .is_some() + } else if let Some(west_rail) = + Rail::find_if_unlocked(world, block_pos, HorizontalFacing::West).await { - RailShape::NorthWest - } else if west_rail.elevation == RailElevation::Up { - RailShape::AscendingWest + if Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North) + .await + .is_some() + { + RailShape::NorthWest + } else if west_rail.elevation == RailElevation::Up { + RailShape::AscendingWest + } else { + RailShape::EastWest + } + } else if let Some(north_rail) = + Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North).await + { + if north_rail.elevation == RailElevation::Up { + RailShape::AscendingNorth + } else { + RailShape::NorthSouth + } } else { - RailShape::EastWest + args.player + .living_entity + .entity + .get_horizontal_facing() + .to_rail_shape_flat() + .as_shape() + }; + + rail_props.set_shape(shape); + rail_props.to_state_id(args.block) + }) + } + + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + update_flanking_rails_shape(args.world, args.block, args.state_id, args.position).await; + }) + } + + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if !rail_placement_is_valid(args.world, args.block, args.position).await { + args.world + .break_block(args.position, None, BlockFlags::NOTIFY_ALL) + .await; } - } else if let Some(north_rail) = - Rail::find_if_unlocked(world, block_pos, HorizontalFacing::North).await - { - if north_rail.elevation == RailElevation::Up { - RailShape::AscendingNorth - } else { - RailShape::NorthSouth - } - } else { - args.player - .living_entity - .entity - .get_horizontal_facing() - .to_rail_shape_flat() - .as_shape() - }; - - rail_props.set_shape(shape); - rail_props.to_state_id(args.block) + }) } - async fn placed(&self, args: PlacedArgs<'_>) { - update_flanking_rails_shape(args.world, args.block, args.state_id, args.position).await; - } - - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - if !rail_placement_is_valid(args.world, args.block, args.position).await { - args.world - .break_block(args.position, None, BlockFlags::NOTIFY_ALL) - .await; - return; - } - } - - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - can_place_rail_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_rail_at(args.block_accessor, args.position).await }) } } diff --git a/pumpkin/src/block/blocks/redstone/redstone_block.rs b/pumpkin/src/block/blocks/redstone/redstone_block.rs index 1be7198e1..d5d557385 100644 --- a/pumpkin/src/block/blocks/redstone/redstone_block.rs +++ b/pumpkin/src/block/blocks/redstone/redstone_block.rs @@ -1,18 +1,22 @@ -use async_trait::async_trait; use pumpkin_macros::pumpkin_block; -use crate::block::{BlockBehaviour, EmitsRedstonePowerArgs, GetRedstonePowerArgs}; +use crate::block::{BlockBehaviour, BlockFuture, EmitsRedstonePowerArgs, GetRedstonePowerArgs}; #[pumpkin_block("minecraft:redstone_block")] pub struct RedstoneBlock; -#[async_trait] impl BlockBehaviour for RedstoneBlock { - async fn get_weak_redstone_power(&self, _args: GetRedstonePowerArgs<'_>) -> u8 { - 15 + fn get_weak_redstone_power<'a>( + &'a self, + _args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { 15 }) } - async fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { - true + fn emits_redstone_power<'a>( + &'a self, + _args: EmitsRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { true }) } } diff --git a/pumpkin/src/block/blocks/redstone/redstone_lamp.rs b/pumpkin/src/block/blocks/redstone/redstone_lamp.rs index d4e0186ba..bb9c672de 100644 --- a/pumpkin/src/block/blocks/redstone/redstone_lamp.rs +++ b/pumpkin/src/block/blocks/redstone/redstone_lamp.rs @@ -1,5 +1,4 @@ -use crate::block::{OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs}; -use async_trait::async_trait; +use crate::block::{BlockFuture, OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs}; use pumpkin_data::block_properties::BlockProperties; use pumpkin_macros::pumpkin_block; use pumpkin_world::{BlockStateId, tick::TickPriority, world::BlockFlags}; @@ -13,26 +12,49 @@ type RedstoneLampProperties = pumpkin_data::block_properties::RedstoneOreLikePro #[pumpkin_block("minecraft:redstone_lamp")] pub struct RedstoneLamp; -#[async_trait] impl BlockBehaviour for RedstoneLamp { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut props = RedstoneLampProperties::default(args.block); - props.lit = block_receives_redstone_power(args.world, args.position).await; - 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 = RedstoneLampProperties::default(args.block); + props.lit = block_receives_redstone_power(args.world, args.position).await; + props.to_state_id(args.block) + }) } - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - let state = args.world.get_block_state(args.position).await; - let mut props = RedstoneLampProperties::from_state_id(state.id, args.block); - let is_lit = props.lit; - let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let state = args.world.get_block_state(args.position).await; + let mut props = RedstoneLampProperties::from_state_id(state.id, args.block); + let is_lit = props.lit; + let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; - if is_lit != is_receiving_power { - if is_lit { - args.world - .schedule_block_tick(args.block, *args.position, 4, TickPriority::Normal) - .await; - } else { + if is_lit != is_receiving_power { + if is_lit { + args.world + .schedule_block_tick(args.block, *args.position, 4, TickPriority::Normal) + .await; + } else { + props.lit = !props.lit; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ) + .await; + } + } + }) + } + + fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let state = args.world.get_block_state(args.position).await; + let mut props = RedstoneLampProperties::from_state_id(state.id, args.block); + let is_lit = props.lit; + let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; + + if is_lit && !is_receiving_power { props.lit = !props.lit; args.world .set_block_state( @@ -42,24 +64,6 @@ impl BlockBehaviour for RedstoneLamp { ) .await; } - } - } - - async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { - let state = args.world.get_block_state(args.position).await; - let mut props = RedstoneLampProperties::from_state_id(state.id, args.block); - let is_lit = props.lit; - let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; - - if is_lit && !is_receiving_power { - props.lit = !props.lit; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - } + }) } } diff --git a/pumpkin/src/block/blocks/redstone/redstone_torch.rs b/pumpkin/src/block/blocks/redstone/redstone_torch.rs index 09fe66296..db21a4adc 100644 --- a/pumpkin/src/block/blocks/redstone/redstone_torch.rs +++ b/pumpkin/src/block/blocks/redstone/redstone_torch.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use crate::block::BlockFuture; use crate::block::BlockIsReplacing; use crate::block::CanPlaceAtArgs; use crate::block::EmitsRedstonePowerArgs; @@ -11,7 +12,6 @@ use crate::block::OnScheduledTickArgs; use crate::block::OnStateReplacedArgs; use crate::block::PlacedArgs; use crate::entity::EntityBase; -use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::BlockDirection; use pumpkin_data::FacingExt; @@ -44,219 +44,246 @@ impl BlockMetadata for RedstoneTorchBlock { } } -#[async_trait] impl BlockBehaviour for RedstoneTorchBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let world = args.world; - let block = args.block; - let location = args.position; + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let world = args.world; + let block = args.block; + let location = args.position; + + if args.direction == BlockDirection::Down { + let support_block = world.get_block_state(&location.down()).await; + if support_block.is_center_solid(BlockDirection::Up) { + return block.default_state.id; + } + } + let mut directions = args.player.get_entity().get_entity_facing_order(); + + if args.replacing == BlockIsReplacing::None { + let face = args.direction.to_facing(); + let mut i = 0; + while i < directions.len() && directions[i] != face { + i += 1; + } + + if i > 0 { + directions.copy_within(0..i, 1); + directions[0] = face; + } + } else if directions[0] == Facing::Down { + let support_block = world.get_block_state(&location.down()).await; + if support_block.is_center_solid(BlockDirection::Up) { + return block.default_state.id; + } + } + + for dir in directions { + if dir != Facing::Up + && dir != Facing::Down + && can_place_at(world, location, dir.to_block_direction()).await + { + let mut torch_props = RWallTorchProps::default(&Block::REDSTONE_WALL_TORCH); + torch_props.facing = dir + .opposite() + .to_block_direction() + .to_horizontal_facing() + .unwrap(); + return torch_props.to_state_id(&Block::REDSTONE_WALL_TORCH); + } + } - if args.direction == BlockDirection::Down { let support_block = world.get_block_state(&location.down()).await; if support_block.is_center_solid(BlockDirection::Up) { - return block.default_state.id; + block.default_state.id + } else { + 0 } - } - let mut directions = args.player.get_entity().get_entity_facing_order(); - - if args.replacing == BlockIsReplacing::None { - let face = args.direction.to_facing(); - let mut i = 0; - while i < directions.len() && directions[i] != face { - i += 1; - } - - if i > 0 { - directions.copy_within(0..i, 1); - directions[0] = face; - } - } else if directions[0] == Facing::Down { - let support_block = world.get_block_state(&location.down()).await; - if support_block.is_center_solid(BlockDirection::Up) { - return block.default_state.id; - } - } - - for dir in directions { - if dir != Facing::Up - && dir != Facing::Down - && can_place_at(world, location, dir.to_block_direction()).await - { - let mut torch_props = RWallTorchProps::default(&Block::REDSTONE_WALL_TORCH); - torch_props.facing = dir - .opposite() - .to_block_direction() - .to_horizontal_facing() - .unwrap(); - return torch_props.to_state_id(&Block::REDSTONE_WALL_TORCH); - } - } - - let support_block = world.get_block_state(&location.down()).await; - if support_block.is_center_solid(BlockDirection::Up) { - block.default_state.id - } else { - 0 - } + }) } - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - let support_block = args - .block_accessor - .get_block_state(&args.position.down()) - .await; - if support_block.is_center_solid(BlockDirection::Up) { - return true; - } - for dir in BlockDirection::horizontal() { - if can_place_at(args.block_accessor, args.position, dir).await { + fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> { + Box::pin(async move { + let support_block = args + .block_accessor + .get_block_state(&args.position.down()) + .await; + if support_block.is_center_solid(BlockDirection::Up) { return true; } - } - false + for dir in BlockDirection::horizontal() { + if can_place_at(args.block_accessor, args.position, dir).await { + return true; + } + } + false + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - if args.block == &Block::REDSTONE_WALL_TORCH { - let props = RWallTorchProps::from_state_id(args.state_id, args.block); - if props.facing.to_block_direction().opposite() == args.direction - && !can_place_at( - args.world, - args.position, - props.facing.to_block_direction().opposite(), - ) + fn get_state_for_neighbor_update<'a>( + &'a self, + args: GetStateForNeighborUpdateArgs<'a>, + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + if args.block == &Block::REDSTONE_WALL_TORCH { + let props = RWallTorchProps::from_state_id(args.state_id, args.block); + if props.facing.to_block_direction().opposite() == args.direction + && !can_place_at( + args.world, + args.position, + props.facing.to_block_direction().opposite(), + ) + .await + { + return 0; + } + } else if args.direction == BlockDirection::Down { + let support_block = args.world.get_block_state(&args.position.down()).await; + if !support_block.is_center_solid(BlockDirection::Up) { + return 0; + } + } + args.state_id + }) + } + + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let state = args.world.get_block_state(args.position).await; + + if args + .world + .is_block_tick_scheduled(args.position, args.block) .await { - return 0; + return; } - } else if args.direction == BlockDirection::Down { - let support_block = args.world.get_block_state(&args.position.down()).await; - if !support_block.is_center_solid(BlockDirection::Up) { - return 0; + + if args.block == &Block::REDSTONE_WALL_TORCH { + let props = RWallTorchProps::from_state_id(state.id, args.block); + if props.lit + != should_be_lit( + args.world, + args.position, + props.facing.to_block_direction().opposite(), + ) + .await + { + args.world + .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal) + .await; + } + } else if args.block == &Block::REDSTONE_TORCH { + let props = RTorchProps::from_state_id(state.id, args.block); + if props.lit != should_be_lit(args.world, args.position, BlockDirection::Down).await + { + args.world + .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal) + .await; + } } - } - args.state_id + }) } - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - let state = args.world.get_block_state(args.position).await; - - if args - .world - .is_block_tick_scheduled(args.position, args.block) - .await - { - return; - } - - if args.block == &Block::REDSTONE_WALL_TORCH { - let props = RWallTorchProps::from_state_id(state.id, args.block); - if props.lit - != should_be_lit( - args.world, - args.position, - props.facing.to_block_direction().opposite(), - ) - .await - { - args.world - .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal) - .await; - } - } else if args.block == &Block::REDSTONE_TORCH { - let props = RTorchProps::from_state_id(state.id, args.block); - if props.lit != should_be_lit(args.world, args.position, BlockDirection::Down).await { - args.world - .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal) - .await; - } - } + fn emits_redstone_power<'a>( + &'a self, + _args: EmitsRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { true }) } - async fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { - true - } - - async fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - if args.block == &Block::REDSTONE_WALL_TORCH { - let props = RWallTorchProps::from_state_id(args.state.id, args.block); - if props.lit && args.direction != props.facing.to_block_direction() { - return 15; - } - } else if args.block == &Block::REDSTONE_TORCH { - let props = RTorchProps::from_state_id(args.state.id, args.block); - if props.lit && args.direction != BlockDirection::Up { - return 15; - } - } - 0 - } - - async fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - if args.direction == BlockDirection::Down { + fn get_weak_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { if args.block == &Block::REDSTONE_WALL_TORCH { let props = RWallTorchProps::from_state_id(args.state.id, args.block); - if props.lit { + if props.lit && args.direction != props.facing.to_block_direction() { return 15; } } else if args.block == &Block::REDSTONE_TORCH { let props = RTorchProps::from_state_id(args.state.id, args.block); - if props.lit { + if props.lit && args.direction != BlockDirection::Up { return 15; } } - } - 0 + 0 + }) } - async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { - let state = args.world.get_block_state(args.position).await; - if args.block == &Block::REDSTONE_WALL_TORCH { - let mut props = RWallTorchProps::from_state_id(state.id, args.block); - let should_be_lit_now = should_be_lit( - args.world, - args.position, - props.facing.to_block_direction().opposite(), - ) - .await; - if props.lit != should_be_lit_now { - props.lit = should_be_lit_now; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - update_neighbors(args.world, args.position).await; + fn get_strong_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { + if args.direction == BlockDirection::Down { + if args.block == &Block::REDSTONE_WALL_TORCH { + let props = RWallTorchProps::from_state_id(args.state.id, args.block); + if props.lit { + return 15; + } + } else if args.block == &Block::REDSTONE_TORCH { + let props = RTorchProps::from_state_id(args.state.id, args.block); + if props.lit { + return 15; + } + } } - } else if args.block == &Block::REDSTONE_TORCH { - let mut props = RTorchProps::from_state_id(state.id, args.block); - let should_be_lit_now = - should_be_lit(args.world, args.position, BlockDirection::Down).await; - if props.lit != should_be_lit_now { - props.lit = should_be_lit_now; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - update_neighbors(args.world, args.position).await; + 0 + }) + } + + fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let state = args.world.get_block_state(args.position).await; + if args.block == &Block::REDSTONE_WALL_TORCH { + let mut props = RWallTorchProps::from_state_id(state.id, args.block); + let should_be_lit_now = should_be_lit( + args.world, + args.position, + props.facing.to_block_direction().opposite(), + ) + .await; + if props.lit != should_be_lit_now { + props.lit = should_be_lit_now; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ) + .await; + update_neighbors(args.world, args.position).await; + } + } else if args.block == &Block::REDSTONE_TORCH { + let mut props = RTorchProps::from_state_id(state.id, args.block); + let should_be_lit_now = + should_be_lit(args.world, args.position, BlockDirection::Down).await; + if props.lit != should_be_lit_now { + props.lit = should_be_lit_now; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ) + .await; + update_neighbors(args.world, args.position).await; + } } - } + }) } - async fn placed(&self, args: PlacedArgs<'_>) { - update_neighbors(args.world, args.position).await; + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + update_neighbors(args.world, args.position).await; + }) } - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - update_neighbors(args.world, args.position).await; + fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + update_neighbors(args.world, args.position).await; + }) } } diff --git a/pumpkin/src/block/blocks/redstone/redstone_wire.rs b/pumpkin/src/block/blocks/redstone/redstone_wire.rs index f5ebcd352..e0056276d 100644 --- a/pumpkin/src/block/blocks/redstone/redstone_wire.rs +++ b/pumpkin/src/block/blocks/redstone/redstone_wire.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use async_trait::async_trait; use pumpkin_data::block_properties::{ BlockProperties, EastWireConnection, EnumVariants, Integer0To15, NorthWireConnection, ObserverLikeProperties, RedstoneWireLikeProperties, RepeaterLikeProperties, @@ -14,7 +13,7 @@ use pumpkin_world::world::{BlockAccessor, BlockFlags}; use crate::block::registry::BlockActionResult; use crate::block::{ - BrokenArgs, CanPlaceAtArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, + BlockFuture, BrokenArgs, CanPlaceAtArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs, PrepareArgs, }; use crate::{ @@ -30,169 +29,195 @@ type RedstoneWireProperties = RedstoneWireLikeProperties; #[pumpkin_block("minecraft:redstone_wire")] pub struct RedstoneWireBlock; -#[async_trait] impl BlockBehaviour for RedstoneWireBlock { - 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 on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut wire = RedstoneWireProperties::default(args.block); - wire.power = - Integer0To15::from_index(calculate_power(args.world, args.position).await.into()); - wire = get_regulated_sides(wire, args.world, args.position).await; - if is_dot(wire) { - wire = make_cross(wire.power); - } + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut wire = RedstoneWireProperties::default(args.block); + wire.power = + Integer0To15::from_index(calculate_power(args.world, args.position).await.into()); + wire = get_regulated_sides(wire, args.world, args.position).await; + if is_dot(wire) { + wire = make_cross(wire.power); + } - wire.to_state_id(args.block) + wire.to_state_id(args.block) + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - let mut wire = RedstoneWireProperties::from_state_id(args.state_id, args.block); - let old_state = wire; - let new_side: WireConnection; + fn get_state_for_neighbor_update<'a>( + &'a self, + args: GetStateForNeighborUpdateArgs<'a>, + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut wire = RedstoneWireProperties::from_state_id(args.state_id, args.block); + let old_state = wire; + let new_side: WireConnection; - match args.direction { - BlockDirection::Up => { - return args.state_id; - } - BlockDirection::Down => { - return get_regulated_sides(wire, args.world, args.position) - .await - .to_state_id(args.block); - } - BlockDirection::North => { - let side = get_side(args.world, args.position, BlockDirection::North).await; - wire.north = side.to_north(); - new_side = side; - } - BlockDirection::South => { - let side = get_side(args.world, args.position, BlockDirection::South).await; - wire.south = side.to_south(); - new_side = side; - } - BlockDirection::East => { - let side = get_side(args.world, args.position, BlockDirection::East).await; - wire.east = side.to_east(); - new_side = side; - } - BlockDirection::West => { - let side = get_side(args.world, args.position, BlockDirection::West).await; - wire.west = side.to_west(); - new_side = side; - } - } - - wire = get_regulated_sides(wire, args.world, args.position).await; - if is_cross(old_state) && new_side.is_none() { - return wire.to_state_id(args.block); - } - if !is_dot(old_state) && is_dot(wire) { - let power = wire.power; - wire = make_cross(power); - } - wire.to_state_id(args.block) - } - - async fn prepare(&self, args: PrepareArgs<'_>) { - let wire_props = - RedstoneWireLikeProperties::from_state_id(args.state_id, &Block::REDSTONE_WIRE); - - for direction in BlockDirection::horizontal() { - let other_block_pos = args.position.offset(direction.to_offset()); - let other_block = args.world.get_block(&other_block_pos).await; - - if wire_props.is_side_connected(direction) && other_block != &Block::REDSTONE_WIRE { - let up_block_pos = other_block_pos.up(); - let up_block = args.world.get_block(&up_block_pos).await; - if up_block == &Block::REDSTONE_WIRE { - args.world - .replace_with_state_for_neighbor_update( - &up_block_pos, - direction.opposite(), - args.flags, - ) - .await; + match args.direction { + BlockDirection::Up => { + return args.state_id; } - - let down_block_pos = other_block_pos.down(); - let down_block = args.world.get_block(&down_block_pos).await; - if down_block == &Block::REDSTONE_WIRE { - args.world - .replace_with_state_for_neighbor_update( - &down_block_pos, - direction.opposite(), - args.flags, - ) - .await; + BlockDirection::Down => { + return get_regulated_sides(wire, args.world, args.position) + .await + .to_state_id(args.block); + } + BlockDirection::North => { + let side = get_side(args.world, args.position, BlockDirection::North).await; + wire.north = side.to_north(); + new_side = side; + } + BlockDirection::South => { + let side = get_side(args.world, args.position, BlockDirection::South).await; + wire.south = side.to_south(); + new_side = side; + } + BlockDirection::East => { + let side = get_side(args.world, args.position, BlockDirection::East).await; + wire.east = side.to_east(); + new_side = side; + } + BlockDirection::West => { + let side = get_side(args.world, args.position, BlockDirection::West).await; + wire.west = side.to_west(); + new_side = side; } } - } + + wire = get_regulated_sides(wire, args.world, args.position).await; + if is_cross(old_state) && new_side.is_none() { + return wire.to_state_id(args.block); + } + if !is_dot(old_state) && is_dot(wire) { + let power = wire.power; + wire = make_cross(power); + } + wire.to_state_id(args.block) + }) } - async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - let state = args.world.get_block_state(args.position).await; - let wire = RedstoneWireProperties::from_state_id(state.id, args.block); - if on_use(wire, args.world, args.position).await { - BlockActionResult::Success - } else { - BlockActionResult::Pass - } + fn prepare<'a>(&'a self, args: PrepareArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let wire_props = + RedstoneWireLikeProperties::from_state_id(args.state_id, &Block::REDSTONE_WIRE); + + for direction in BlockDirection::horizontal() { + let other_block_pos = args.position.offset(direction.to_offset()); + let other_block = args.world.get_block(&other_block_pos).await; + + if wire_props.is_side_connected(direction) && other_block != &Block::REDSTONE_WIRE { + let up_block_pos = other_block_pos.up(); + let up_block = args.world.get_block(&up_block_pos).await; + if up_block == &Block::REDSTONE_WIRE { + args.world + .replace_with_state_for_neighbor_update( + &up_block_pos, + direction.opposite(), + args.flags, + ) + .await; + } + + let down_block_pos = other_block_pos.down(); + let down_block = args.world.get_block(&down_block_pos).await; + if down_block == &Block::REDSTONE_WIRE { + args.world + .replace_with_state_for_neighbor_update( + &down_block_pos, + direction.opposite(), + args.flags, + ) + .await; + } + } + } + }) } - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - if can_place_at(args.world.as_ref(), args.position).await { + 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 mut wire = RedstoneWireProperties::from_state_id(state.id, args.block); - let new_power = calculate_power(args.world, args.position).await; - if wire.power.to_index() as u8 != new_power { - wire.power = Integer0To15::from_index(new_power.into()); - args.world - .set_block_state( - args.position, - wire.to_state_id(&Block::REDSTONE_WIRE), - BlockFlags::empty(), - ) - .await; - RedstoneWireTurbo::update_surrounding_neighbors(args.world, *args.position).await; + let wire = RedstoneWireProperties::from_state_id(state.id, args.block); + if on_use(wire, args.world, args.position).await { + BlockActionResult::Success + } else { + BlockActionResult::Pass } - } else { - args.world - .break_block(args.position, None, BlockFlags::NOTIFY_ALL) - .await; - } + }) } - async fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - let wire = RedstoneWireProperties::from_state_id(args.state.id, args.block); - if args.direction == BlockDirection::Up || wire.is_side_connected(args.direction.opposite()) - { - wire.power.to_index() as u8 - } else { - 0 - } + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if can_place_at(args.world.as_ref(), args.position).await { + let state = args.world.get_block_state(args.position).await; + let mut wire = RedstoneWireProperties::from_state_id(state.id, args.block); + let new_power = calculate_power(args.world, args.position).await; + if wire.power.to_index() as u8 != new_power { + wire.power = Integer0To15::from_index(new_power.into()); + args.world + .set_block_state( + args.position, + wire.to_state_id(&Block::REDSTONE_WIRE), + BlockFlags::empty(), + ) + .await; + RedstoneWireTurbo::update_surrounding_neighbors(args.world, *args.position) + .await; + } + } else { + args.world + .break_block(args.position, None, BlockFlags::NOTIFY_ALL) + .await; + } + }) } - async fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - let wire = RedstoneWireProperties::from_state_id(args.state.id, args.block); - if args.direction == BlockDirection::Up || wire.is_side_connected(args.direction.opposite()) - { - wire.power.to_index() as u8 - } else { - 0 - } + fn get_weak_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { + let wire = RedstoneWireProperties::from_state_id(args.state.id, args.block); + if args.direction == BlockDirection::Up + || wire.is_side_connected(args.direction.opposite()) + { + wire.power.to_index() as u8 + } else { + 0 + } + }) } - async fn placed(&self, args: PlacedArgs<'_>) { - update_wire_neighbors(args.world, args.position).await; + fn get_strong_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { + let wire = RedstoneWireProperties::from_state_id(args.state.id, args.block); + if args.direction == BlockDirection::Up + || wire.is_side_connected(args.direction.opposite()) + { + wire.power.to_index() as u8 + } else { + 0 + } + }) } - async fn broken(&self, args: BrokenArgs<'_>) { - update_wire_neighbors(args.world, args.position).await; + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + update_wire_neighbors(args.world, args.position).await; + }) + } + + fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + update_wire_neighbors(args.world, args.position).await; + }) } } diff --git a/pumpkin/src/block/blocks/redstone/repeater.rs b/pumpkin/src/block/blocks/redstone/repeater.rs index 09f363707..2821d33d7 100644 --- a/pumpkin/src/block/blocks/redstone/repeater.rs +++ b/pumpkin/src/block/blocks/redstone/repeater.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use async_trait::async_trait; use pumpkin_data::{ Block, BlockDirection, BlockState, HorizontalFacingExt, block_properties::{BlockProperties, EnumVariants, HorizontalFacing, Integer1To4}, @@ -12,12 +11,10 @@ use pumpkin_world::{BlockStateId, tick::TickPriority}; use crate::{ block::{ + BlockBehaviour, BlockFuture, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, + GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, + OnScheduledTickArgs, OnStateReplacedArgs, PlacedArgs, PlayerPlacedArgs, registry::BlockActionResult, - { - BlockBehaviour, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, - GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, - OnScheduledTickArgs, OnStateReplacedArgs, PlacedArgs, PlayerPlacedArgs, - }, }, world::World, }; @@ -29,158 +26,186 @@ type RepeaterProperties = pumpkin_data::block_properties::RepeaterLikeProperties #[pumpkin_block("minecraft:repeater")] pub struct RepeaterBlock; -#[async_trait] impl BlockBehaviour for RepeaterBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let state_id = RedstoneGateBlock::on_place(self, args.player, args.block).await; + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let state_id = RedstoneGateBlock::on_place(self, args.player, args.block).await; - let mut props = RepeaterProperties::from_state_id(state_id, args.block); - props.locked = self - .is_locked(args.world, *args.position, state_id, args.block) - .await; + let mut props = RepeaterProperties::from_state_id(state_id, args.block); + props.locked = self + .is_locked(args.world, *args.position, state_id, args.block) + .await; - props.to_state_id(args.block) + props.to_state_id(args.block) + }) } - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - RedstoneGateBlock::on_neighbor_update(self, args).await; + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + RedstoneGateBlock::on_neighbor_update(self, args).await; + }) } - async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { - let state = args.world.get_block_state(args.position).await; - if self - .is_locked(args.world, *args.position, state.id, args.block) - .await - { - return; - } - let mut props = RepeaterProperties::from_state_id(state.id, args.block); + fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let state = args.world.get_block_state(args.position).await; + if self + .is_locked(args.world, *args.position, state.id, args.block) + .await + { + return; + } + let mut props = RepeaterProperties::from_state_id(state.id, args.block); - let now_powered = props.powered; - let should_be_powered = self - .has_power(args.world, *args.position, state, args.block) - .await; + let now_powered = props.powered; + let should_be_powered = self + .has_power(args.world, *args.position, state, args.block) + .await; - if now_powered && !should_be_powered { - props.powered = false; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - RedstoneGateBlock::update_target( - self, - args.world, - *args.position, - props.to_state_id(args.block), - args.block, - ) - .await; - } else if !now_powered { - props.powered = true; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - if !should_be_powered { + if now_powered && !should_be_powered { + props.powered = false; args.world - .schedule_block_tick( - args.block, - *args.position, - RedstoneGateBlock::get_update_delay_internal( - self, - props.to_state_id(args.block), - args.block, - ), - TickPriority::VeryHigh, + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, ) .await; + RedstoneGateBlock::update_target( + self, + args.world, + *args.position, + props.to_state_id(args.block), + args.block, + ) + .await; + } else if !now_powered { + props.powered = true; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ) + .await; + if !should_be_powered { + args.world + .schedule_block_tick( + args.block, + *args.position, + RedstoneGateBlock::get_update_delay_internal( + self, + props.to_state_id(args.block), + args.block, + ), + TickPriority::VeryHigh, + ) + .await; + } + RedstoneGateBlock::update_target( + self, + args.world, + *args.position, + props.to_state_id(args.block), + args.block, + ) + .await; } + }) + } + + 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 props = RepeaterProperties::from_state_id(state.id, args.block); + self.on_use(props, args.world, *args.position, args.block) + .await; + + BlockActionResult::Success + }) + } + + fn get_weak_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { RedstoneGateBlock::get_weak_redstone_power(self, args).await }) + } + + fn get_strong_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { RedstoneGateBlock::get_strong_redstone_power(self, args).await }) + } + + fn emits_redstone_power<'a>( + &'a self, + args: EmitsRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { + let repeater_props = RepeaterProperties::from_state_id(args.state.id, args.block); + repeater_props.facing.to_block_direction() == args.direction + || repeater_props.facing.to_block_direction() == args.direction.opposite() + }) + } + + fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> { + Box::pin(async move { + RedstoneGateBlock::can_place_at(self, args.block_accessor, *args.position).await + }) + } + + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { RedstoneGateBlock::update_target( self, args.world, *args.position, - props.to_state_id(args.block), + BlockState::from_id(args.state_id).id, args.block, ) .await; - } + }) } - async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - let state = args.world.get_block_state(args.position).await; - let props = RepeaterProperties::from_state_id(state.id, args.block); - self.on_use(props, args.world, *args.position, args.block) - .await; - - BlockActionResult::Success + fn get_state_for_neighbor_update<'a>( + &'a self, + args: GetStateForNeighborUpdateArgs<'a>, + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + if args.direction == BlockDirection::Down + && !RedstoneGateBlock::can_place_above( + self, + args.world, + *args.neighbor_position, + BlockState::from_id(args.neighbor_state_id), + ) + .await + { + return Block::AIR.default_state.id; + } + let mut props = RepeaterProperties::from_state_id(args.state_id, args.block); + if args.direction.to_axis() != props.facing.to_block_direction().to_axis() { + props.locked = self + .is_locked(args.world, *args.position, args.state_id, args.block) + .await; + return props.to_state_id(args.block); + } + args.state_id + }) } - async fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - RedstoneGateBlock::get_weak_redstone_power(self, args).await + fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + RedstoneGateBlock::player_placed(self, args).await; + }) } - async fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - RedstoneGateBlock::get_strong_redstone_power(self, args).await - } - - async fn emits_redstone_power(&self, args: EmitsRedstonePowerArgs<'_>) -> bool { - let repeater_props = RepeaterProperties::from_state_id(args.state.id, args.block); - repeater_props.facing.to_block_direction() == args.direction - || repeater_props.facing.to_block_direction() == args.direction.opposite() - } - - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - RedstoneGateBlock::can_place_at(self, args.block_accessor, *args.position).await - } - - async fn placed(&self, args: PlacedArgs<'_>) { - RedstoneGateBlock::update_target( - self, - args.world, - *args.position, - BlockState::from_id(args.state_id).id, - args.block, - ) - .await; - } - - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - if args.direction == BlockDirection::Down - && !RedstoneGateBlock::can_place_above( - self, - args.world, - *args.neighbor_position, - BlockState::from_id(args.neighbor_state_id), - ) - .await - { - return Block::AIR.default_state.id; - } - let mut props = RepeaterProperties::from_state_id(args.state_id, args.block); - if args.direction.to_axis() != props.facing.to_block_direction().to_axis() { - props.locked = self - .is_locked(args.world, *args.position, args.state_id, args.block) - .await; - return props.to_state_id(args.block); - } - args.state_id - } - - async fn player_placed(&self, args: PlayerPlacedArgs<'_>) { - RedstoneGateBlock::player_placed(self, args).await; - } - - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - RedstoneGateBlock::on_state_replaced(self, args).await; + fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + RedstoneGateBlock::on_state_replaced(self, args).await; + }) } } @@ -198,43 +223,54 @@ impl RedstoneGateBlockProperties for RepeaterProperties { } } -#[async_trait] impl RedstoneGateBlock for RepeaterBlock { - async fn get_output_level(&self, _world: &World, _pos: BlockPos) -> u8 { - 15 + fn get_output_level<'a>(&'a self, _world: &'a World, _pos: BlockPos) -> BlockFuture<'a, u8> { + Box::pin(async { 15 }) } - async fn update_powered( - &self, - world: &World, + fn update_powered<'a>( + &'a self, + world: &'a World, pos: BlockPos, - state: &BlockState, - block: &Block, - ) { - if self.is_locked(world, pos, state.id, block).await { - return; - } - let props = RepeaterProperties::from_state_id(state.id, block); - let powered = props.powered; - let has_power = RedstoneGateBlock::has_power(self, world, pos, state, block).await; - if powered != has_power && !world.is_block_tick_scheduled(&pos, block).await { - let priority = - if RedstoneGateBlock::is_target_not_aligned(self, world, pos, state, block).await { - TickPriority::ExtremelyHigh - } else if powered { - TickPriority::VeryHigh - } else { - TickPriority::High - }; - world - .schedule_block_tick( - block, - pos, - RedstoneGateBlock::get_update_delay_internal(self, state.id, block), - priority, - ) - .await; - } + state: &'a BlockState, + block: &'a Block, + ) -> BlockFuture<'a, ()> { + Box::pin(async move { + // Note: is_locked is assumed to remain an async fn or return a future + if self.is_locked(world, pos, state.id, block).await { + return; + } + let props = RepeaterProperties::from_state_id(state.id, block); + let powered = props.powered; + + // 💡 FIX 3: Trait method calls now return futures and must be awaited. + // Note: The signature for has_power must be called without self, as it's a trait method. + let has_power = RedstoneGateBlock::has_power(self, world, pos, state, block).await; + + if powered != has_power && !world.is_block_tick_scheduled(&pos, block).await { + // 💡 FIX 4: is_target_not_aligned returns a Future and must be awaited. + let priority = + if RedstoneGateBlock::is_target_not_aligned(self, world, pos, state, block) + .await + { + TickPriority::ExtremelyHigh + } else if powered { + TickPriority::VeryHigh + } else { + TickPriority::High + }; + + world + .schedule_block_tick( + block, + pos, + // 💡 FIX 5: get_update_delay_internal is not async and is called normally. + RedstoneGateBlock::get_update_delay_internal(self, state.id, block), + priority, + ) + .await; + } + }) } fn get_update_delay_internal(&self, state_id: BlockStateId, block: &Block) -> u8 { diff --git a/pumpkin/src/block/blocks/redstone/target_block.rs b/pumpkin/src/block/blocks/redstone/target_block.rs index 0a2fe0900..cbb96c0da 100644 --- a/pumpkin/src/block/blocks/redstone/target_block.rs +++ b/pumpkin/src/block/blocks/redstone/target_block.rs @@ -1,14 +1,15 @@ -use async_trait::async_trait; use pumpkin_macros::pumpkin_block; -use crate::block::{BlockBehaviour, EmitsRedstonePowerArgs}; +use crate::block::{BlockBehaviour, BlockFuture, EmitsRedstonePowerArgs}; #[pumpkin_block("minecraft:target")] pub struct TargetBlock; -#[async_trait] impl BlockBehaviour for TargetBlock { - async fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { - true + fn emits_redstone_power<'a>( + &'a self, + _args: EmitsRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { true }) } } diff --git a/pumpkin/src/block/blocks/redstone/tripwire.rs b/pumpkin/src/block/blocks/redstone/tripwire.rs index 23131c160..6acbecc19 100644 --- a/pumpkin/src/block/blocks/redstone/tripwire.rs +++ b/pumpkin/src/block/blocks/redstone/tripwire.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use async_trait::async_trait; use pumpkin_data::item::Item; use pumpkin_data::{ Block, BlockDirection, @@ -10,6 +9,7 @@ use pumpkin_macros::pumpkin_block; use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos}; use pumpkin_world::{BlockStateId, tick::TickPriority, world::BlockFlags}; +use crate::block::BlockFuture; use crate::{ block::{ BlockBehaviour, BrokenArgs, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs, @@ -26,131 +26,145 @@ type TripwireHookProperties = pumpkin_data::block_properties::TripwireHookLikePr #[pumpkin_block("minecraft:tripwire")] pub struct TripwireBlock; -#[async_trait] impl BlockBehaviour for TripwireBlock { - async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { - let mut props = TripwireProperties::from_state_id(args.state.id, args.block); - if props.powered { - return; - } - props.powered = true; - - let state_id = props.to_state_id(args.block); - args.world - .set_block_state(args.position, state_id, BlockFlags::NOTIFY_ALL) - .await; - - Self::update(args.world, args.position, state_id).await; - - args.world - .schedule_block_tick(args.block, *args.position, 10, TickPriority::Normal) - .await; - } - - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let [connect_north, connect_east, connect_south, connect_west] = [ - BlockDirection::North, - BlockDirection::East, - BlockDirection::South, - BlockDirection::West, - ] - .map(async |dir| { - let current_pos = args.position.offset(dir.to_offset()); - let state_id = args.world.get_block_state_id(¤t_pos).await; - Self::should_connect_to(state_id, dir) - }); - - let mut props = TripwireProperties::from_state_id(args.block.default_state.id, args.block); - - props.north = connect_north.await; - props.south = connect_south.await; - props.west = connect_west.await; - props.east = connect_east.await; - - props.to_state_id(args.block) - } - - async fn placed(&self, args: PlacedArgs<'_>) { - if Block::from_state_id(args.old_state_id) == Block::from_state_id(args.state_id) { - return; - } - - Self::update(args.world, args.position, args.state_id).await; - } - - async fn broken(&self, args: BrokenArgs<'_>) { - let has_shears = { - let main_hand_item_stack = args.player.inventory().held_item(); - main_hand_item_stack - .lock() - .await - .get_item() - .eq(&Item::SHEARS) - }; - if has_shears { + fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { let mut props = TripwireProperties::from_state_id(args.state.id, args.block); - props.disarmed = true; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::empty(), - ) - .await; - // TODO world.emitGameEvent(player, GameEvent.SHEAR, pos); - } - } + if props.powered { + return; + } + props.powered = true; - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - args.direction - .to_horizontal_facing() - .map_or(args.state_id, |facing| { - let mut props = TripwireProperties::from_state_id(args.state_id, args.block); - *match facing { - HorizontalFacing::North => &mut props.north, - HorizontalFacing::South => &mut props.south, - HorizontalFacing::West => &mut props.west, - HorizontalFacing::East => &mut props.east, - } = Self::should_connect_to(args.neighbor_state_id, args.direction); - props.to_state_id(args.block) - }) - } - - async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { - let state_id = args.world.get_block_state_id(args.position).await; - - let mut props = TripwireProperties::from_state_id(state_id, args.block); - if !props.powered { - return; - } - - let aabb = BoundingBox::from_block(args.position); - // TODO entity.canAvoidTraps() - if args.world.get_entities_at_box(&aabb).await.is_empty() - && args.world.get_players_at_box(&aabb).await.is_empty() - { - props.powered = false; let state_id = props.to_state_id(args.block); args.world .set_block_state(args.position, state_id, BlockFlags::NOTIFY_ALL) .await; + Self::update(args.world, args.position, state_id).await; - } else { + args.world .schedule_block_tick(args.block, *args.position, 10, TickPriority::Normal) .await; - } + }) } - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - if args.moved || Block::from_state_id(args.old_state_id) == args.block { - return; - } - let state_id = args.world.get_block_state_id(args.position).await; - Self::update(args.world, args.position, state_id).await; + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let [connect_north, connect_east, connect_south, connect_west] = [ + BlockDirection::North, + BlockDirection::East, + BlockDirection::South, + BlockDirection::West, + ] + .map(async |dir| { + let current_pos = args.position.offset(dir.to_offset()); + let state_id = args.world.get_block_state_id(¤t_pos).await; + Self::should_connect_to(state_id, dir) + }); + + let mut props = + TripwireProperties::from_state_id(args.block.default_state.id, args.block); + + props.north = connect_north.await; + props.south = connect_south.await; + props.west = connect_west.await; + props.east = connect_east.await; + + props.to_state_id(args.block) + }) + } + + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if Block::from_state_id(args.old_state_id) == Block::from_state_id(args.state_id) { + return; + } + + Self::update(args.world, args.position, args.state_id).await; + }) + } + + fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let has_shears = { + let main_hand_item_stack = args.player.inventory().held_item(); + main_hand_item_stack + .lock() + .await + .get_item() + .eq(&Item::SHEARS) + }; + if has_shears { + let mut props = TripwireProperties::from_state_id(args.state.id, args.block); + props.disarmed = true; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::empty(), + ) + .await; + // TODO world.emitGameEvent(player, GameEvent.SHEAR, pos); + } + }) + } + + fn get_state_for_neighbor_update<'a>( + &'a self, + args: GetStateForNeighborUpdateArgs<'a>, + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + args.direction + .to_horizontal_facing() + .map_or(args.state_id, |facing| { + let mut props = TripwireProperties::from_state_id(args.state_id, args.block); + *match facing { + HorizontalFacing::North => &mut props.north, + HorizontalFacing::South => &mut props.south, + HorizontalFacing::West => &mut props.west, + HorizontalFacing::East => &mut props.east, + } = Self::should_connect_to(args.neighbor_state_id, args.direction); + props.to_state_id(args.block) + }) + }) + } + + 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 mut props = TripwireProperties::from_state_id(state_id, args.block); + if !props.powered { + return; + } + + let aabb = BoundingBox::from_block(args.position); + // TODO entity.canAvoidTraps() + if args.world.get_entities_at_box(&aabb).await.is_empty() + && args.world.get_players_at_box(&aabb).await.is_empty() + { + props.powered = false; + let state_id = props.to_state_id(args.block); + args.world + .set_block_state(args.position, state_id, BlockFlags::NOTIFY_ALL) + .await; + Self::update(args.world, args.position, state_id).await; + } else { + args.world + .schedule_block_tick(args.block, *args.position, 10, TickPriority::Normal) + .await; + } + }) + } + + fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if args.moved || Block::from_state_id(args.old_state_id) == args.block { + return; + } + let state_id = args.world.get_block_state_id(args.position).await; + Self::update(args.world, args.position, state_id).await; + }) } } diff --git a/pumpkin/src/block/blocks/redstone/tripwire_hook.rs b/pumpkin/src/block/blocks/redstone/tripwire_hook.rs index f6a39e552..9736e06bf 100644 --- a/pumpkin/src/block/blocks/redstone/tripwire_hook.rs +++ b/pumpkin/src/block/blocks/redstone/tripwire_hook.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use async_trait::async_trait; use pumpkin_data::{ Block, BlockDirection, block_properties::BlockProperties, @@ -17,7 +16,7 @@ use rand::{Rng, rng}; use crate::{ block::{ - BlockBehaviour, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, + BlockBehaviour, BlockFuture, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, OnStateReplacedArgs, PlayerPlacedArgs, }, @@ -30,106 +29,130 @@ type TripwireHookProperties = pumpkin_data::block_properties::TripwireHookLikePr #[pumpkin_block("minecraft:tripwire_hook")] pub struct TripwireHookBlock; -#[async_trait] impl BlockBehaviour for TripwireHookBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut props = TripwireHookProperties::default(args.block); - props.powered = false; - props.attached = false; - if Self::can_place_at(args.world, args.position, args.direction).await { - props.facing = args.direction.opposite().to_cardinal_direction(); - return props.to_state_id(args.block); - } - args.block.default_state.id + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut props = TripwireHookProperties::default(args.block); + props.powered = false; + props.attached = false; + if Self::can_place_at(args.world, args.position, args.direction).await { + props.facing = args.direction.opposite().to_cardinal_direction(); + return props.to_state_id(args.block); + } + args.block.default_state.id + }) } - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - Self::can_place_at(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 { + Self::can_place_at(args.block_accessor, args.position, args.direction).await + }) } - async fn player_placed(&self, args: PlayerPlacedArgs<'_>) { - Self::update( - args.world, - *args.position, - args.state_id, - false, - false, - -1, - None, - ) - .await; - } - - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - if args.direction.to_horizontal_facing().is_some_and(|facing| { - let props = TripwireHookProperties::from_state_id(args.state_id, args.block); - facing.opposite() == props.facing - }) && !Self::can_place_at(args.world, args.position, args.direction).await - { - Block::AIR.default_state.id - } else { - args.state_id - } - } - - async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { - let state_id = args.world.get_block_state_id(args.position).await; - Self::update(args.world, *args.position, state_id, false, true, -1, None).await; - } - - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - if args.moved || Block::from_state_id(args.old_state_id) == args.block { - return; - } - let props = TripwireHookProperties::from_state_id(args.old_state_id, args.block); - if props.powered || props.attached { + fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { Self::update( args.world, *args.position, - args.old_state_id, - true, + args.state_id, + false, false, -1, None, ) .await; - } - if props.powered { - args.world.update_neighbor(args.position, args.block).await; - args.world - .update_neighbor( - &args.position.offset(props.facing.opposite().to_offset()), - args.block, + }) + } + + fn get_state_for_neighbor_update<'a>( + &'a self, + args: GetStateForNeighborUpdateArgs<'a>, + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + if args.direction.to_horizontal_facing().is_some_and(|facing| { + let props = TripwireHookProperties::from_state_id(args.state_id, args.block); + facing.opposite() == props.facing + }) && !Self::can_place_at(args.world, args.position, args.direction).await + { + Block::AIR.default_state.id + } else { + args.state_id + } + }) + } + + 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; + Self::update(args.world, *args.position, state_id, false, true, -1, None).await; + }) + } + + fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if args.moved || Block::from_state_id(args.old_state_id) == args.block { + return; + } + let props = TripwireHookProperties::from_state_id(args.old_state_id, args.block); + if props.powered || props.attached { + Self::update( + args.world, + *args.position, + args.old_state_id, + true, + false, + -1, + None, ) .await; - } + } + if props.powered { + args.world.update_neighbor(args.position, args.block).await; + args.world + .update_neighbor( + &args.position.offset(props.facing.opposite().to_offset()), + args.block, + ) + .await; + } + }) } #[inline] - async fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { - true + fn emits_redstone_power<'a>( + &'a self, + _args: EmitsRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { true }) } - async fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - let props = TripwireHookProperties::from_state_id(args.state.id, args.block); - if props.powered { 15 } else { 0 } + fn get_weak_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { + let props = TripwireHookProperties::from_state_id(args.state.id, args.block); + if props.powered { 15 } else { 0 } + }) } - async fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 { - let props = TripwireHookProperties::from_state_id(args.state.id, args.block); - if props.powered - && args - .direction - .to_horizontal_facing() - .is_some_and(|facing| props.facing == facing) - { - 15 - } else { - 0 - } + fn get_strong_redstone_power<'a>( + &'a self, + args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { + let props = TripwireHookProperties::from_state_id(args.state.id, args.block); + if props.powered + && args + .direction + .to_horizontal_facing() + .is_some_and(|facing| props.facing == facing) + { + 15 + } else { + 0 + } + }) } } diff --git a/pumpkin/src/block/blocks/shulker_box.rs b/pumpkin/src/block/blocks/shulker_box.rs index 789cc5782..90b2a9695 100644 --- a/pumpkin/src/block/blocks/shulker_box.rs +++ b/pumpkin/src/block/blocks/shulker_box.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use crate::block::{BlockMetadata, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs}; +use crate::block::{BlockFuture, BlockMetadata, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs}; use crate::block::{ registry::BlockActionResult, {BlockBehaviour, NormalUseArgs}, @@ -51,37 +51,47 @@ impl BlockMetadata for ShulkerBoxBlock { type EndRodLikeProperties = pumpkin_data::block_properties::EndRodLikeProperties; -#[async_trait] impl BlockBehaviour for ShulkerBoxBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut props = EndRodLikeProperties::default(args.block); - props.facing = args.direction.to_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 = EndRodLikeProperties::default(args.block); + props.facing = args.direction.to_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 the Animation steps for now, because the client is responsible for that. - // TODO: Do not open the shulker box when it is currently closing - args.r#type == Self::OPEN_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 the Animation steps for now, because the client is responsible for that. + // TODO: Do not open the shulker box when it is currently closing + args.r#type == Self::OPEN_ANIMATION_EVENT_TYPE + }) } - async fn placed(&self, args: PlacedArgs<'_>) { - let barrel_block_entity = ShulkerBoxBlockEntity::new(*args.position); - args.world - .add_block_entity(Arc::new(barrel_block_entity)) - .await; - } - - 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(&ShulkerBoxScreenFactory(inventory)) + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let barrel_block_entity = ShulkerBoxBlockEntity::new(*args.position); + args.world + .add_block_entity(Arc::new(barrel_block_entity)) .await; - } + }) + } - BlockActionResult::Success + 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(&ShulkerBoxScreenFactory(inventory)) + .await; + } + + BlockActionResult::Success + }) } } diff --git a/pumpkin/src/block/blocks/signs.rs b/pumpkin/src/block/blocks/signs.rs index a8672e880..58ad71b90 100644 --- a/pumpkin/src/block/blocks/signs.rs +++ b/pumpkin/src/block/blocks/signs.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use std::sync::atomic::Ordering; -use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::EnumVariants; @@ -12,10 +11,12 @@ use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; +use pumpkin_world::BlockStateId; use pumpkin_world::block::entities::sign::SignBlockEntity; use uuid::Uuid; use crate::block::BlockBehaviour; +use crate::block::BlockFuture; use crate::block::NormalUseArgs; use crate::block::OnPlaceArgs; use crate::block::OnStateReplacedArgs; @@ -40,118 +41,132 @@ pub struct SignBlock; //TODO: Add support for Wall Signs //TODO: Add support for Hanging Signs //TODO: add support for click commands -#[async_trait] impl BlockBehaviour for SignBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> u16 { - let mut sign_props = SignProperties::default(args.block); - sign_props.waterlogged = args.replacing.water_source(); - sign_props.rotation = args.player.get_entity().get_flipped_rotation_16(); - sign_props.to_state_id(args.block) + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut sign_props = SignProperties::default(args.block); + sign_props.waterlogged = args.replacing.water_source(); + sign_props.rotation = args.player.get_entity().get_flipped_rotation_16(); + sign_props.to_state_id(args.block) + }) } - async fn placed(&self, args: PlacedArgs<'_>) { - args.world - .add_block_entity(Arc::new(SignBlockEntity::empty(*args.position))) - .await; - } - - async fn player_placed(&self, args: PlayerPlacedArgs<'_>) { - match &args.player.client { - crate::net::ClientPlatform::Java(java) => { - java.send_sign_packet(*args.position, true).await; - } - crate::net::ClientPlatform::Bedrock(_bedrock) => todo!(), - } - } - - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - args.world.remove_block_entity(args.position).await; - } - - async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - let Some(block_entity) = args.world.get_block_entity(args.position).await else { - return BlockActionResult::Pass; - }; - let Some(sign_entity) = block_entity.as_any().downcast_ref::() else { - return BlockActionResult::Pass; - }; - - if sign_entity.is_waxed.load(Ordering::Relaxed) { + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { args.world - .play_block_sound( - pumpkin_data::sound::Sound::BlockSignWaxedInteractFail, - pumpkin_data::sound::SoundCategory::Blocks, - *args.position, - ) + .add_block_entity(Arc::new(SignBlockEntity::empty(*args.position))) .await; - return BlockActionResult::SuccessServer; - } - - let mut currently_editing = sign_entity.currently_editing_player.lock().await; - if !try_claim_sign( - &mut currently_editing, - &args.player.gameprofile.id, - args.world, - args.position, - ) - .await - { - return BlockActionResult::Pass; - } - - let is_facing_front_text = - is_facing_front_text(args.world, args.position, args.block, args.player).await; - match &args.player.client { - ClientPlatform::Java(java) => { - java.send_sign_packet(*args.position, is_facing_front_text) - .await; - } - ClientPlatform::Bedrock(_bedrock) => todo!(), - } - - BlockActionResult::SuccessServer + }) } - async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { - let Some(block_entity) = args.world.get_block_entity(args.position).await else { - return BlockActionResult::Pass; - }; - let Some(sign_entity) = block_entity.as_any().downcast_ref::() else { - return BlockActionResult::Pass; - }; + fn player_placed<'a>(&'a self, args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + match &args.player.client { + crate::net::ClientPlatform::Java(java) => { + java.send_sign_packet(*args.position, true).await; + } + crate::net::ClientPlatform::Bedrock(_bedrock) => todo!(), + } + }) + } - if sign_entity.is_waxed.load(Ordering::Relaxed) { - return BlockActionResult::PassToDefaultBlockAction; - } + fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + args.world.remove_block_entity(args.position).await; + }) + } - let mut currently_editing = sign_entity.currently_editing_player.lock().await; - if !try_claim_sign( - &mut currently_editing, - &args.player.gameprofile.id, - args.world, - args.position, - ) - .await - { - // I don't think that makes sense, since it will also just return in normal_use, but vanilla does it like this - return BlockActionResult::PassToDefaultBlockAction; - } + fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { + Box::pin(async move { + let Some(block_entity) = args.world.get_block_entity(args.position).await else { + return BlockActionResult::Pass; + }; + let Some(sign_entity) = block_entity.as_any().downcast_ref::() else { + return BlockActionResult::Pass; + }; - let text = if is_facing_front_text(args.world, args.position, args.block, args.player).await - { - &sign_entity.front_text - } else { - &sign_entity.back_text - }; + if sign_entity.is_waxed.load(Ordering::Relaxed) { + args.world + .play_block_sound( + pumpkin_data::sound::Sound::BlockSignWaxedInteractFail, + pumpkin_data::sound::SoundCategory::Blocks, + *args.position, + ) + .await; + return BlockActionResult::SuccessServer; + } - let mut item = args.item_stack.lock().await; + let mut currently_editing = sign_entity.currently_editing_player.lock().await; + if !try_claim_sign( + &mut currently_editing, + &args.player.gameprofile.id, + args.world, + args.position, + ) + .await + { + return BlockActionResult::Pass; + } - let Some(pumpkin_item) = args.server.item_registry.get_pumpkin_item(item.item) else { - return BlockActionResult::PassToDefaultBlockAction; - }; + let is_facing_front_text = + is_facing_front_text(args.world, args.position, args.block, args.player).await; + match &args.player.client { + ClientPlatform::Java(java) => { + java.send_sign_packet(*args.position, is_facing_front_text) + .await; + } + ClientPlatform::Bedrock(_bedrock) => todo!(), + } - let result = - if let Some(honeycomb_item) = pumpkin_item.as_any().downcast_ref::() { + BlockActionResult::SuccessServer + }) + } + + fn use_with_item<'a>( + &'a self, + args: UseWithItemArgs<'a>, + ) -> BlockFuture<'a, BlockActionResult> { + Box::pin(async move { + let Some(block_entity) = args.world.get_block_entity(args.position).await else { + return BlockActionResult::Pass; + }; + let Some(sign_entity) = block_entity.as_any().downcast_ref::() else { + return BlockActionResult::Pass; + }; + + if sign_entity.is_waxed.load(Ordering::Relaxed) { + return BlockActionResult::PassToDefaultBlockAction; + } + + let mut currently_editing = sign_entity.currently_editing_player.lock().await; + if !try_claim_sign( + &mut currently_editing, + &args.player.gameprofile.id, + args.world, + args.position, + ) + .await + { + // I don't think that makes sense, since it will also just return in normal_use, but vanilla does it like this + return BlockActionResult::PassToDefaultBlockAction; + } + + let text = + if is_facing_front_text(args.world, args.position, args.block, args.player).await { + &sign_entity.front_text + } else { + &sign_entity.back_text + }; + + let mut item = args.item_stack.lock().await; + + let Some(pumpkin_item) = args.server.item_registry.get_pumpkin_item(item.item) else { + return BlockActionResult::PassToDefaultBlockAction; + }; + + let result = if let Some(honeycomb_item) = + pumpkin_item.as_any().downcast_ref::() + { honeycomb_item .apply_to_sign(&args, &block_entity, sign_entity) .await @@ -171,14 +186,15 @@ impl BlockBehaviour for SignBlock { BlockActionResult::PassToDefaultBlockAction }; - if result == BlockActionResult::Success { - if !args.player.has_infinite_materials() { - item.decrement(1); + if result == BlockActionResult::Success { + if !args.player.has_infinite_materials() { + item.decrement(1); + } + *currently_editing = None; } - *currently_editing = None; - } - result + result + }) } } diff --git a/pumpkin/src/block/blocks/skull_block.rs b/pumpkin/src/block/blocks/skull_block.rs index 950ed0c73..08b22193f 100644 --- a/pumpkin/src/block/blocks/skull_block.rs +++ b/pumpkin/src/block/blocks/skull_block.rs @@ -1,7 +1,6 @@ use crate::block::blocks::redstone::block_receives_redstone_power; -use crate::block::{BlockBehaviour, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs}; +use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs}; use crate::entity::EntityBase; -use async_trait::async_trait; use pumpkin_data::block_properties::BlockProperties; use pumpkin_world::BlockStateId; use pumpkin_world::world::BlockFlags; @@ -28,28 +27,31 @@ impl BlockMetadata for SkullBlock { } } -#[async_trait] impl BlockBehaviour for SkullBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut props = SkeletonSkullLikeProperties::default(args.block); - props.rotation = args.player.get_entity().get_rotation_16(); - props.powered = block_receives_redstone_power(args.world, args.position).await; - 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 = SkeletonSkullLikeProperties::default(args.block); + props.rotation = args.player.get_entity().get_rotation_16(); + props.powered = block_receives_redstone_power(args.world, args.position).await; + props.to_state_id(args.block) + }) } - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - let state = args.world.get_block_state(args.position).await; - let mut props = SkeletonSkullLikeProperties::from_state_id(state.id, args.block); - let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; - if props.powered != is_receiving_power { - props.powered = is_receiving_power; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; - } + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let state = args.world.get_block_state(args.position).await; + let mut props = SkeletonSkullLikeProperties::from_state_id(state.id, args.block); + let is_receiving_power = block_receives_redstone_power(args.world, args.position).await; + if props.powered != is_receiving_power { + props.powered = is_receiving_power; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ) + .await; + } + }) } } diff --git a/pumpkin/src/block/blocks/slabs.rs b/pumpkin/src/block/blocks/slabs.rs index 3dcaeab08..12863a664 100644 --- a/pumpkin/src/block/blocks/slabs.rs +++ b/pumpkin/src/block/blocks/slabs.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::BlockDirection; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::SlabType; @@ -8,6 +7,7 @@ use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_world::BlockStateId; use crate::block::BlockBehaviour; +use crate::block::BlockFuture; use crate::block::BlockIsReplacing; use crate::block::CanUpdateAtArgs; use crate::block::OnPlaceArgs; @@ -17,41 +17,44 @@ type SlabProperties = pumpkin_data::block_properties::ResinBrickSlabLikeProperti #[pumpkin_block_from_tag("minecraft:slabs")] pub struct SlabBlock; -#[async_trait] impl BlockBehaviour for SlabBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - if let BlockIsReplacing::Itself(state_id) = args.replacing { - let mut slab_props = SlabProperties::from_state_id(state_id, args.block); - slab_props.r#type = SlabType::Double; - slab_props.waterlogged = false; - return slab_props.to_state_id(args.block); - } + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + if let BlockIsReplacing::Itself(state_id) = args.replacing { + let mut slab_props = SlabProperties::from_state_id(state_id, args.block); + slab_props.r#type = SlabType::Double; + slab_props.waterlogged = false; + return slab_props.to_state_id(args.block); + } - let mut slab_props = SlabProperties::default(args.block); - slab_props.waterlogged = args.replacing.water_source(); - slab_props.r#type = match args.direction { - BlockDirection::Up => SlabType::Top, - BlockDirection::Down => SlabType::Bottom, - _ => match args.use_item_on.cursor_pos.y { - 0.0...0.5 => SlabType::Bottom, - _ => SlabType::Top, - }, - }; + let mut slab_props = SlabProperties::default(args.block); + slab_props.waterlogged = args.replacing.water_source(); + slab_props.r#type = match args.direction { + BlockDirection::Up => SlabType::Top, + BlockDirection::Down => SlabType::Bottom, + _ => match args.use_item_on.cursor_pos.y { + 0.0..0.5 => SlabType::Bottom, + _ => SlabType::Top, + }, + }; - slab_props.to_state_id(args.block) + slab_props.to_state_id(args.block) + }) } - async fn can_update_at(&self, args: CanUpdateAtArgs<'_>) -> bool { - let slab_props = SlabProperties::from_state_id(args.state_id, args.block); + fn can_update_at<'a>(&'a self, args: CanUpdateAtArgs<'a>) -> BlockFuture<'a, bool> { + Box::pin(async move { + let slab_props = SlabProperties::from_state_id(args.state_id, args.block); - slab_props.r#type - == match args.direction { - BlockDirection::Up => SlabType::Bottom, - BlockDirection::Down => SlabType::Top, - _ => match args.use_item_on.cursor_pos.y { - 0.0...0.5 => SlabType::Top, - _ => SlabType::Bottom, - }, - } + slab_props.r#type + == match args.direction { + BlockDirection::Up => SlabType::Bottom, + BlockDirection::Down => SlabType::Top, + _ => match args.use_item_on.cursor_pos.y { + 0.0..0.5 => SlabType::Top, + _ => SlabType::Bottom, + }, + } + }) } } diff --git a/pumpkin/src/block/blocks/smoker.rs b/pumpkin/src/block/blocks/smoker.rs index 3374fd666..0a1c11c5f 100644 --- a/pumpkin/src/block/blocks/smoker.rs +++ b/pumpkin/src/block/blocks/smoker.rs @@ -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:smoker")] pub struct SmokerBlock; -#[async_trait] impl BlockBehaviour for SmokerBlock { - 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) + }) } } diff --git a/pumpkin/src/block/blocks/spawner.rs b/pumpkin/src/block/blocks/spawner.rs index 42900bbdd..81b3804d6 100644 --- a/pumpkin/src/block/blocks/spawner.rs +++ b/pumpkin/src/block/blocks/spawner.rs @@ -1,20 +1,20 @@ use std::sync::Arc; -use async_trait::async_trait; use pumpkin_macros::pumpkin_block; use pumpkin_world::block::entities::mob_spawner::MobSpawnerBlockEntity; -use crate::block::{BlockBehaviour, PlacedArgs}; +use crate::block::{BlockBehaviour, BlockFuture, PlacedArgs}; #[pumpkin_block("minecraft:spawner")] pub struct SpawnerBlock; -#[async_trait] impl BlockBehaviour for SpawnerBlock { - async fn placed(&self, args: PlacedArgs<'_>) { - let hopper_block_entity = MobSpawnerBlockEntity::new(*args.position); - args.world - .add_block_entity(Arc::new(hopper_block_entity)) - .await; + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let hopper_block_entity = MobSpawnerBlockEntity::new(*args.position); + args.world + .add_block_entity(Arc::new(hopper_block_entity)) + .await; + }) } } diff --git a/pumpkin/src/block/blocks/stairs.rs b/pumpkin/src/block/blocks/stairs.rs index 8b8a06fbc..285a8b6cd 100644 --- a/pumpkin/src/block/blocks/stairs.rs +++ b/pumpkin/src/block/blocks/stairs.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::block_properties::BlockHalf; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::HorizontalFacing; @@ -13,6 +12,7 @@ use pumpkin_world::BlockStateId; use pumpkin_world::world::BlockFlags; use crate::block::BlockBehaviour; +use crate::block::BlockFuture; use crate::block::OnNeighborUpdateArgs; use crate::block::OnPlaceArgs; use crate::world::World; @@ -22,59 +22,62 @@ type StairsProperties = pumpkin_data::block_properties::OakStairsLikeProperties; #[pumpkin_block_from_tag("minecraft:stairs")] pub struct StairBlock; -#[async_trait] impl BlockBehaviour for StairBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut stair_props = StairsProperties::default(args.block); - stair_props.waterlogged = args.replacing.water_source(); + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut stair_props = StairsProperties::default(args.block); + stair_props.waterlogged = args.replacing.water_source(); - stair_props.facing = args.player.living_entity.entity.get_horizontal_facing(); - stair_props.half = match args.direction { - BlockDirection::Up => BlockHalf::Top, - BlockDirection::Down => BlockHalf::Bottom, - _ => match args.use_item_on.cursor_pos.y { - 0.0...0.5 => BlockHalf::Bottom, - 0.5...1.0 => BlockHalf::Top, + stair_props.facing = args.player.living_entity.entity.get_horizontal_facing(); + stair_props.half = match args.direction { + BlockDirection::Up => BlockHalf::Top, + BlockDirection::Down => BlockHalf::Bottom, + _ => match args.use_item_on.cursor_pos.y { + 0.0..0.5 => BlockHalf::Bottom, + 0.5..1.0 => BlockHalf::Top, - // This cannot happen normally - #[allow(clippy::match_same_arms)] - _ => BlockHalf::Bottom, - }, - }; + // This cannot happen normally + #[allow(clippy::match_same_arms)] + _ => BlockHalf::Bottom, + }, + }; - stair_props.shape = compute_stair_shape( - args.world, - args.position, - stair_props.facing, - stair_props.half, - ) - .await; + stair_props.shape = compute_stair_shape( + args.world, + args.position, + stair_props.facing, + stair_props.half, + ) + .await; - stair_props.to_state_id(args.block) + stair_props.to_state_id(args.block) + }) } - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - let state_id = args.world.get_block_state_id(args.position).await; - let mut stair_props = StairsProperties::from_state_id(state_id, args.block); + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let state_id = args.world.get_block_state_id(args.position).await; + let mut stair_props = StairsProperties::from_state_id(state_id, args.block); - let new_shape = compute_stair_shape( - args.world, - args.position, - stair_props.facing, - stair_props.half, - ) - .await; + let new_shape = compute_stair_shape( + args.world, + args.position, + stair_props.facing, + stair_props.half, + ) + .await; - if stair_props.shape != new_shape { - stair_props.shape = new_shape; - args.world - .set_block_state( - args.position, - stair_props.to_state_id(args.block), - BlockFlags::NOTIFY_ALL, - ) - .await; - } + if stair_props.shape != new_shape { + stair_props.shape = new_shape; + args.world + .set_block_state( + args.position, + stair_props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ) + .await; + } + }) } } diff --git a/pumpkin/src/block/blocks/sugar_cane.rs b/pumpkin/src/block/blocks/sugar_cane.rs index 9d4b46c8d..6447eb544 100644 --- a/pumpkin/src/block/blocks/sugar_cane.rs +++ b/pumpkin/src/block/blocks/sugar_cane.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::block_properties::HorizontalFacing; use pumpkin_data::tag::Taggable; use pumpkin_data::{ @@ -13,75 +12,80 @@ use pumpkin_world::tick::TickPriority; use pumpkin_world::world::{BlockAccessor, BlockFlags}; use crate::block::{ - BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnScheduledTickArgs, - RandomTickArgs, + BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, + OnScheduledTickArgs, RandomTickArgs, }; #[pumpkin_block("minecraft:sugar_cane")] pub struct SugarCaneBlock; -#[async_trait] impl BlockBehaviour for SugarCaneBlock { - 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(), state_id, BlockFlags::empty()) - .await; - let props = CactusLikeProperties { - age: Integer0To15::L0, - }; - args.world - .set_block_state( - args.position, - props.to_state_id(args.block), - 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 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 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(), state_id, BlockFlags::empty()) + .await; + let props = CactusLikeProperties { + age: Integer0To15::L0, + }; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + 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 can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - can_place_at(args.block_accessor, args.position).await + fn get_state_for_neighbor_update<'a>( + &'a self, + args: GetStateForNeighborUpdateArgs<'a>, + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + 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 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 }) } } diff --git a/pumpkin/src/block/blocks/tnt.rs b/pumpkin/src/block/blocks/tnt.rs index f00e2c82a..2cc17f8b1 100644 --- a/pumpkin/src/block/blocks/tnt.rs +++ b/pumpkin/src/block/blocks/tnt.rs @@ -3,12 +3,11 @@ use std::sync::Arc; use crate::block::registry::BlockActionResult; use crate::block::{ - BlockBehaviour, ExplodeArgs, OnNeighborUpdateArgs, PlacedArgs, UseWithItemArgs, + BlockBehaviour, BlockFuture, ExplodeArgs, OnNeighborUpdateArgs, PlacedArgs, UseWithItemArgs, }; use crate::entity::Entity; use crate::entity::tnt::TNTEntity; use crate::world::World; -use async_trait::async_trait; use pumpkin_data::entity::EntityType; use pumpkin_data::sound::SoundCategory; use pumpkin_macros::pumpkin_block; @@ -51,46 +50,56 @@ impl TNTBlock { const DEFAULT_FUSE: u32 = 80; const DEFAULT_POWER: f32 = 4.0; -#[async_trait] impl BlockBehaviour for TNTBlock { - async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { - let item = args.item_stack.lock().await.item; - if item != &Item::FLINT_AND_STEEL || item == &Item::FIRE_CHARGE { - return BlockActionResult::Pass; - } - let world = args.player.world(); - Self::prime(world, args.position).await; + 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; + if item != &Item::FLINT_AND_STEEL || item == &Item::FIRE_CHARGE { + return BlockActionResult::Pass; + } + let world = args.player.world(); + Self::prime(world, args.position).await; - BlockActionResult::Consume + BlockActionResult::Consume + }) } - async fn placed(&self, args: PlacedArgs<'_>) { - if block_receives_redstone_power(args.world, args.position).await { - Self::prime(args.world, args.position).await; - } + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if block_receives_redstone_power(args.world, args.position).await { + Self::prime(args.world, args.position).await; + } + }) } - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - if block_receives_redstone_power(args.world, args.position).await { - Self::prime(args.world, args.position).await; - } + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if block_receives_redstone_power(args.world, args.position).await { + Self::prime(args.world, args.position).await; + } + }) } - async fn explode(&self, args: ExplodeArgs<'_>) { - let entity = Entity::new( - Uuid::new_v4(), - args.world.clone(), - args.position.to_f64(), - &EntityType::TNT, - false, - ); - let angle = rand::random::() * std::f64::consts::TAU; - entity - .set_velocity(Vector3::new(-angle.sin() * 0.02, 0.2, -angle.cos() * 0.02)) - .await; - let fuse = rand::rng().random_range(0..DEFAULT_FUSE / 4) + DEFAULT_FUSE / 8; - let tnt = Arc::new(TNTEntity::new(entity, DEFAULT_POWER, fuse)); - args.world.spawn_entity(tnt).await; + fn explode<'a>(&'a self, args: ExplodeArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let entity = Entity::new( + Uuid::new_v4(), + args.world.clone(), + args.position.to_f64(), + &EntityType::TNT, + false, + ); + let angle = rand::random::() * std::f64::consts::TAU; + entity + .set_velocity(Vector3::new(-angle.sin() * 0.02, 0.2, -angle.cos() * 0.02)) + .await; + let fuse = rand::rng().random_range(0..DEFAULT_FUSE / 4) + DEFAULT_FUSE / 8; + let tnt = Arc::new(TNTEntity::new(entity, DEFAULT_POWER, fuse)); + args.world.spawn_entity(tnt).await; + }) } fn should_drop_items_on_explosion(&self) -> bool { diff --git a/pumpkin/src/block/blocks/torches.rs b/pumpkin/src/block/blocks/torches.rs index df2dc2e72..99eeff72e 100644 --- a/pumpkin/src/block/blocks/torches.rs +++ b/pumpkin/src/block/blocks/torches.rs @@ -1,6 +1,5 @@ -use crate::block::BlockIsReplacing; +use crate::block::{BlockFuture, BlockIsReplacing}; use crate::entity::EntityBase; -use async_trait::async_trait; use pumpkin_data::BlockDirection; use pumpkin_data::block_properties::{BlockProperties, Facing}; use pumpkin_data::{Block, FacingExt, HorizontalFacingExt}; @@ -32,97 +31,103 @@ impl BlockMetadata for TorchBlock { } } -#[async_trait] impl BlockBehaviour for TorchBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - if args.direction == BlockDirection::Down { + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + if args.direction == BlockDirection::Down { + let support_block = args.world.get_block_state(&args.position.down()).await; + if support_block.is_center_solid(BlockDirection::Up) { + return args.block.default_state.id; + } + } + let mut directions = args.player.get_entity().get_entity_facing_order(); + + if args.replacing == BlockIsReplacing::None { + let face = args.direction.to_facing(); + let mut i = 0; + while i < directions.len() && directions[i] != face { + i += 1; + } + + if i > 0 { + directions.copy_within(0..i, 1); + directions[0] = face; + } + } else if directions[0] == Facing::Down { + let support_block = args.world.get_block_state(&args.position.down()).await; + if support_block.is_center_solid(BlockDirection::Up) { + return args.block.default_state.id; + } + } + + for dir in directions { + if dir != Facing::Up + && dir != Facing::Down + && can_place_at(args.world, args.position, dir.to_block_direction()).await + { + let wall_block = if args.block == &Block::TORCH { + Block::WALL_TORCH + } else { + Block::SOUL_WALL_TORCH + }; + let mut torch_props = WallTorchProps::default(&wall_block); + torch_props.facing = dir + .opposite() + .to_block_direction() + .to_horizontal_facing() + .unwrap(); + return torch_props.to_state_id(&wall_block); + } + } + let support_block = args.world.get_block_state(&args.position.down()).await; if support_block.is_center_solid(BlockDirection::Up) { - return args.block.default_state.id; + args.block.default_state.id + } else { + 0 } - } - let mut directions = args.player.get_entity().get_entity_facing_order(); - - if args.replacing == BlockIsReplacing::None { - let face = args.direction.to_facing(); - let mut i = 0; - while i < directions.len() && directions[i] != face { - i += 1; - } - - if i > 0 { - directions.copy_within(0..i, 1); - directions[0] = face; - } - } else if directions[0] == Facing::Down { - let support_block = args.world.get_block_state(&args.position.down()).await; - if support_block.is_center_solid(BlockDirection::Up) { - return args.block.default_state.id; - } - } - - for dir in directions { - if dir != Facing::Up - && dir != Facing::Down - && can_place_at(args.world, args.position, dir.to_block_direction()).await - { - let wall_block = if args.block == &Block::TORCH { - Block::WALL_TORCH - } else { - Block::SOUL_WALL_TORCH - }; - let mut torch_props = WallTorchProps::default(&wall_block); - torch_props.facing = dir - .opposite() - .to_block_direction() - .to_horizontal_facing() - .unwrap(); - return torch_props.to_state_id(&wall_block); - } - } - - let support_block = args.world.get_block_state(&args.position.down()).await; - if support_block.is_center_solid(BlockDirection::Up) { - args.block.default_state.id - } else { - 0 - } + }) } - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - let support_block = args - .block_accessor - .get_block_state(&args.position.down()) - .await; - if support_block.is_center_solid(BlockDirection::Up) { - return true; - } - for dir in BlockDirection::horizontal() { - if can_place_at(args.block_accessor, args.position, dir).await { + fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> { + Box::pin(async move { + let support_block = args + .block_accessor + .get_block_state(&args.position.down()) + .await; + if support_block.is_center_solid(BlockDirection::Up) { return true; } - } - false + for dir in BlockDirection::horizontal() { + if can_place_at(args.block_accessor, args.position, dir).await { + return true; + } + } + false + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - if args.block == &Block::WALL_TORCH || args.block == &Block::SOUL_WALL_TORCH { - let props = WallTorchProps::from_state_id(args.state_id, args.block); - if props.facing.to_block_direction().opposite() == args.direction - && !can_place_at(args.world, args.position, props.facing.to_block_direction()).await - { - return 0; + fn get_state_for_neighbor_update<'a>( + &'a self, + args: GetStateForNeighborUpdateArgs<'a>, + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + if args.block == &Block::WALL_TORCH || args.block == &Block::SOUL_WALL_TORCH { + let props = WallTorchProps::from_state_id(args.state_id, args.block); + if props.facing.to_block_direction().opposite() == args.direction + && !can_place_at(args.world, args.position, props.facing.to_block_direction()) + .await + { + return 0; + } + } else if args.direction == BlockDirection::Down { + let support_block = args.world.get_block_state(&args.position.down()).await; + if !support_block.is_center_solid(BlockDirection::Up) { + return 0; + } } - } else if args.direction == BlockDirection::Down { - let support_block = args.world.get_block_state(&args.position.down()).await; - if !support_block.is_center_solid(BlockDirection::Up) { - return 0; - } - } - args.state_id + args.state_id + }) } } diff --git a/pumpkin/src/block/blocks/trapdoor.rs b/pumpkin/src/block/blocks/trapdoor.rs index 8c9dd61e4..c5be21e03 100644 --- a/pumpkin/src/block/blocks/trapdoor.rs +++ b/pumpkin/src/block/blocks/trapdoor.rs @@ -1,9 +1,8 @@ use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::registry::BlockActionResult; -use crate::block::{BlockBehaviour, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs}; +use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs}; use crate::entity::player::Player; use crate::world::World; -use async_trait::async_trait; use pumpkin_data::BlockDirection; use pumpkin_data::block_properties::{BlockHalf, BlockProperties}; use pumpkin_data::sound::{Sound, SoundCategory}; @@ -69,71 +68,76 @@ fn get_sound(block: &Block, open: bool) -> Sound { #[pumpkin_block_from_tag("minecraft:trapdoors")] pub struct TrapDoorBlock; -#[async_trait] impl BlockBehaviour for TrapDoorBlock { - async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { - if !can_open_trapdoor(args.block) { - return BlockActionResult::Pass; - } - - toggle_trapdoor(args.player, args.world, args.position).await; - - BlockActionResult::Success - } - - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut trapdoor_props = TrapDoorProperties::default(args.block); - trapdoor_props.waterlogged = args.replacing.water_source(); - - let powered = block_receives_redstone_power(args.world, args.position).await; - let direction = args - .player - .living_entity - .entity - .get_horizontal_facing() - .opposite(); - - trapdoor_props.facing = direction; - trapdoor_props.half = match args.direction { - BlockDirection::Up => BlockHalf::Top, - BlockDirection::Down => BlockHalf::Bottom, - _ => match args.use_item_on.cursor_pos.y { - 0.0...0.5 => BlockHalf::Bottom, - _ => BlockHalf::Top, - }, - }; - trapdoor_props.powered = powered; - trapdoor_props.open = powered; - - trapdoor_props.to_state_id(args.block) - } - - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { - let block_state = args.world.get_block_state(args.position).await; - let mut trapdoor_props = TrapDoorProperties::from_state_id(block_state.id, args.block); - let powered = block_receives_redstone_power(args.world, args.position).await; - if powered != trapdoor_props.powered { - trapdoor_props.powered = !trapdoor_props.powered; - - if powered != trapdoor_props.open { - trapdoor_props.open = trapdoor_props.powered; - - args.world - .play_block_sound( - get_sound(args.block, powered), - SoundCategory::Blocks, - *args.position, - ) - .await; + fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { + Box::pin(async move { + if !can_open_trapdoor(args.block) { + return BlockActionResult::Pass; } - } - args.world - .set_block_state( - args.position, - trapdoor_props.to_state_id(args.block), - BlockFlags::NOTIFY_LISTENERS, - ) - .await; + toggle_trapdoor(args.player, args.world, args.position).await; + + BlockActionResult::Success + }) + } + + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut trapdoor_props = TrapDoorProperties::default(args.block); + trapdoor_props.waterlogged = args.replacing.water_source(); + + let powered = block_receives_redstone_power(args.world, args.position).await; + let direction = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + + trapdoor_props.facing = direction; + trapdoor_props.half = match args.direction { + BlockDirection::Up => BlockHalf::Top, + BlockDirection::Down => BlockHalf::Bottom, + _ => match args.use_item_on.cursor_pos.y { + 0.0..0.5 => BlockHalf::Bottom, + _ => BlockHalf::Top, + }, + }; + trapdoor_props.powered = powered; + trapdoor_props.open = powered; + + trapdoor_props.to_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 trapdoor_props = TrapDoorProperties::from_state_id(block_state.id, args.block); + let powered = block_receives_redstone_power(args.world, args.position).await; + if powered != trapdoor_props.powered { + trapdoor_props.powered = !trapdoor_props.powered; + + if powered != trapdoor_props.open { + trapdoor_props.open = trapdoor_props.powered; + + args.world + .play_block_sound( + get_sound(args.block, powered), + SoundCategory::Blocks, + *args.position, + ) + .await; + } + } + + args.world + .set_block_state( + args.position, + trapdoor_props.to_state_id(args.block), + BlockFlags::NOTIFY_LISTENERS, + ) + .await; + }) } } diff --git a/pumpkin/src/block/blocks/vine.rs b/pumpkin/src/block/blocks/vine.rs index a8746ad1e..2fde3c126 100644 --- a/pumpkin/src/block/blocks/vine.rs +++ b/pumpkin/src/block/blocks/vine.rs @@ -1,26 +1,26 @@ -use async_trait::async_trait; use pumpkin_data::BlockDirection; use pumpkin_macros::pumpkin_block; -use crate::block::{BlockBehaviour, CanPlaceAtArgs}; +use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs}; #[pumpkin_block("minecraft:vine")] pub struct VineBlock; -#[async_trait] impl BlockBehaviour for VineBlock { - async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { - // TODO: This is bad and not vanilla, just a "hotfix" - for dir in BlockDirection::all() { - if !args - .block_accessor - .get_block_state(&args.position.offset(dir.to_offset())) - .await - .is_air() - { - return true; + fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> { + Box::pin(async move { + // TODO: This is bad and not vanilla, just a "hotfix" + for dir in BlockDirection::all() { + if !args + .block_accessor + .get_block_state(&args.position.offset(dir.to_offset())) + .await + .is_air() + { + return true; + } } - } - false + false + }) } } diff --git a/pumpkin/src/block/blocks/walls.rs b/pumpkin/src/block/blocks/walls.rs index 8e890bddd..0a6b54fef 100644 --- a/pumpkin/src/block/blocks/walls.rs +++ b/pumpkin/src/block/blocks/walls.rs @@ -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; @@ -26,21 +26,24 @@ type WallProperties = pumpkin_data::block_properties::ResinBrickWallLikeProperti #[pumpkin_block_from_tag("minecraft:walls")] pub struct WallBlock; -#[async_trait] impl BlockBehaviour for WallBlock { - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - let mut wall_props = WallProperties::default(args.block); - wall_props.waterlogged = args.replacing.water_source(); + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut wall_props = WallProperties::default(args.block); + wall_props.waterlogged = args.replacing.water_source(); - compute_wall_state(wall_props, args.world, args.block, args.position).await + compute_wall_state(wall_props, args.world, args.block, args.position).await + }) } - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - let wall_props = WallProperties::from_state_id(args.state_id, args.block); - compute_wall_state(wall_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 wall_props = WallProperties::from_state_id(args.state_id, args.block); + compute_wall_state(wall_props, args.world, args.block, args.position).await + }) } } diff --git a/pumpkin/src/block/fluid/flowing.rs b/pumpkin/src/block/fluid/flowing.rs index 129033be9..0d2137fd4 100644 --- a/pumpkin/src/block/fluid/flowing.rs +++ b/pumpkin/src/block/fluid/flowing.rs @@ -1,7 +1,6 @@ -use std::collections::HashMap; use std::sync::Arc; +use std::{collections::HashMap, pin::Pin}; -use async_trait::async_trait; use pumpkin_data::{ Block, BlockDirection, fluid::{EnumVariants, Falling, Fluid, FluidProperties, Level}, @@ -9,7 +8,7 @@ use pumpkin_data::{ use pumpkin_util::math::position::BlockPos; use pumpkin_world::{BlockId, BlockStateId, world::BlockFlags}; -use crate::world::World; +use crate::{block::BlockFuture, world::World}; type FlowingFluidProperties = pumpkin_data::fluid::FlowingWaterLikeFluidProperties; #[derive(Clone)] @@ -51,55 +50,70 @@ impl SpreadContext { } } -#[async_trait] -pub trait FlowingFluid { - async fn get_drop_off(&self) -> i32; +pub type FluidFuture<'a, T> = Pin + Send + 'a>>; - async fn get_source(&self, fluid: &Fluid, falling: bool) -> FlowingFluidProperties { - let mut source_props = FlowingFluidProperties::default(fluid); - source_props.level = Level::L8; - source_props.falling = if falling { - Falling::True - } else { - Falling::False - }; - source_props +pub trait FlowingFluid: Send + Sync { + fn get_drop_off(&self) -> i32; + + fn get_source<'a>( + &'a self, + fluid: &'a Fluid, + falling: bool, + ) -> FluidFuture<'a, FlowingFluidProperties> { + Box::pin(async move { + let mut source_props = FlowingFluidProperties::default(fluid); + source_props.level = Level::L8; + source_props.falling = if falling { + Falling::True + } else { + Falling::False + }; + source_props + }) } - async fn get_flowing( - &self, - fluid: &Fluid, + fn get_flowing<'a>( + &'a self, + fluid: &'a Fluid, level: Level, falling: bool, - ) -> FlowingFluidProperties { - let mut flowing_props = FlowingFluidProperties::default(fluid); - flowing_props.level = level; - flowing_props.falling = if falling { - Falling::True - } else { - Falling::False - }; - flowing_props + ) -> FluidFuture<'a, FlowingFluidProperties> { + Box::pin(async move { + let mut flowing_props = FlowingFluidProperties::default(fluid); + flowing_props.level = level; + flowing_props.falling = if falling { + Falling::True + } else { + Falling::False + }; + flowing_props + }) } - async fn get_slope_find_distance(&self) -> i32; + fn get_slope_find_distance(&self) -> i32; - async fn can_convert_to_source(&self, world: &Arc) -> bool; + fn can_convert_to_source(&self, world: &Arc) -> bool; - async fn is_waterlogged(&self, world: &Arc, pos: &BlockPos) -> Option { - let block = world.get_block(pos).await; + fn is_waterlogged<'a>( + &'a self, + world: &'a Arc, + pos: &'a BlockPos, + ) -> FluidFuture<'a, Option> { + Box::pin(async move { + let block = world.get_block(pos).await; - let state_id = world.get_block_state_id(pos).await; - // Check if the block has waterlogged property and if it's true - if let Some(properties) = block.properties(state_id) - && properties - .to_props() - .iter() - .any(|(key, value)| *key == "waterlogged" && *value == "true") - { - return Some(state_id); - } - None + let state_id = world.get_block_state_id(pos).await; + // Check if the block has waterlogged property and if it's true + if let Some(properties) = block.properties(state_id) + && properties + .to_props() + .iter() + .any(|(key, value)| *key == "waterlogged" && *value == "true") + { + return Some(state_id); + } + None + }) } fn is_same_fluid(&self, fluid: &Fluid, other_state_id: BlockStateId) -> bool { @@ -109,379 +123,433 @@ pub trait FlowingFluid { false } - async fn spread_fluid(&self, world: &Arc, fluid: &Fluid, block_pos: &BlockPos) { - let block_state_id = world.get_block_state_id(block_pos).await; - if let Some(new_fluid_state) = self.get_new_liquid(world, fluid, block_pos).await { - if new_fluid_state.to_state_id(fluid) != block_state_id - && self.is_waterlogged(world, block_pos).await.is_none() - { + fn spread_fluid<'a>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, + block_pos: &'a BlockPos, + ) -> FluidFuture<'a, ()> { + Box::pin(async move { + let block_state_id = world.get_block_state_id(block_pos).await; + if let Some(new_fluid_state) = self.get_new_liquid(world, fluid, block_pos).await { + if new_fluid_state.to_state_id(fluid) != block_state_id + && self.is_waterlogged(world, block_pos).await.is_none() + { + world + .set_block_state( + block_pos, + new_fluid_state.to_state_id(fluid), + BlockFlags::NOTIFY_ALL, + ) + .await; + } + self.spread(world, fluid, block_pos, &new_fluid_state).await; + } else if self.is_waterlogged(world, block_pos).await.is_some() { + self.spread( + world, + fluid, + block_pos, + &FlowingFluidProperties::default(fluid), + ) + .await; + } else { + world + .break_block(block_pos, None, BlockFlags::NOTIFY_NEIGHBORS) + .await; world .set_block_state( block_pos, - new_fluid_state.to_state_id(fluid), + Block::AIR.default_state.id, BlockFlags::NOTIFY_ALL, ) .await; } - self.spread(world, fluid, block_pos, &new_fluid_state).await; - } else if self.is_waterlogged(world, block_pos).await.is_some() { - self.spread( - world, - fluid, - block_pos, - &FlowingFluidProperties::default(fluid), - ) - .await; - } else { - world - .break_block(block_pos, None, BlockFlags::NOTIFY_NEIGHBORS) - .await; - world - .set_block_state( - block_pos, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - } + }) } - async fn spread( - &self, - world: &Arc, - fluid: &Fluid, - block_pos: &BlockPos, - props: &FlowingFluidProperties, - ) { - let below_pos = block_pos.down(); - let below_can_replace = !self.is_solid_or_source(world, &below_pos, 0, fluid).await; - - if below_can_replace { - let mut new_props = FlowingFluidProperties::default(fluid); - new_props.level = Level::L8; - new_props.falling = Falling::True; - - self.spread_to(world, fluid, &below_pos, new_props.to_state_id(fluid)) - .await; - } else if props.level == Level::L8 && props.falling == Falling::False - || !self - .is_water_hole(world, fluid, block_pos, &below_pos) - .await - { - self.spread_to_sides(world, fluid, block_pos).await; - } - } - - async fn get_new_liquid( - &self, - world: &Arc, - fluid: &Fluid, - block_pos: &BlockPos, - ) -> Option { - let current_state_id = world.get_block_state_id(block_pos).await; - - let current_props = FlowingFluidProperties::from_state_id(current_state_id, fluid); - let current_level = i32::from(current_props.level.to_index()) + 1; - if current_level == 8 && current_props.falling != Falling::True { - return Some(current_props); - } - let mut highest_level = 0; - let mut source_count = 0; - - for direction in BlockDirection::horizontal() { - let neighbor_pos = block_pos.offset(direction.to_offset()); - let neighbor_state_id = world.get_block_state_id(&neighbor_pos).await; - - if fluid.default_state_index == Fluid::WATER.default_state_index - && self.is_waterlogged(world, &neighbor_pos).await.is_some() - { - source_count += 1; - highest_level = highest_level.max(8); - continue; - } - - if !self.is_same_fluid(fluid, neighbor_state_id) { - continue; - } - - let neighbor_props = FlowingFluidProperties::from_state_id(neighbor_state_id, fluid); - let neighbor_level = i32::from(neighbor_props.level.to_index()) + 1; - - if neighbor_level == 8 && neighbor_props.falling != Falling::True { - source_count += 1; - } - - highest_level = highest_level.max(neighbor_level); - } - - if source_count >= 2 && self.can_convert_to_source(world).await { + fn spread<'a>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, + block_pos: &'a BlockPos, + props: &'a FlowingFluidProperties, + ) -> FluidFuture<'a, ()> { + Box::pin(async move { let below_pos = block_pos.down(); - let below_state_id = world.get_block_state_id(&below_pos).await; - if self - .is_solid_or_source(world, &below_pos, below_state_id, fluid) - .await - { - return Some(self.get_source(fluid, false).await); - } - } + let below_can_replace = !self.is_solid_or_source(world, &below_pos, 0, fluid).await; - let above_pos = block_pos.up(); - let above_state_id = world.get_block_state_id(&above_pos).await; + if below_can_replace { + let mut new_props = FlowingFluidProperties::default(fluid); + new_props.level = Level::L8; + new_props.falling = Falling::True; - if self.is_same_fluid(fluid, above_state_id) - || self.is_waterlogged(world, &above_pos).await.is_some() - { - return Some(self.get_flowing(fluid, Level::L8, true).await); - } - - let drop_off = self.get_drop_off().await; - let new_level = highest_level - drop_off; - - if new_level <= 0 { - return None; - } - if new_level != current_level { - return Some( - self.get_flowing(fluid, Level::from_index(new_level as u16 - 1), false) - .await, - ); - } - Some(current_props) - } - - async fn is_solid_or_source( - &self, - world: &Arc, - block_pos: &BlockPos, - state_id: BlockStateId, - fluid: &Fluid, - ) -> bool { - let block = world.get_block(block_pos).await; - - if block.id != 0 - && !self - .can_be_replaced(world, block_pos, block.id, fluid) - .await - { - return true; - } - - //TODO check if source - if self.is_same_fluid(fluid, state_id) { - let props = FlowingFluidProperties::from_state_id(state_id, fluid); - return props.level == Level::L8 && props.falling != Falling::True; - } - - false - } - - async fn spread_to_sides(&self, world: &Arc, fluid: &Fluid, block_pos: &BlockPos) { - let block_state_id = world.get_block_state_id(block_pos).await; - - let props = FlowingFluidProperties::from_state_id(block_state_id, fluid); - let level = i32::from(props.level.to_index()) + 1; - - let effective_level = if props.falling == Falling::True { - 7 - } else { - level - }; - - let drop_off = self.get_drop_off().await; - let new_level = effective_level - drop_off; - - if new_level <= 0 { - return; - } - - let spread_dirs = self.get_spread(world, fluid, block_pos).await; - - for (direction, _slope_dist) in spread_dirs { - let side_pos = block_pos.offset(direction.to_offset()); - - if self.can_replace_block(world, &side_pos, fluid).await { - let new_props = self - .get_flowing(fluid, Level::from_index(new_level as u16 - 1), false) + self.spread_to(world, fluid, &below_pos, new_props.to_state_id(fluid)) .await; - self.spread_to(world, fluid, &side_pos, new_props.to_state_id(fluid)) - .await; - } - } - } - - async fn get_spread( - &self, - world: &Arc, - fluid: &Fluid, - block_pos: &BlockPos, - ) -> HashMap { - let mut min_dist = 1000; - let mut result = HashMap::new(); - let mut ctx = None; - for direction in BlockDirection::horizontal() { - let side_pos = block_pos.offset(direction.to_offset()); - let side_state_id = world.get_block_state_id(&side_pos).await; - - let side_props = FlowingFluidProperties::from_state_id(side_state_id, fluid); - - if !self.can_pass_through(world, fluid, &side_pos).await - || (side_props.level == Level::L8 && side_props.falling != Falling::True) - { - continue; - } - - if ctx.is_none() { - ctx = Some(SpreadContext::new()); - } - - let ctx_ref = ctx.as_mut().unwrap(); - - let slope_dist = if ctx_ref.is_hole(self, world, fluid, &side_pos).await { - 0 - } else { - self.get_slope_distance(world, fluid, side_pos, 1, direction.opposite(), ctx_ref) + } else if props.level == Level::L8 && props.falling == Falling::False + || !self + .is_water_hole(world, fluid, block_pos, &below_pos) .await - }; - - if slope_dist < min_dist { - result.clear(); + { + self.spread_to_sides(world, fluid, block_pos).await; } - - if slope_dist <= min_dist { - result.insert(direction, slope_dist); - min_dist = slope_dist; - } - } - result + }) } - async fn get_slope_distance( - &self, - world: &Arc, - fluid: &Fluid, - block_pos: BlockPos, - distance: i32, - exclude_dir: BlockDirection, - ctx: &mut SpreadContext, - ) -> i32 { - if distance > self.get_slope_find_distance().await { - return 1000; - } + fn get_new_liquid<'a>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, + block_pos: &'a BlockPos, + ) -> FluidFuture<'a, Option> { + Box::pin(async move { + let current_state_id = world.get_block_state_id(block_pos).await; - let mut min_dist = 1000; + let current_props = FlowingFluidProperties::from_state_id(current_state_id, fluid); + let current_level = i32::from(current_props.level.to_index()) + 1; + if current_level == 8 && current_props.falling != Falling::True { + return Some(current_props); + } + let mut highest_level = 0; + let mut source_count = 0; - for direction in BlockDirection::horizontal() { - if direction == exclude_dir { - continue; + for direction in BlockDirection::horizontal() { + let neighbor_pos = block_pos.offset(direction.to_offset()); + let neighbor_state_id = world.get_block_state_id(&neighbor_pos).await; + + if fluid.default_state_index == Fluid::WATER.default_state_index + && self.is_waterlogged(world, &neighbor_pos).await.is_some() + { + source_count += 1; + highest_level = highest_level.max(8); + continue; + } + + if !self.is_same_fluid(fluid, neighbor_state_id) { + continue; + } + + let neighbor_props = + FlowingFluidProperties::from_state_id(neighbor_state_id, fluid); + let neighbor_level = i32::from(neighbor_props.level.to_index()) + 1; + + if neighbor_level == 8 && neighbor_props.falling != Falling::True { + source_count += 1; + } + + highest_level = highest_level.max(neighbor_level); } - let next_pos = block_pos.offset(direction.to_offset()); - - if !self.can_pass_through(world, fluid, &next_pos).await { - continue; - } - - let next_state_id = world.get_block_state_id(&next_pos).await; - - if self.is_same_fluid(fluid, next_state_id) { - let next_props = FlowingFluidProperties::from_state_id(next_state_id, fluid); - if next_props.level == Level::L8 && next_props.falling == Falling::False { - return 1000; + if source_count >= 2 && self.can_convert_to_source(world) { + let below_pos = block_pos.down(); + let below_state_id = world.get_block_state_id(&below_pos).await; + if self + .is_solid_or_source(world, &below_pos, below_state_id, fluid) + .await + { + return Some(self.get_source(fluid, false).await); } } - if ctx.is_hole(self, world, fluid, &next_pos).await { - return distance; + let above_pos = block_pos.up(); + let above_state_id = world.get_block_state_id(&above_pos).await; + + if self.is_same_fluid(fluid, above_state_id) + || self.is_waterlogged(world, &above_pos).await.is_some() + { + return Some(self.get_flowing(fluid, Level::L8, true).await); } - let next_dist = self - .get_slope_distance( - world, - fluid, - next_pos, - distance + 1, - direction.opposite(), - ctx, - ) - .await; + let drop_off = self.get_drop_off(); + let new_level = highest_level - drop_off; - min_dist = min_dist.min(next_dist); - } - min_dist + if new_level <= 0 { + return None; + } + if new_level != current_level { + return Some( + self.get_flowing(fluid, Level::from_index(new_level as u16 - 1), false) + .await, + ); + } + Some(current_props) + }) } - async fn spread_to( - &self, - world: &Arc, - _fluid: &Fluid, - pos: &BlockPos, + fn is_solid_or_source<'a>( + &'a self, + world: &'a Arc, + block_pos: &'a BlockPos, state_id: BlockStateId, - ) { - if self.is_waterlogged(world, pos).await.is_some() { - return; - } + fluid: &'a Fluid, + ) -> FluidFuture<'a, bool> { + Box::pin(async move { + let block = world.get_block(block_pos).await; - world - .set_block_state(pos, state_id, BlockFlags::NOTIFY_ALL) - .await; + if block.id != 0 + && !self + .can_be_replaced(world, block_pos, block.id, fluid) + .await + { + return true; + } + + //TODO check if source + if self.is_same_fluid(fluid, state_id) { + let props = FlowingFluidProperties::from_state_id(state_id, fluid); + return props.level == Level::L8 && props.falling != Falling::True; + } + + false + }) } - async fn can_pass_through(&self, world: &Arc, fluid: &Fluid, pos: &BlockPos) -> bool { - let state_id = world.get_block_state_id(pos).await; + fn spread_to_sides<'a>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, + block_pos: &'a BlockPos, + ) -> FluidFuture<'a, ()> { + Box::pin(async move { + let block_state_id = world.get_block_state_id(block_pos).await; - if self.is_same_fluid(fluid, state_id) { - return true; - } + let props = FlowingFluidProperties::from_state_id(block_state_id, fluid); + let level = i32::from(props.level.to_index()) + 1; - self.can_replace_block(world, pos, fluid).await + let effective_level = if props.falling == Falling::True { + 7 + } else { + level + }; + + let drop_off = self.get_drop_off(); + let new_level = effective_level - drop_off; + + if new_level <= 0 { + return; + } + + let spread_dirs = self.get_spread(world, fluid, block_pos).await; + + for (direction, _slope_dist) in spread_dirs { + let side_pos = block_pos.offset(direction.to_offset()); + + if self.can_replace_block(world, &side_pos, fluid).await { + let new_props = self + .get_flowing(fluid, Level::from_index(new_level as u16 - 1), false) + .await; + self.spread_to(world, fluid, &side_pos, new_props.to_state_id(fluid)) + .await; + } + } + }) } - async fn can_replace_block(&self, world: &Arc, pos: &BlockPos, fluid: &Fluid) -> bool { - let block = world.get_block(pos).await; + fn get_spread<'a>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, + block_pos: &'a BlockPos, + ) -> FluidFuture<'a, HashMap> { + Box::pin(async move { + let mut min_dist = 1000; + let mut result = HashMap::new(); + let mut ctx = None; + for direction in BlockDirection::horizontal() { + let side_pos = block_pos.offset(direction.to_offset()); + let side_state_id = world.get_block_state_id(&side_pos).await; - self.can_be_replaced(world, pos, block.id, fluid).await + let side_props = FlowingFluidProperties::from_state_id(side_state_id, fluid); + + if !self.can_pass_through(world, fluid, &side_pos).await + || (side_props.level == Level::L8 && side_props.falling != Falling::True) + { + continue; + } + + if ctx.is_none() { + ctx = Some(SpreadContext::new()); + } + + let ctx_ref = ctx.as_mut().unwrap(); + + let slope_dist = if ctx_ref.is_hole(self, world, fluid, &side_pos).await { + 0 + } else { + self.get_slope_distance( + world, + fluid, + side_pos, + 1, + direction.opposite(), + ctx_ref, + ) + .await + }; + + if slope_dist < min_dist { + result.clear(); + } + + if slope_dist <= min_dist { + result.insert(direction, slope_dist); + min_dist = slope_dist; + } + } + result + }) } - async fn can_be_replaced( - &self, - world: &Arc, - pos: &BlockPos, + fn get_slope_distance<'a, 'b>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, + block_pos: BlockPos, + distance: i32, + exclude_dir: BlockDirection, + ctx: &'b mut SpreadContext, + ) -> BlockFuture<'b, i32> + where + 'a: 'b, + { + Box::pin(async move { + if distance > self.get_slope_find_distance() { + return 1000; + } + + let mut min_dist = 1000; + + for direction in BlockDirection::horizontal() { + if direction == exclude_dir { + continue; + } + + let next_pos = block_pos.offset(direction.to_offset()); + + if !self.can_pass_through(world, fluid, &next_pos).await { + continue; + } + + let next_state_id = world.get_block_state_id(&next_pos).await; + + if self.is_same_fluid(fluid, next_state_id) { + let next_props = FlowingFluidProperties::from_state_id(next_state_id, fluid); + if next_props.level == Level::L8 && next_props.falling == Falling::False { + return 1000; + } + } + + if ctx.is_hole(self, world, fluid, &next_pos).await { + return distance; + } + + let next_dist = self + .get_slope_distance( + world, + fluid, + next_pos, + distance + 1, + direction.opposite(), + ctx, + ) + .await; + + min_dist = min_dist.min(next_dist); + } + min_dist + }) + } + + fn spread_to<'a>( + &'a self, + world: &'a Arc, + _fluid: &'a Fluid, + pos: &'a BlockPos, + state_id: BlockStateId, + ) -> FluidFuture<'a, ()> { + Box::pin(async move { + if self.is_waterlogged(world, pos).await.is_some() { + return; + } + + world + .set_block_state(pos, state_id, BlockFlags::NOTIFY_ALL) + .await; + }) + } + + fn can_pass_through<'a>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, + pos: &'a BlockPos, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { + let state_id = world.get_block_state_id(pos).await; + + if self.is_same_fluid(fluid, state_id) { + return true; + } + + self.can_replace_block(world, pos, fluid).await + }) + } + + fn can_replace_block<'a>( + &'a self, + world: &'a Arc, + pos: &'a BlockPos, + fluid: &'a Fluid, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { + let block = world.get_block(pos).await; + self.can_be_replaced(world, pos, block.id, fluid).await + }) + } + + fn can_be_replaced<'a>( + &'a self, + world: &'a Arc, + pos: &'a BlockPos, block_id: BlockId, - fluid: &Fluid, - ) -> bool { - // let block_state_id = world.get_block_state_id(pos).await; - let block_state = world.get_block_state(pos).await; + fluid: &'a Fluid, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { + // let block_state_id = world.get_block_state_id(pos).await; + let block_state = world.get_block_state(pos).await; - if let Some(other_fluid) = Fluid::from_state_id(block_state.id) { - if fluid.id != other_fluid.id { - return true; + if let Some(other_fluid) = Fluid::from_state_id(block_state.id) { + if fluid.id != other_fluid.id { + return true; + } + if other_fluid.is_source(block_state.id) && other_fluid.is_falling(block_state.id) { + return true; + } } - if other_fluid.is_source(block_state.id) && other_fluid.is_falling(block_state.id) { - return true; - } - } - //TODO Add check for blocks that aren't solid - matches!(block_id, 0) + //TODO Add check for blocks that aren't solid + matches!(block_id, 0) + }) } - async fn is_water_hole( - &self, - world: &Arc, - fluid: &Fluid, - _pos: &BlockPos, - below_pos: &BlockPos, - ) -> bool { - let below_state_id = world.get_block_state_id(below_pos).await; + fn is_water_hole<'a>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, + _pos: &'a BlockPos, + below_pos: &'a BlockPos, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { + let below_state_id = world.get_block_state_id(below_pos).await; - if self.is_same_fluid(fluid, below_state_id) { - return true; - } + if self.is_same_fluid(fluid, below_state_id) { + return true; + } - if self.can_replace_block(world, below_pos, fluid).await { - return true; - } + if self.can_replace_block(world, below_pos, fluid).await { + return true; + } - false + false + }) } } diff --git a/pumpkin/src/block/fluid/lava.rs b/pumpkin/src/block/fluid/lava.rs index a4aa48bfd..fdc4b527a 100644 --- a/pumpkin/src/block/fluid/lava.rs +++ b/pumpkin/src/block/fluid/lava.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use async_trait::async_trait; use pumpkin_data::{ Block, BlockDirection, fluid::{Falling, Fluid, FluidProperties, Level}, @@ -10,7 +9,14 @@ use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::{BlockStateId, tick::TickPriority, world::BlockFlags}; -use crate::{block::fluid::FluidBehaviour, entity::EntityBase, world::World}; +use crate::{ + block::{ + BlockFuture, + fluid::{FluidBehaviour, flowing::FluidFuture}, + }, + entity::EntityBase, + world::World, +}; use super::flowing::FlowingFluid; type FlowingFluidProperties = pumpkin_data::fluid::FlowingWaterLikeFluidProperties; @@ -72,95 +78,110 @@ impl FlowingLava { const LAVA_FLOW_SPEED: u8 = 30; -#[async_trait] impl FluidBehaviour for FlowingLava { - async fn placed( - &self, - world: &Arc, - fluid: &Fluid, + fn placed<'a>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, state_id: BlockStateId, - block_pos: &BlockPos, + block_pos: &'a BlockPos, old_state_id: BlockStateId, _notify: bool, - ) { - if old_state_id != state_id && self.receive_neighbor_fluids(world, fluid, block_pos).await { - world - .schedule_fluid_tick(fluid, *block_pos, LAVA_FLOW_SPEED, TickPriority::Normal) - .await; - } + ) -> BlockFuture<'a, ()> { + Box::pin(async move { + if old_state_id != state_id + && self.receive_neighbor_fluids(world, fluid, block_pos).await + { + world + .schedule_fluid_tick(fluid, *block_pos, LAVA_FLOW_SPEED, TickPriority::Normal) + .await; + } + }) } - async fn on_scheduled_tick(&self, world: &Arc, fluid: &Fluid, block_pos: &BlockPos) { - self.spread_fluid(world, fluid, block_pos).await; + fn on_scheduled_tick<'a>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, + block_pos: &'a BlockPos, + ) -> BlockFuture<'a, ()> { + Box::pin(async move { + self.spread_fluid(world, fluid, block_pos).await; + }) } - async fn on_neighbor_update( - &self, - world: &Arc, - fluid: &Fluid, - block_pos: &BlockPos, + fn on_neighbor_update<'a>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, + block_pos: &'a BlockPos, _notify: bool, - ) { - if self.receive_neighbor_fluids(world, fluid, block_pos).await { - world - .schedule_fluid_tick(fluid, *block_pos, LAVA_FLOW_SPEED, TickPriority::Normal) - .await; - } + ) -> BlockFuture<'a, ()> { + Box::pin(async move { + if self.receive_neighbor_fluids(world, fluid, block_pos).await { + world + .schedule_fluid_tick(fluid, *block_pos, LAVA_FLOW_SPEED, TickPriority::Normal) + .await; + } + }) } - async fn on_entity_collision(&self, entity: &dyn EntityBase) { - let base_entity = entity.get_entity(); - if !base_entity.entity_type.fire_immune { - base_entity.set_on_fire_for(15.0); - } + fn on_entity_collision<'a>(&'a self, entity: &'a dyn EntityBase) -> BlockFuture<'a, ()> { + Box::pin(async move { + let base_entity = entity.get_entity(); + if !base_entity.entity_type.fire_immune { + base_entity.set_on_fire_for(15.0); + } + }) } } -#[async_trait] impl FlowingFluid for FlowingLava { //TODO implement ultrawarm logic - async fn get_drop_off(&self) -> i32 { + fn get_drop_off(&self) -> i32 { 2 } - async fn get_slope_find_distance(&self) -> i32 { + fn get_slope_find_distance(&self) -> i32 { 2 } - async fn can_convert_to_source(&self, _world: &Arc) -> bool { + fn can_convert_to_source(&self, _world: &Arc) -> bool { //TODO add game rule check for lava conversion false } - async fn spread_to( - &self, - world: &Arc, - fluid: &Fluid, - pos: &BlockPos, + fn spread_to<'a>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, + pos: &'a BlockPos, state_id: BlockStateId, - ) { - let mut new_props = FlowingFluidProperties::default(fluid); - new_props.level = Level::L8; - new_props.falling = Falling::True; - if state_id == new_props.to_state_id(fluid) { - // STONE creation - if world.get_block(pos).await == &Block::WATER { - world - .set_block_state(pos, Block::STONE.default_state.id, BlockFlags::NOTIFY_ALL) - .await; - world - .sync_world_event(WorldEvent::LavaExtinguished, *pos, 0) - .await; + ) -> FluidFuture<'a, ()> { + Box::pin(async move { + let mut new_props = FlowingFluidProperties::default(fluid); + new_props.level = Level::L8; + new_props.falling = Falling::True; + if state_id == new_props.to_state_id(fluid) { + // STONE creation + if world.get_block(pos).await == &Block::WATER { + world + .set_block_state(pos, Block::STONE.default_state.id, BlockFlags::NOTIFY_ALL) + .await; + world + .sync_world_event(WorldEvent::LavaExtinguished, *pos, 0) + .await; + return; + } + } + + if self.is_waterlogged(world, pos).await.is_some() { return; } - } - if self.is_waterlogged(world, pos).await.is_some() { - return; - } - - world - .set_block_state(pos, state_id, BlockFlags::NOTIFY_ALL) - .await; + world + .set_block_state(pos, state_id, BlockFlags::NOTIFY_ALL) + .await; + }) } } diff --git a/pumpkin/src/block/fluid/mod.rs b/pumpkin/src/block/fluid/mod.rs index b61a2b95c..4dac5ac1d 100644 --- a/pumpkin/src/block/fluid/mod.rs +++ b/pumpkin/src/block/fluid/mod.rs @@ -4,8 +4,8 @@ pub mod water; use std::sync::Arc; +use crate::block::BlockFuture; use crate::entity::{EntityBase, player::Player}; -use async_trait::async_trait; use pumpkin_data::BlockDirection; use pumpkin_data::{fluid::Fluid, item::Item}; use pumpkin_protocol::java::server::play::SUseItemOn; @@ -16,78 +16,103 @@ use crate::{server::Server, world::World}; use super::{BlockIsReplacing, registry::BlockActionResult}; -#[async_trait] pub trait FluidBehaviour: Send + Sync { - async fn normal_use( - &self, - _fluid: &Fluid, - _player: &Player, + fn normal_use<'a>( + &'a self, + _fluid: &'a Fluid, + _player: &'a Player, _location: BlockPos, - _server: &Server, - _world: &Arc, - ) { - } - async fn use_with_item( - &self, - _fluid: &Fluid, - _player: &Player, - _location: BlockPos, - _item: &Item, - _server: &Server, - _world: &Arc, - ) -> BlockActionResult { - BlockActionResult::Pass + _server: &'a Server, + _world: &'a Arc, + ) -> BlockFuture<'a, ()> { + Box::pin(async {}) } - async fn placed( - &self, - _world: &Arc, - _fluid: &Fluid, + fn use_with_item<'a>( + &'a self, + _fluid: &'a Fluid, + _player: &'a Player, + _location: BlockPos, + _item: &'a Item, + _server: &'a Server, + _world: &'a Arc, + ) -> BlockFuture<'a, BlockActionResult> { + Box::pin(async { BlockActionResult::Pass }) + } + + fn placed<'a>( + &'a self, + _world: &'a Arc, + _fluid: &'a Fluid, _state_id: BlockStateId, - _block_pos: &BlockPos, + _block_pos: &'a BlockPos, _old_state_id: BlockStateId, _notify: bool, - ) { + ) -> BlockFuture<'a, ()> { + Box::pin(async {}) } #[allow(clippy::too_many_arguments)] - async fn on_place( - &self, - _server: &Server, - _world: &Arc, - fluid: &Fluid, + fn on_place<'a>( + &'a self, + _server: &'a Server, + _world: &'a Arc, + fluid: &'a Fluid, _face: BlockDirection, - _block_pos: &BlockPos, - _use_item_on: &SUseItemOn, + _block_pos: &'a BlockPos, + _use_item_on: &'a SUseItemOn, _replacing: BlockIsReplacing, - ) -> BlockStateId { - fluid.default_state_index + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async { fluid.default_state_index }) } - async fn get_state_for_neighbour_update( - &self, - _world: &Arc, - _fluid: &Fluid, - _block_pos: &BlockPos, + fn get_state_for_neighbour_update<'a>( + &'a self, + _world: &'a Arc, + _fluid: &'a Fluid, + _block_pos: &'a BlockPos, _notify: bool, - ) -> BlockStateId { - 0 + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async { 0 }) } - async fn on_neighbor_update( - &self, - _world: &Arc, - _fluid: &Fluid, - _block_pos: &BlockPos, + fn on_neighbor_update<'a>( + &'a self, + _world: &'a Arc, + _fluid: &'a Fluid, + _block_pos: &'a BlockPos, _notify: bool, - ) { + ) -> BlockFuture<'a, ()> { + Box::pin(async {}) } - async fn on_entity_collision(&self, _entity: &dyn EntityBase) {} + fn on_entity_collision<'a>(&'a self, _entity: &'a dyn EntityBase) -> BlockFuture<'a, ()> { + Box::pin(async {}) + } - async fn on_scheduled_tick(&self, _world: &Arc, _fluid: &Fluid, _block_pos: &BlockPos) {} + fn on_scheduled_tick<'a>( + &'a self, + _world: &'a Arc, + _fluid: &'a Fluid, + _block_pos: &'a BlockPos, + ) -> BlockFuture<'a, ()> { + Box::pin(async {}) + } - async fn random_tick(&self, _fluid: &Fluid, _world: &Arc, _block_pos: &BlockPos) {} + fn random_tick<'a>( + &'a self, + _fluid: &'a Fluid, + _world: &'a Arc, + _block_pos: &'a BlockPos, + ) -> BlockFuture<'a, ()> { + Box::pin(async {}) + } - async fn create_legacy_block(&self, _world: &Arc, _block_pos: &BlockPos) {} + fn create_legacy_block<'a>( + &'a self, + _world: &'a Arc, + _block_pos: &'a BlockPos, + ) -> BlockFuture<'a, ()> { + Box::pin(async {}) + } } diff --git a/pumpkin/src/block/fluid/water.rs b/pumpkin/src/block/fluid/water.rs index 99dd16272..fc78b2ae9 100644 --- a/pumpkin/src/block/fluid/water.rs +++ b/pumpkin/src/block/fluid/water.rs @@ -1,12 +1,15 @@ use std::sync::Arc; -use async_trait::async_trait; use pumpkin_data::fluid::Fluid; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::{BlockStateId, tick::TickPriority}; -use crate::{block::fluid::FluidBehaviour, entity::EntityBase, world::World}; +use crate::{ + block::{BlockFuture, fluid::FluidBehaviour}, + entity::EntityBase, + world::World, +}; use super::flowing::FlowingFluid; @@ -15,56 +18,67 @@ pub struct FlowingWater; const WATER_FLOW_SPEED: u8 = 5; -#[async_trait] impl FluidBehaviour for FlowingWater { - async fn placed( - &self, - world: &Arc, - fluid: &Fluid, + fn placed<'a>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, state_id: BlockStateId, - block_pos: &BlockPos, + block_pos: &'a BlockPos, old_state_id: BlockStateId, _notify: bool, - ) { - if old_state_id != state_id { + ) -> BlockFuture<'a, ()> { + Box::pin(async move { + if old_state_id != state_id { + world + .schedule_fluid_tick(fluid, *block_pos, WATER_FLOW_SPEED, TickPriority::Normal) + .await; + } + }) + } + + fn on_scheduled_tick<'a>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, + block_pos: &'a BlockPos, + ) -> BlockFuture<'a, ()> { + Box::pin(async { + self.spread_fluid(world, fluid, block_pos).await; + }) + } + + fn on_neighbor_update<'a>( + &'a self, + world: &'a Arc, + fluid: &'a Fluid, + block_pos: &'a BlockPos, + _notify: bool, + ) -> BlockFuture<'a, ()> { + Box::pin(async { world .schedule_fluid_tick(fluid, *block_pos, WATER_FLOW_SPEED, TickPriority::Normal) .await; - } + }) } - async fn on_scheduled_tick(&self, world: &Arc, fluid: &Fluid, block_pos: &BlockPos) { - self.spread_fluid(world, fluid, block_pos).await; - } - - async fn on_neighbor_update( - &self, - world: &Arc, - fluid: &Fluid, - block_pos: &BlockPos, - _notify: bool, - ) { - world - .schedule_fluid_tick(fluid, *block_pos, WATER_FLOW_SPEED, TickPriority::Normal) - .await; - } - - async fn on_entity_collision(&self, entity: &dyn EntityBase) { - entity.get_entity().extinguish(); + fn on_entity_collision<'a>(&'a self, entity: &'a dyn EntityBase) -> BlockFuture<'a, ()> { + Box::pin(async { + entity.get_entity().extinguish(); + }) } } -#[async_trait] impl FlowingFluid for FlowingWater { - async fn get_drop_off(&self) -> i32 { + fn get_drop_off(&self) -> i32 { 1 } - async fn get_slope_find_distance(&self) -> i32 { + fn get_slope_find_distance(&self) -> i32 { 4 } - async fn can_convert_to_source(&self, _world: &Arc) -> bool { + fn can_convert_to_source(&self, _world: &Arc) -> bool { //TODO add game rule check for water conversion true } diff --git a/pumpkin/src/block/mod.rs b/pumpkin/src/block/mod.rs index 0d6a95ef9..b8427d967 100644 --- a/pumpkin/src/block/mod.rs +++ b/pumpkin/src/block/mod.rs @@ -9,6 +9,7 @@ use crate::entity::experience_orb::ExperienceOrbEntity; use crate::entity::player::Player; use crate::world::World; use crate::world::loot::{LootContextParameters, LootTableExt}; +use std::pin::Pin; use std::sync::Arc; pub mod blocks; @@ -18,7 +19,6 @@ pub mod registry; use crate::block::registry::BlockActionResult; use crate::entity::EntityBase; use crate::server::Server; -use async_trait::async_trait; use pumpkin_data::BlockDirection; use pumpkin_protocol::java::server::play::SUseItemOn; use pumpkin_util::math::vector3::Vector3; @@ -37,86 +37,127 @@ pub trait BlockMetadata { } } -#[async_trait] +pub type BlockFuture<'a, T> = Pin + Send + 'a>>; + pub trait BlockBehaviour: Send + Sync { - async fn normal_use(&self, _args: NormalUseArgs<'_>) -> BlockActionResult { - BlockActionResult::Pass + fn normal_use<'a>(&'a self, _args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { + Box::pin(async move { BlockActionResult::Pass }) } - async fn use_with_item(&self, _args: UseWithItemArgs<'_>) -> BlockActionResult { - BlockActionResult::PassToDefaultBlockAction + fn use_with_item<'a>( + &'a self, + _args: UseWithItemArgs<'a>, + ) -> BlockFuture<'a, BlockActionResult> { + Box::pin(async move { BlockActionResult::PassToDefaultBlockAction }) } - async fn on_entity_collision(&self, _args: OnEntityCollisionArgs<'_>) {} + fn on_entity_collision<'a>(&'a self, _args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move {}) + } fn should_drop_items_on_explosion(&self) -> bool { true } - async fn explode(&self, _args: ExplodeArgs<'_>) {} + fn explode<'a>(&'a self, _args: ExplodeArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move {}) + } /// Handles the block event, which is an event specific to a block with an integer ID and data. /// /// returns whether the event was handled successfully - async fn on_synced_block_event(&self, _args: OnSyncedBlockEventArgs<'_>) -> bool { - false + fn on_synced_block_event<'a>( + &'a self, + _args: OnSyncedBlockEventArgs<'a>, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { false }) } /// getPlacementState in source code - async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { - args.block.default_state.id + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { args.block.default_state.id }) } - async fn random_tick(&self, _args: RandomTickArgs<'_>) {} - - async fn can_place_at(&self, _args: CanPlaceAtArgs<'_>) -> bool { - true + fn random_tick<'a>(&'a self, _args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move {}) } - async fn can_update_at(&self, _args: CanUpdateAtArgs<'_>) -> bool { - false + fn can_place_at<'a>(&'a self, _args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> { + Box::pin(async move { true }) + } + + fn can_update_at<'a>(&'a self, _args: CanUpdateAtArgs<'a>) -> BlockFuture<'a, bool> { + Box::pin(async move { false }) } /// onBlockAdded in source code - async fn placed(&self, _args: PlacedArgs<'_>) {} - - async fn player_placed(&self, _args: PlayerPlacedArgs<'_>) {} - - async fn broken(&self, _args: BrokenArgs<'_>) {} - - async fn on_neighbor_update(&self, _args: OnNeighborUpdateArgs<'_>) {} - - /// Called if a block state is replaced or it replaces another state - async fn prepare(&self, _args: PrepareArgs<'_>) {} - - async fn get_state_for_neighbor_update( - &self, - args: GetStateForNeighborUpdateArgs<'_>, - ) -> BlockStateId { - args.state_id + fn placed<'a>(&'a self, _args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move {}) } - async fn on_scheduled_tick(&self, _args: OnScheduledTickArgs<'_>) {} + fn player_placed<'a>(&'a self, _args: PlayerPlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move {}) + } - async fn on_state_replaced(&self, _args: OnStateReplacedArgs<'_>) {} + fn broken<'a>(&'a self, _args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move {}) + } + + fn on_neighbor_update<'a>(&'a self, _args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move {}) + } + + /// Called if a block state is replaced or it replaces another state + fn prepare<'a>(&'a self, _args: PrepareArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move {}) + } + + fn get_state_for_neighbor_update<'a>( + &'a self, + args: GetStateForNeighborUpdateArgs<'a>, + ) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { args.state_id }) + } + + fn on_scheduled_tick<'a>(&'a self, _args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move {}) + } + + fn on_state_replaced<'a>(&'a self, _args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move {}) + } + + // --- Redstone/Comparator Methods --- /// Sides where redstone connects to - async fn emits_redstone_power(&self, _args: EmitsRedstonePowerArgs<'_>) -> bool { - false + fn emits_redstone_power<'a>( + &'a self, + _args: EmitsRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, bool> { + Box::pin(async move { false }) } /// Weak redstone power, aka. block that should be powered needs to be directly next to the source block - async fn get_weak_redstone_power(&self, _args: GetRedstonePowerArgs<'_>) -> u8 { - 0 + fn get_weak_redstone_power<'a>( + &'a self, + _args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { 0 }) } /// Strong redstone power. this can power a block that then gives power - async fn get_strong_redstone_power(&self, _args: GetRedstonePowerArgs<'_>) -> u8 { - 0 + fn get_strong_redstone_power<'a>( + &'a self, + _args: GetRedstonePowerArgs<'a>, + ) -> BlockFuture<'a, u8> { + Box::pin(async move { 0 }) } - async fn get_comparator_output(&self, _args: GetComparatorOutputArgs<'_>) -> Option { - None + fn get_comparator_output<'a>( + &'a self, + _args: GetComparatorOutputArgs<'a>, + ) -> BlockFuture<'a, Option> { + Box::pin(async move { None }) } } diff --git a/pumpkin/src/command/args/resource/effect.rs b/pumpkin/src/command/args/resource/effect.rs index 48caeef39..8925741bc 100644 --- a/pumpkin/src/command/args/resource/effect.rs +++ b/pumpkin/src/command/args/resource/effect.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::effect::StatusEffect; use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; @@ -27,7 +26,6 @@ impl GetClientSideArgParser for EffectTypeArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for EffectTypeArgumentConsumer { fn consume<'a, 'b>( &'a self, diff --git a/pumpkin/src/command/args/resource/enchantment.rs b/pumpkin/src/command/args/resource/enchantment.rs index 9dc0fa2a5..08cf36764 100644 --- a/pumpkin/src/command/args/resource/enchantment.rs +++ b/pumpkin/src/command/args/resource/enchantment.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::Enchantment; use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; @@ -27,7 +26,6 @@ impl GetClientSideArgParser for EnchantmentArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for EnchantmentArgumentConsumer { fn consume<'a, 'b>( &'a self, diff --git a/pumpkin/src/command/args/resource/item.rs b/pumpkin/src/command/args/resource/item.rs index 6ec703674..37089e317 100644 --- a/pumpkin/src/command/args/resource/item.rs +++ b/pumpkin/src/command/args/resource/item.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::item::Item; use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use pumpkin_util::text::TextComponent; @@ -26,7 +25,6 @@ impl GetClientSideArgParser for ItemArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for ItemArgumentConsumer { fn consume<'a>( &'a self, diff --git a/pumpkin/src/command/args/resource/particle.rs b/pumpkin/src/command/args/resource/particle.rs index 6627c13b3..f42639518 100644 --- a/pumpkin/src/command/args/resource/particle.rs +++ b/pumpkin/src/command/args/resource/particle.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::particle::Particle; use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; @@ -27,7 +26,6 @@ impl GetClientSideArgParser for ParticleArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for ParticleArgumentConsumer { fn consume<'a, 'b>( &'a self, diff --git a/pumpkin/src/command/args/summonable_entities.rs b/pumpkin/src/command/args/summonable_entities.rs index 0ebb8607f..59cef1e08 100644 --- a/pumpkin/src/command/args/summonable_entities.rs +++ b/pumpkin/src/command/args/summonable_entities.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::entity::EntityType; use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use pumpkin_util::text::TextComponent; @@ -28,7 +27,6 @@ impl GetClientSideArgParser for SummonableEntitiesArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for SummonableEntitiesArgumentConsumer { fn consume<'a, 'b>( &'a self, diff --git a/pumpkin/src/entity/ai/goal/active_target_goal.rs b/pumpkin/src/entity/ai/goal/active_target_goal.rs index 2bbb3964a..5932eea7d 100644 --- a/pumpkin/src/entity/ai/goal/active_target_goal.rs +++ b/pumpkin/src/entity/ai/goal/active_target_goal.rs @@ -1,11 +1,11 @@ use super::{Controls, Goal, to_goal_ticks}; +use crate::entity::ai::goal::GoalFuture; use crate::entity::ai::goal::track_target_goal::TrackTargetGoal; use crate::entity::ai::target_predicate::TargetPredicate; use crate::entity::living::LivingEntity; use crate::entity::mob::Mob; use crate::entity::{EntityBase, mob::MobEntity, player::Player}; use crate::world::World; -use async_trait::async_trait; use pumpkin_data::entity::EntityType; use rand::Rng; use std::sync::Arc; @@ -89,31 +89,36 @@ impl ActiveTargetGoal { } } -#[async_trait] impl Goal for ActiveTargetGoal { - async fn can_start(&mut self, mob: &dyn Mob) -> bool { - if self.reciprocal_chance > 0 - && mob.get_random().random_range(0..self.reciprocal_chance) != 0 - { - return false; - } - self.find_closest_target(mob.get_mob_entity()).await; - self.target.is_some() + fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { + if self.reciprocal_chance > 0 + && mob.get_random().random_range(0..self.reciprocal_chance) != 0 + { + return false; + } + self.find_closest_target(mob.get_mob_entity()).await; + self.target.is_some() + }) } - async fn should_continue(&self, mob: &dyn Mob) -> bool { - self.track_target_goal.should_continue(mob).await + fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { self.track_target_goal.should_continue(mob).await }) } - async fn start(&mut self, mob: &dyn Mob) { - let mob_entity = mob.get_mob_entity(); - let mut mob_target = mob_entity.target.lock().await; - (*mob_target).clone_from(&self.target); + fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + let mob_entity = mob.get_mob_entity(); + let mut mob_target = mob_entity.target.lock().await; + (*mob_target).clone_from(&self.target); - self.track_target_goal.start(mob).await; + self.track_target_goal.start(mob).await; + }) } - async fn stop(&mut self, mob: &dyn Mob) { - self.track_target_goal.stop(mob).await; + fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + self.track_target_goal.stop(mob).await; + }) } fn controls(&self) -> Controls { diff --git a/pumpkin/src/entity/ai/goal/ambient_stand_goal.rs b/pumpkin/src/entity/ai/goal/ambient_stand_goal.rs index e567349ed..0863156a2 100644 --- a/pumpkin/src/entity/ai/goal/ambient_stand_goal.rs +++ b/pumpkin/src/entity/ai/goal/ambient_stand_goal.rs @@ -1,6 +1,5 @@ use super::{Controls, Goal}; -use crate::entity::mob::Mob; -use async_trait::async_trait; +use crate::entity::{ai::goal::GoalFuture, mob::Mob}; use rand::Rng; #[allow(dead_code)] @@ -18,18 +17,16 @@ impl AmbientStandGoal { } } -#[async_trait] impl Goal for AmbientStandGoal { - async fn can_start(&mut self, mob: &dyn Mob) -> bool { - self.cooldown += 1; - if self.cooldown > 0 && mob.get_random().random_range(0..1000) < self.cooldown { - self.reset_cooldown(); - } + fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { + self.cooldown += 1; + if self.cooldown > 0 && mob.get_random().random_range(0..1000) < self.cooldown { + self.reset_cooldown(); + } - false - } - async fn should_continue(&self, _mob: &dyn Mob) -> bool { - false + false + }) } fn controls(&self) -> Controls { diff --git a/pumpkin/src/entity/ai/goal/look_around_goal.rs b/pumpkin/src/entity/ai/goal/look_around_goal.rs index f55b32beb..8d105757b 100644 --- a/pumpkin/src/entity/ai/goal/look_around_goal.rs +++ b/pumpkin/src/entity/ai/goal/look_around_goal.rs @@ -1,8 +1,7 @@ use std::f64::consts::TAU; use super::{Controls, Goal}; -use crate::entity::mob::Mob; -use async_trait::async_trait; +use crate::entity::{ai::goal::GoalFuture, mob::Mob}; use rand::Rng; #[allow(dead_code)] @@ -24,35 +23,39 @@ impl Default for LookAroundGoal { } } -#[async_trait] impl Goal for LookAroundGoal { - async fn can_start(&mut self, mob: &dyn Mob) -> bool { - mob.get_random().random::() < 0.02 + fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { mob.get_random().random::() < 0.02 }) } - async fn should_continue(&self, _mob: &dyn Mob) -> bool { - self.look_time >= 0 + fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { self.look_time >= 0 }) } - async fn start(&mut self, mob: &dyn Mob) { - let d = TAU * mob.get_random().random::(); - self.delta_x = d.cos(); - self.delta_z = d.sin(); - let look_time = 20 + mob.get_random().random_range(0..20); - self.look_time = look_time; + fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + let d = TAU * mob.get_random().random::(); + self.delta_x = d.cos(); + self.delta_z = d.sin(); + let look_time = 20 + mob.get_random().random_range(0..20); + self.look_time = look_time; + }) } - async fn tick(&mut self, mob: &dyn Mob) { - let mob_entity = mob.get_mob_entity(); - self.look_time = (self.look_time - 1).max(0); - let mut look_control = mob_entity.look_control.lock().await; - let pos = mob_entity.living_entity.entity.pos.load(); - look_control.look_at( - mob, - pos.x + self.delta_x, - mob_entity.living_entity.entity.get_eye_y(), - pos.z + self.delta_z, - ); + fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + let mob_entity = mob.get_mob_entity(); + self.look_time = (self.look_time - 1).max(0); + let mut look_control = mob_entity.look_control.lock().await; + + let pos = mob_entity.living_entity.entity.pos.load(); + look_control.look_at( + mob, + pos.x + self.delta_x, + mob_entity.living_entity.entity.get_eye_y(), + pos.z + self.delta_z, + ); + }) } fn should_run_every_tick(&self) -> bool { diff --git a/pumpkin/src/entity/ai/goal/look_at_entity.rs b/pumpkin/src/entity/ai/goal/look_at_entity.rs index 250b3b082..eb252887d 100644 --- a/pumpkin/src/entity/ai/goal/look_at_entity.rs +++ b/pumpkin/src/entity/ai/goal/look_at_entity.rs @@ -1,9 +1,9 @@ use super::{Controls, Goal}; +use crate::entity::ai::goal::GoalFuture; use crate::entity::ai::target_predicate::TargetPredicate; use crate::entity::mob::Mob; use crate::entity::predicate::EntityPredicate; use crate::entity::{EntityBase, player::Player}; -use async_trait::async_trait; use pumpkin_data::entity::EntityType; use rand::Rng; use std::sync::{Arc, Weak}; @@ -76,77 +76,81 @@ impl LookAtEntityGoal { } } -#[async_trait] impl Goal for LookAtEntityGoal { - async fn can_start(&mut self, mob: &dyn Mob) -> bool { - if mob.get_random().random::() >= self.chance { - return false; - } - - let mob_entity = mob.get_mob_entity(); - - { - let mob_target = mob_entity.target.lock().await; - if mob_target.is_some() { - self.target.clone_from(&mob_target); - } - } - - let world = &mob_entity.living_entity.entity.world; - if *self.target_type == EntityType::PLAYER { - self.target = world - .get_closest_player( - mob_entity.living_entity.entity.pos.load(), - self.range.into(), - ) - .await - .map(|p: Arc| p as Arc); - } else { - self.target = world - .get_closest_entity( - mob_entity.living_entity.entity.pos.load(), - self.range.into(), - Some(&[self.target_type]), - ) - .await; - } - - self.target.is_some() - } - - async fn should_continue(&self, mob: &dyn Mob) -> bool { - let mob = mob.get_mob_entity(); - if let Some(target) = &self.target { - if !target.get_entity().is_alive() { + fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { + if mob.get_random().random::() >= self.chance { return false; } - let mob_pos = mob.living_entity.entity.pos.load(); - let target_pos = target.get_entity().pos.load(); - if mob_pos.squared_distance_to_vec(target_pos) as f32 > (self.range * self.range) { - return false; + + let mob_entity = mob.get_mob_entity(); + + { + let mob_target = mob_entity.target.lock().await; + if mob_target.is_some() { + self.target.clone_from(&mob_target); + } } - return self.look_time > 0; - } - false + + let world = &mob_entity.living_entity.entity.world; + let mob_pos = mob_entity.living_entity.entity.pos.load(); + + if *self.target_type == EntityType::PLAYER { + self.target = world + .get_closest_player(mob_pos, self.range.into()) + .await + .map(|p: Arc| p as Arc); + } else { + self.target = world + .get_closest_entity(mob_pos, self.range.into(), Some(&[self.target_type])) + .await; + } + + self.target.is_some() + }) } - async fn start(&mut self, mob: &dyn Mob) { - self.look_time = self.get_tick_count(40 + mob.get_random().random_range(0..40)); + fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { + let mob_entity = mob.get_mob_entity(); + if let Some(target) = &self.target { + if !target.get_entity().is_alive() { + return false; + } + let mob_pos = mob_entity.living_entity.entity.pos.load(); + let target_pos = target.get_entity().pos.load(); + if mob_pos.squared_distance_to_vec(target_pos) as f32 > (self.range * self.range) { + return false; + } + return self.look_time > 0; + } + false + }) } - async fn stop(&mut self, _mob: &dyn Mob) { - self.target = None; + fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + self.look_time = self.get_tick_count(40 + mob.get_random().random_range(0..40)); + }) } - async fn tick(&mut self, mob: &dyn Mob) { - let mob = mob.get_mob_entity(); - if let Some(target) = &self.target - && target.get_entity().is_alive() - { - let target_pos = target.get_entity().pos.load(); - mob.living_entity.entity.look_at(target_pos); - self.look_time -= 1; - } + fn stop<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + self.target = None; + }) + } + + fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + let mob_entity = mob.get_mob_entity(); + if let Some(target) = &self.target + && target.get_entity().is_alive() + { + let target_pos = target.get_entity().pos.load(); + mob_entity.living_entity.entity.look_at(target_pos); + self.look_time -= 1; + } + }) } fn controls(&self) -> Controls { diff --git a/pumpkin/src/entity/ai/goal/melee_attack_goal.rs b/pumpkin/src/entity/ai/goal/melee_attack_goal.rs index 0e10c002b..6192d45d1 100644 --- a/pumpkin/src/entity/ai/goal/melee_attack_goal.rs +++ b/pumpkin/src/entity/ai/goal/melee_attack_goal.rs @@ -1,9 +1,9 @@ use super::{Controls, Goal}; use crate::entity::EntityBase; +use crate::entity::ai::goal::GoalFuture; use crate::entity::ai::path::NavigatorGoal; use crate::entity::mob::Mob; use crate::entity::predicate::EntityPredicate; -use async_trait::async_trait; use pumpkin_util::math::vector3::Vector3; const MAX_ATTACK_TIME: i64 = 20; @@ -42,93 +42,113 @@ impl MeleeAttackGoal { } } -#[async_trait] impl Goal for MeleeAttackGoal { - async fn can_start(&mut self, mob: &dyn Mob) -> bool { - let time = { - let world = &mob.get_entity().world; - let level_time = world.level_time.lock().await; - level_time.world_age - }; + fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { + let time = { + let world = &mob.get_entity().world; + // Assuming world.level_time is the AsyncLevelTimeLock + let level_time = world.level_time.lock().await; + level_time.world_age + }; - if time - self.last_update_time < MAX_ATTACK_TIME { - return false; - } - self.last_update_time = time; - let target = mob.get_mob_entity().target.lock().await; - let Some(target) = target.as_ref() else { - return false; - }; - if !target.get_entity().is_alive() { - return false; - } - // TODO: add path when is implemented Navigation - true //TODO: modify that because if a path to the target not exists then call mob.is_in_attack_range(target) + if time - self.last_update_time < MAX_ATTACK_TIME { + return false; + } + self.last_update_time = time; + + // 💡 FIX: Await lock operation + let target = mob.get_mob_entity().target.lock().await; + + let Some(target) = target.as_ref() else { + return false; + }; + if !target.get_entity().is_alive() { + return false; + } + // TODO: add path when is implemented Navigation + true //TODO: modify that because if a path to the target not exists then call mob.is_in_attack_range(target) + }) } - async fn should_continue(&self, mob: &dyn Mob) -> bool { - let target = mob.get_mob_entity().target.lock().await; - let Some(target) = target.as_ref() else { - return false; - }; - if !target.get_entity().is_alive() { - return false; - } - if !self.pause_when_mob_idle { - return !mob.get_mob_entity().navigator.lock().await.is_idle(); - } - if mob - .get_mob_entity() - .is_in_position_target_range_pos(target.get_entity().block_pos.load()) - { - target - .get_player() - .is_some_and(|player| player.is_spectator() || player.is_creative()) - } else { - false - } + fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { + let target = mob.get_mob_entity().target.lock().await; + + let Some(target) = target.as_ref() else { + return false; + }; + if !target.get_entity().is_alive() { + return false; + } + + if !self.pause_when_mob_idle { + return !mob.get_mob_entity().navigator.lock().await.is_idle(); + } + + if mob + .get_mob_entity() + .is_in_position_target_range_pos(target.get_entity().block_pos.load()) + { + // This is sync based on the assumed Player methods + target + .get_player() + .is_some_and(|player| player.is_spectator() || player.is_creative()) + } else { + false + } + }) } - async fn start(&mut self, mob: &dyn Mob) { - // TODO: add missing fields like mob attacking to true and correct Navigation methods - if let Some(target) = mob.get_mob_entity().target.lock().await.as_ref() { - let mut navigator = mob.get_mob_entity().navigator.lock().await; - navigator.set_progress(NavigatorGoal { - current_progress: mob.get_entity().pos.load(), - destination: target.get_entity().pos.load(), - speed: self.speed, - }); - } - self.update_countdown_ticks = 0; - self.cooldown = 0; + fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + // TODO: add missing fields like mob attacking to true and correct Navigation methods + + if let Some(target) = mob.get_mob_entity().target.lock().await.as_ref() { + let mut navigator = mob.get_mob_entity().navigator.lock().await; + navigator.set_progress(NavigatorGoal { + current_progress: mob.get_entity().pos.load(), + destination: target.get_entity().pos.load(), + speed: self.speed, + }); + } + self.update_countdown_ticks = 0; + self.cooldown = 0; + }) } - async fn stop(&mut self, mob: &dyn Mob) { - let mut target = mob.get_mob_entity().target.lock().await; - if target.is_none() { - return; - } - if !EntityPredicate::ExceptCreativeOrSpectator - .test(mob.get_entity()) - .await - { - *target = None; - } + fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + let mut target = mob.get_mob_entity().target.lock().await; + if target.is_none() { + return; + } - // TODO: set attacking to false and stop navigation + if !EntityPredicate::ExceptCreativeOrSpectator + .test(mob.get_entity()) + .await + { + *target = None; + } + + // TODO: set attacking to false and stop navigation + }) } - async fn tick(&mut self, mob: &dyn Mob) { - // TODO: implement - // This code is not Vanilla, tick method needs to be reimplemented - if let Some(target) = mob.get_mob_entity().target.lock().await.as_ref() { - let mut navigator = mob.get_mob_entity().navigator.lock().await; - navigator.set_progress(NavigatorGoal { - current_progress: mob.get_entity().pos.load(), - destination: target.get_entity().pos.load(), - speed: self.speed, - }); - } + fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + // TODO: implement + // This code is not Vanilla, tick method needs to be reimplemented + + if let Some(target) = mob.get_mob_entity().target.lock().await.as_ref() { + let mut navigator = mob.get_mob_entity().navigator.lock().await; + navigator.set_progress(NavigatorGoal { + current_progress: mob.get_entity().pos.load(), + destination: target.get_entity().pos.load(), + speed: self.speed, + }); + } + }) } fn should_run_every_tick(&self) -> bool { diff --git a/pumpkin/src/entity/ai/goal/mod.rs b/pumpkin/src/entity/ai/goal/mod.rs index bf9e37ad9..8b34b0dc1 100644 --- a/pumpkin/src/entity/ai/goal/mod.rs +++ b/pumpkin/src/entity/ai/goal/mod.rs @@ -1,6 +1,5 @@ use crate::entity::mob::Mob; -use async_trait::async_trait; -use std::{any::TypeId, ops::BitOr, ptr}; +use std::{any::TypeId, ops::BitOr, pin::Pin, ptr}; pub mod active_target_goal; pub mod ambient_stand_goal; @@ -18,18 +17,33 @@ pub fn to_goal_ticks(server_ticks: i32) -> i32 { -(-server_ticks).div_euclid(2) } -#[async_trait] +pub type GoalFuture<'a, T> = Pin + Send + 'a>>; + pub trait Goal: Send + Sync { /// How should the `Goal` initially start? - async fn can_start(&mut self, mob: &dyn Mob) -> bool; + fn can_start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { false }) + } + /// When it's started, how should it continue to run? - async fn should_continue(&self, mob: &dyn Mob) -> bool; + fn should_continue<'a>(&'a self, _mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { false }) + } + /// Call when goal start - async fn start(&mut self, _: &dyn Mob) {} + fn start<'a>(&'a mut self, _: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async {}) + } + /// Call when goal stop - async fn stop(&mut self, _: &dyn Mob) {} + fn stop<'a>(&'a mut self, _: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async {}) + } + /// If the `Goal` is running, this gets called every tick. - async fn tick(&mut self, _: &dyn Mob) {} + fn tick<'a>(&'a mut self, _: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async {}) + } fn should_run_every_tick(&self) -> bool { false @@ -125,33 +139,39 @@ impl PrioritizedGoal { } } -#[async_trait] impl Goal for PrioritizedGoal { - async fn can_start(&mut self, mob: &dyn Mob) -> bool { - self.goal.can_start(mob).await + fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { self.goal.can_start(mob).await }) } - async fn should_continue(&self, mob: &dyn Mob) -> bool { - self.goal.should_continue(mob).await + fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { self.goal.should_continue(mob).await }) } - async fn start(&mut self, mob: &dyn Mob) { - if !self.running { - self.running = true; - self.goal.start(mob).await; - } + fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + if !self.running { + self.running = true; + self.goal.start(mob).await; + } + }) } - async fn stop(&mut self, mob: &dyn Mob) { - if self.running { - self.running = false; - self.goal.stop(mob).await; - } + fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + if self.running { + self.running = false; + self.goal.stop(mob).await; + } + }) } - async fn tick(&mut self, mob: &dyn Mob) { - self.goal.tick(mob).await; + fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + self.goal.tick(mob).await; + }) } + fn should_run_every_tick(&self) -> bool { self.goal.should_run_every_tick() } diff --git a/pumpkin/src/entity/ai/goal/move_to_target_pos_goal.rs b/pumpkin/src/entity/ai/goal/move_to_target_pos_goal.rs index b5ae3d131..a9011f04c 100644 --- a/pumpkin/src/entity/ai/goal/move_to_target_pos_goal.rs +++ b/pumpkin/src/entity/ai/goal/move_to_target_pos_goal.rs @@ -1,10 +1,11 @@ use super::{Controls, Goal, to_goal_ticks}; +use crate::entity::ai::goal::GoalFuture; use crate::entity::{ai::goal::ParentHandle, mob::Mob}; use crate::world::World; -use async_trait::async_trait; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use rand::Rng; +use std::pin::Pin; use std::sync::Arc; const MIN_WAITING_TIME: i32 = 1200; @@ -121,66 +122,77 @@ impl MoveToTargetPosGoal { } // Contains overridable functions -#[async_trait] pub trait MoveToTargetPos: Send + Sync { - async fn is_target_pos(&self, world: Arc, block_pos: BlockPos) -> bool; + fn is_target_pos<'a>( + &'a self, + world: Arc, + block_pos: BlockPos, + ) -> Pin + Send + 'a>>; fn get_desired_distance_to_target(&self) -> f64 { 1.0 } } -#[async_trait] impl Goal for MoveToTargetPosGoal { - async fn can_start(&mut self, mob: &dyn Mob) -> bool { - if self.cooldown > 0 { - self.cooldown -= 1; - return false; - } - self.cooldown = Self::get_interval(mob); - self.find_target_pos(mob).await - } - - async fn should_continue(&self, mob: &dyn Mob) -> bool { - let world = &mob.get_entity().world; - let can_target = if let Some(x) = self.move_to_target_pos.get() { - x.is_target_pos(world.clone(), self.target_pos).await - } else { - false - }; - self.trying_time >= -self.safe_waiting_time - && self.trying_time <= MAX_TRYING_TIME - && can_target - } - - async fn start(&mut self, mob: &dyn Mob) { - Self::start_moving_to_target(mob); - self.trying_time = 0; - let random = mob.get_random().random_range(0..MIN_WAITING_TIME); - self.safe_waiting_time = - mob.get_random().random_range(random..MIN_WAITING_TIME) + MIN_WAITING_TIME; - } - - async fn tick(&mut self, mob: &dyn Mob) { - let block_pos = self.get_target_pos(); - let block_pos: Vector3 = block_pos.0.to_f64(); - let Some(move_to_target_pos) = self.move_to_target_pos.get() else { - return; - }; - let desired_distance = move_to_target_pos.get_desired_distance_to_target(); - if block_pos.squared_distance_to_vec(mob.get_entity().pos.load()) - < desired_distance * desired_distance - { - self.reached = true; - self.trying_time -= 1; - } else { - self.reached = false; - self.trying_time += 1; - if self.should_reset_path() { - // TODO: implement when navigation is implemented - // this.mob.getNavigation().startMovingTo(lv.getX() + 0.5, lv.getY(), lv.getZ() + 0.5, this.speed); + fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { + if self.cooldown > 0 { + self.cooldown -= 1; + return false; } - } + self.cooldown = Self::get_interval(mob); + self.find_target_pos(mob).await + }) + } + + fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { + let world = &mob.get_entity().world; + let can_target = if let Some(x) = self.move_to_target_pos.get() { + x.is_target_pos(world.clone(), self.target_pos).await + } else { + false + }; + self.trying_time >= -self.safe_waiting_time + && self.trying_time <= MAX_TRYING_TIME + && can_target + }) + } + + fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + Self::start_moving_to_target(mob); + self.trying_time = 0; + let random = mob.get_random().random_range(0..MIN_WAITING_TIME); + self.safe_waiting_time = + mob.get_random().random_range(random..MIN_WAITING_TIME) + MIN_WAITING_TIME; + }) + } + + fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + let block_pos = self.get_target_pos(); + let block_pos: Vector3 = block_pos.to_f64(); + let Some(move_to_target_pos) = self.move_to_target_pos.get() else { + return; + }; + let desired_distance = move_to_target_pos.get_desired_distance_to_target(); + + if block_pos.squared_distance_to_vec(mob.get_entity().pos.load()) + < desired_distance * desired_distance + { + self.reached = true; + self.trying_time -= 1; + } else { + self.reached = false; + self.trying_time += 1; + if self.should_reset_path() { + // TODO: implement when navigation is implemented + // this.mob.getNavigation().startMovingTo(lv.getX() + 0.5, lv.getY(), lv.getZ() + 0.5, this.speed); + } + } + }) } fn should_run_every_tick(&self) -> bool { diff --git a/pumpkin/src/entity/ai/goal/step_and_destroy_block_goal.rs b/pumpkin/src/entity/ai/goal/step_and_destroy_block_goal.rs index 87f4d9a8d..39b6186c0 100644 --- a/pumpkin/src/entity/ai/goal/step_and_destroy_block_goal.rs +++ b/pumpkin/src/entity/ai/goal/step_and_destroy_block_goal.rs @@ -1,12 +1,12 @@ use super::{Controls, Goal, to_goal_ticks}; -use crate::entity::ai::goal::ParentHandle; use crate::entity::ai::goal::move_to_target_pos_goal::{MoveToTargetPos, MoveToTargetPosGoal}; +use crate::entity::ai::goal::{GoalFuture, ParentHandle}; use crate::entity::mob::Mob; use crate::world::World; -use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; +use std::pin::Pin; use std::sync::Arc; const MAX_COOLDOWN: i32 = 20; @@ -73,95 +73,110 @@ impl StepAndDestroyBlockGoal { } } -// Contains overridable functions -#[async_trait] -pub trait Stepping: Send + Sync { - async fn tick_stepping(&self, _world: Arc, _block_pos: BlockPos) {} +pub type SteppingFuture<'a> = Pin + Send + 'a>>; - async fn on_destroy_block(&self, _world: Arc, _block_pos: BlockPos) {} +pub trait Stepping: Send + Sync { + fn tick_stepping(&self, _world: Arc, _block_pos: BlockPos) -> SteppingFuture<'_> { + Box::pin(async {}) + } + + fn on_destroy_block(&self, _world: Arc, _block_pos: BlockPos) -> SteppingFuture<'_> { + Box::pin(async {}) + } } -#[async_trait] -impl Goal for StepAndDestroyBlockGoal { - async fn can_start(&mut self, mob: &dyn Mob) -> bool { - let world = &mob.get_entity().world; - let level_info = world.level_info.read().await; - if !level_info.game_rules.mob_griefing { - false - } else if self.move_to_target_pos_goal.cooldown > 0 { - self.move_to_target_pos_goal.cooldown -= 1; - false - } else if self.move_to_target_pos_goal.find_target_pos(mob).await { - self.move_to_target_pos_goal.cooldown = to_goal_ticks(MAX_COOLDOWN); - true - } else { - self.move_to_target_pos_goal.cooldown = MoveToTargetPosGoal::::get_interval(mob); - false - } +impl Goal + for StepAndDestroyBlockGoal +{ + fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { + let world = &mob.get_entity().world; + let level_info = world.level_info.read().await; + if !level_info.game_rules.mob_griefing { + false + } else if self.move_to_target_pos_goal.cooldown > 0 { + self.move_to_target_pos_goal.cooldown -= 1; + false + } else if self.move_to_target_pos_goal.find_target_pos(mob).await { + self.move_to_target_pos_goal.cooldown = to_goal_ticks(MAX_COOLDOWN); + true + } else { + self.move_to_target_pos_goal.cooldown = MoveToTargetPosGoal::::get_interval(mob); + false + } + }) } - async fn should_continue(&self, mob: &dyn Mob) -> bool { - self.move_to_target_pos_goal.should_continue(mob).await + fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { self.move_to_target_pos_goal.should_continue(mob).await }) } - async fn start(&mut self, mob: &dyn Mob) { - self.move_to_target_pos_goal.start(mob).await; - self.counter = 0; + fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + self.move_to_target_pos_goal.start(mob).await; + self.counter = 0; + }) } - async fn stop(&mut self, mob: &dyn Mob) { - mob.get_mob_entity().living_entity.fall_distance.store(1.0); + fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + // Assuming fall_distance is AtomicF32/f32 + mob.get_mob_entity().living_entity.fall_distance.store(1.0); + }) } - async fn tick(&mut self, mob: &dyn Mob) { - self.move_to_target_pos_goal.tick(mob).await; - let mob_entity = mob.get_mob_entity(); - let world = &mob.get_entity().world; - let block_pos = mob.get_entity().block_pos.load(); - let tweak_pos = self.tweak_to_proper_pos(block_pos, world.clone()).await; - // TODO: drop world? - if !self.move_to_target_pos_goal.reached || tweak_pos.is_none() { - return; - } - let tweak_pos = tweak_pos.unwrap(); - let counter = self.counter; + fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + self.move_to_target_pos_goal.tick(mob).await; + let mob_entity = mob.get_mob_entity(); + let world = &mob.get_entity().world; + let block_pos = mob.get_entity().block_pos.load(); - if counter > 0 { - let velocity = mob_entity.living_entity.entity.velocity.load(); - mob_entity - .living_entity - .entity - .set_velocity(Vector3::new(velocity.x, 0.3, velocity.z)) - .await; - // TODO: spawn particles - } + let tweak_pos = self.tweak_to_proper_pos(block_pos, world.clone()).await; + // TODO: drop world? + if !self.move_to_target_pos_goal.reached || tweak_pos.is_none() { + return; + } + let tweak_pos = tweak_pos.unwrap(); + let counter = self.counter; - if counter % 2 == 0 { - let velocity = mob_entity.living_entity.entity.velocity.load(); - mob_entity - .living_entity - .entity - .set_velocity(Vector3::new(velocity.x, -0.3, velocity.z)) - .await; - if counter % 6 == 0 { - if let Some(stepping) = self.stepping.get() { - stepping - .tick_stepping(world.clone(), self.move_to_target_pos_goal.target_pos) - .await; - } else { - self.tick_stepping(world.clone(), self.move_to_target_pos_goal.target_pos) - .await; + if counter > 0 { + let velocity = mob_entity.living_entity.entity.velocity.load(); + mob_entity + .living_entity + .entity + .set_velocity(Vector3::new(velocity.x, 0.3, velocity.z)) + .await; + // TODO: spawn particles + } + + if counter % 2 == 0 { + let velocity = mob_entity.living_entity.entity.velocity.load(); + mob_entity + .living_entity + .entity + .set_velocity(Vector3::new(velocity.x, -0.3, velocity.z)) + .await; + if counter % 6 == 0 { + if let Some(stepping) = self.stepping.get() { + stepping + .tick_stepping(world.clone(), self.move_to_target_pos_goal.target_pos) + .await; + } else { + self.tick_stepping(world.clone(), self.move_to_target_pos_goal.target_pos) + .await; + } } } - } - if counter > 60 { - // TODO: world.removeBlock HOW? - // TODO: spawn particles - self.on_destroy_block(world.clone(), tweak_pos).await; - } + if counter > 60 { + // TODO: world.removeBlock HOW? + // TODO: spawn particles + self.on_destroy_block(world.clone(), tweak_pos).await; + } - self.counter += 1; + self.counter += 1; + }) } fn should_run_every_tick(&self) -> bool { @@ -173,17 +188,33 @@ impl Goal for StepAndDestroyBlockGoal { } } -#[async_trait] -impl MoveToTargetPos for StepAndDestroyBlockGoal { - async fn is_target_pos(&self, world: Arc, block_pos: BlockPos) -> bool { - world.get_block(&block_pos).await.id == self.target_block.id - && world.get_block_state(&block_pos.up()).await.is_air() - && world - .get_block_state(&block_pos.up_height(2)) - .await - .is_air() +impl MoveToTargetPos + for StepAndDestroyBlockGoal +{ + fn is_target_pos<'a>( + &'a self, + world: Arc, + block_pos: BlockPos, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + world.get_block(&block_pos).await.id == self.target_block.id + && world.get_block_state(&block_pos.up()).await.is_air() + && world + .get_block_state(&block_pos.up_height(2)) + .await + .is_air() + }) } } -#[async_trait] -impl Stepping for StepAndDestroyBlockGoal {} +impl Stepping + for StepAndDestroyBlockGoal +{ + fn tick_stepping(&self, _world: Arc, _block_pos: BlockPos) -> SteppingFuture<'_> { + Box::pin(async {}) + } + + fn on_destroy_block(&self, _world: Arc, _block_pos: BlockPos) -> SteppingFuture<'_> { + Box::pin(async {}) + } +} diff --git a/pumpkin/src/entity/ai/goal/track_target_goal.rs b/pumpkin/src/entity/ai/goal/track_target_goal.rs index d0aa4081f..e72afd9c1 100644 --- a/pumpkin/src/entity/ai/goal/track_target_goal.rs +++ b/pumpkin/src/entity/ai/goal/track_target_goal.rs @@ -1,9 +1,9 @@ use super::{Controls, Goal, to_goal_ticks}; +use crate::entity::ai::goal::GoalFuture; use crate::entity::ai::target_predicate::TargetPredicate; use crate::entity::living::LivingEntity; use crate::entity::mob::Mob; use crate::entity::{EntityBase, mob::MobEntity}; -use async_trait::async_trait; use rand::Rng; use std::sync::Arc; @@ -99,38 +99,47 @@ impl TrackTargetGoal { } } -#[async_trait] impl Goal for TrackTargetGoal { - async fn can_start(&mut self, _mob: &dyn Mob) -> bool { - false + fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { + let mob = mob.get_mob_entity(); + let mob_target = mob.target.lock().await; + + // We need to decide which target to use for the check + let target = if mob_target.is_some() { + (*mob_target).clone() + } else { + self.target.clone() + }; + + // Drop the guard immediately after access to release the lock + drop(mob_target); + + if target.is_none() { + return false; + } // TODO: continue when scoreboard team are implemented + true + }) } - async fn should_continue(&self, mob: &dyn Mob) -> bool { - let mob = mob.get_mob_entity(); - let mob_target = mob.target.lock().await; - let target = if mob_target.is_some() { - mob_target.clone() - } else { - self.target.clone() - }; - drop(mob_target); - - if target.is_none() { - return false; - } // TODO: continue when scoreboard team are implemented - true + fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + self.can_navigate_flag = 0; + self.check_can_navigate_cooldown = 0; + self.time_without_visibility = 0; + // No await needed here + }) } - async fn start(&mut self, _mob: &dyn Mob) { - self.can_navigate_flag = 0; - self.check_can_navigate_cooldown = 0; - self.time_without_visibility = 0; - } + fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + let mob = mob.get_mob_entity(); - async fn stop(&mut self, mob: &dyn Mob) { - let mob = mob.get_mob_entity(); - *mob.target.lock().await = None; - self.target = None; + let mut mob_target = mob.target.lock().await; + *mob_target = None; + + self.target = None; + }) } fn controls(&self) -> Controls { diff --git a/pumpkin/src/entity/ai/goal/zombie_attack_goal.rs b/pumpkin/src/entity/ai/goal/zombie_attack_goal.rs index 14f69a688..0ceb89761 100644 --- a/pumpkin/src/entity/ai/goal/zombie_attack_goal.rs +++ b/pumpkin/src/entity/ai/goal/zombie_attack_goal.rs @@ -1,7 +1,7 @@ use super::{Controls, Goal}; +use crate::entity::ai::goal::GoalFuture; use crate::entity::ai::goal::melee_attack_goal::MeleeAttackGoal; use crate::entity::mob::Mob; -use async_trait::async_trait; pub struct ZombieAttackGoal { melee_attack_goal: MeleeAttackGoal, @@ -18,36 +18,44 @@ impl ZombieAttackGoal { } } -#[async_trait] impl Goal for ZombieAttackGoal { - async fn can_start(&mut self, mob: &dyn Mob) -> bool { - self.melee_attack_goal.can_start(mob).await + fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { self.melee_attack_goal.can_start(mob).await }) } - async fn should_continue(&self, mob: &dyn Mob) -> bool { - self.melee_attack_goal.should_continue(mob).await + fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { self.melee_attack_goal.should_continue(mob).await }) } - async fn start(&mut self, mob: &dyn Mob) { - self.melee_attack_goal.start(mob).await; - self.ticks = 0; + fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + self.melee_attack_goal.start(mob).await; + self.ticks = 0; + }) } - async fn stop(&mut self, mob: &dyn Mob) { - self.melee_attack_goal.stop(mob).await; - mob.get_mob_entity().set_attacking(false); - } - - async fn tick(&mut self, mob: &dyn Mob) { - self.melee_attack_goal.tick(mob).await; - self.ticks += 1; - if self.ticks >= 5 - && self.melee_attack_goal.cooldown < self.melee_attack_goal.get_max_cooldown() - { - mob.get_mob_entity().set_attacking(true); - } else { + fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + self.melee_attack_goal.stop(mob).await; + // Assuming set_attacking is synchronous mob.get_mob_entity().set_attacking(false); - } + }) + } + + fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + self.melee_attack_goal.tick(mob).await; + self.ticks += 1; + + // Note: Accessing cooldown/get_max_cooldown is assumed synchronous. + if self.ticks >= 5 + && self.melee_attack_goal.cooldown < self.melee_attack_goal.get_max_cooldown() + { + mob.get_mob_entity().set_attacking(true); + } else { + mob.get_mob_entity().set_attacking(false); + } + }) } fn should_run_every_tick(&self) -> bool { diff --git a/pumpkin/src/entity/decoration/armor_stand.rs b/pumpkin/src/entity/decoration/armor_stand.rs index c851f25e0..5337af78d 100644 --- a/pumpkin/src/entity/decoration/armor_stand.rs +++ b/pumpkin/src/entity/decoration/armor_stand.rs @@ -4,10 +4,8 @@ use std::sync::{ }; use crate::entity::{ - Entity, EntityBase, NBTStorage, - living::{LivingEntity, LivingEntityTrait}, + Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture, living::LivingEntity, }; -use async_trait::async_trait; use crossbeam::atomic::AtomicCell; use pumpkin_data::{ damage::DamageType, @@ -220,77 +218,74 @@ impl ArmorStandEntity { } } -#[async_trait] impl NBTStorage for ArmorStandEntity { - async fn write_nbt(&self, nbt: &mut NbtCompound) { - let disabled_slots = self.disabled_slots.load(Ordering::Relaxed); + fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async { + let disabled_slots = self.disabled_slots.load(Ordering::Relaxed); - nbt.put_bool("Invisible", self.is_invisible()); - nbt.put_bool("Small", self.is_small()); - nbt.put_bool("ShowArms", self.should_show_arms()); - nbt.put_int("DisabledSlots", disabled_slots); - nbt.put_bool("NoBasePlate", !self.should_show_base_plate()); - if self.is_marker() { - nbt.put_bool("Marker", true); - } + nbt.put_bool("Invisible", self.is_invisible()); + nbt.put_bool("Small", self.is_small()); + nbt.put_bool("ShowArms", self.should_show_arms()); + nbt.put_int("DisabledSlots", disabled_slots); + nbt.put_bool("NoBasePlate", !self.should_show_base_plate()); + if self.is_marker() { + nbt.put_bool("Marker", true); + } - nbt.put("Pose", self.pack_rotation()); + nbt.put("Pose", self.pack_rotation()); + }) } - async fn read_nbt_non_mut(&self, nbt: &NbtCompound) { - let mut flags = 0u8; + fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async { + let mut flags = 0u8; - if let Some(invisible) = nbt.get_bool("Invisible") - && invisible - { - self.get_entity().set_invisible(invisible).await; - } + if let Some(invisible) = nbt.get_bool("Invisible") + && invisible + { + self.get_entity().set_invisible(invisible).await; + } - if let Some(small) = nbt.get_bool("Small") - && small - { - flags |= ArmorStandFlags::Small as u8; - } + if let Some(small) = nbt.get_bool("Small") + && small + { + flags |= ArmorStandFlags::Small as u8; + } - if let Some(show_arms) = nbt.get_bool("ShowArms") - && show_arms - { - flags |= ArmorStandFlags::ShowArms as u8; - } + if let Some(show_arms) = nbt.get_bool("ShowArms") + && show_arms + { + flags |= ArmorStandFlags::ShowArms as u8; + } - if let Some(disabled_slots) = nbt.get_int("DisabledSlots") { - self.disabled_slots.store(disabled_slots, Ordering::Relaxed); - } + if let Some(disabled_slots) = nbt.get_int("DisabledSlots") { + self.disabled_slots.store(disabled_slots, Ordering::Relaxed); + } - if let Some(no_base_plate) = nbt.get_bool("NoBasePlate") { - if !no_base_plate { + if let Some(no_base_plate) = nbt.get_bool("NoBasePlate") { + if !no_base_plate { + flags |= ArmorStandFlags::HideBasePlate as u8; + } + } else { flags |= ArmorStandFlags::HideBasePlate as u8; } - } else { - flags |= ArmorStandFlags::HideBasePlate as u8; - } - if let Some(marker) = nbt.get_bool("Marker") - && marker - { - flags |= ArmorStandFlags::Marker as u8; - } + if let Some(marker) = nbt.get_bool("Marker") + && marker + { + flags |= ArmorStandFlags::Marker as u8; + } - self.armor_stand_flags.store(flags, Ordering::Relaxed); + self.armor_stand_flags.store(flags, Ordering::Relaxed); - if let Some(pose_tag) = nbt.get("Pose") { - let packed: PackedRotation = pose_tag.clone().into(); - self.unpack_rotation(&packed); - } + if let Some(pose_tag) = nbt.get("Pose") { + let packed: PackedRotation = pose_tag.clone().into(); + self.unpack_rotation(&packed); + } + }) } } -#[async_trait] -impl LivingEntityTrait for ArmorStandEntity { - //async fn on_actually_hurt() -} - -#[async_trait] impl EntityBase for ArmorStandEntity { fn get_entity(&self) -> &Entity { &self.living_entity.entity @@ -304,59 +299,90 @@ impl EntityBase for ArmorStandEntity { self } - async fn damage_with_context( - &self, + fn damage_with_context<'a>( + &'a self, caller: Arc, _amount: f32, damage_type: DamageType, _position: Option>, - source: Option<&dyn EntityBase>, - _cause: Option<&dyn EntityBase>, - ) -> bool { - let entity = self.get_entity(); - if entity.is_removed() { - return false; - } - - let world = &entity.world; - - let mob_griefing_gamerule = { - let game_rules = &world.level_info.read().await.game_rules; - game_rules.mob_griefing - }; - - if !mob_griefing_gamerule && source.is_some_and(|source| source.get_player().is_none()) { - return false; - } - - // TODO: .isIn(DamageTypeTags::BYPASSES_INVULNERABILITY) - - if damage_type == DamageType::EXPLOSION { - // TODO: Implement Dropping Items that are in the Equipment Slots & entity.kill() - self.on_break(entity).await; - entity.kill(caller).await; - //entity.remove().await; - return false; - } // TODO: Implement .isIn(DamageTypeTags::IGNITES_ARMOR_STANDS) - - // TODO: Implement .isIn(DamageTypeTags::BURNS_ARMOR_STANDS) - - /* // TODO: - bl1: bool = .isIn(DamageTypeTags.CAN_BREAK_ARMOR_STAND); - bl2: bool = .isIn(DamageTypeTags.ALWAYS_KILLS_ARMOR_STANDS); - - if !bl1 && !bl2 { - return false; - } - */ - - let Some(source) = source else { return false }; - - // TODO: source is not giving the real player or wrong stuff cause .is_creative() is false even tho the player is in creative. - if let Some(player) = source.get_player() { - if !player.abilities.lock().await.allow_modify_world { + source: Option<&'a dyn EntityBase>, + _cause: Option<&'a dyn EntityBase>, + ) -> EntityBaseFuture<'a, bool> { + Box::pin(async move { + let entity = self.get_entity(); + if entity.is_removed() { return false; - } else if player.is_creative() { + } + + let world = &entity.world; + + let mob_griefing_gamerule = { + let game_rules = &world.level_info.read().await.game_rules; + game_rules.mob_griefing + }; + + if !mob_griefing_gamerule && source.is_some_and(|source| source.get_player().is_none()) + { + return false; + } + + // TODO: .isIn(DamageTypeTags::BYPASSES_INVULNERABILITY) + + if damage_type == DamageType::EXPLOSION { + // TODO: Implement Dropping Items that are in the Equipment Slots & entity.kill() + self.on_break(entity).await; + entity.kill(caller).await; + //entity.remove().await; + return false; + } // TODO: Implement .isIn(DamageTypeTags::IGNITES_ARMOR_STANDS) + + // TODO: Implement .isIn(DamageTypeTags::BURNS_ARMOR_STANDS) + + /* // TODO: + bl1: bool = .isIn(DamageTypeTags.CAN_BREAK_ARMOR_STAND); + bl2: bool = .isIn(DamageTypeTags.ALWAYS_KILLS_ARMOR_STANDS); + + if !bl1 && !bl2 { + return false; + } + */ + + let Some(source) = source else { return false }; + + // TODO: source is not giving the real player or wrong stuff cause .is_creative() is false even tho the player is in creative. + if let Some(player) = source.get_player() { + if !player.abilities.lock().await.allow_modify_world { + return false; + } else if player.is_creative() { + world + .play_sound( + Sound::EntityArmorStandBreak, + SoundCategory::Neutral, + &entity.block_pos.load().to_f64(), + ) + .await; + self.break_and_drop_items().await; + entity.kill(caller).await; + return true; + } + } + + let time = world.level_time.lock().await.query_gametime(); + + if time - self.last_hit_time.load(Ordering::Relaxed) > 5 { + // && !bl2 { + world + .send_entity_status(entity, EntityStatus::HitArmorStand) + .await; + world + .play_sound( + Sound::EntityArmorStandHit, + SoundCategory::Neutral, + &entity.block_pos.load().to_f64(), + ) + .await; + self.last_hit_time.store(time, Ordering::Relaxed); + } else { world .play_sound( Sound::EntityArmorStandBreak, @@ -366,38 +392,10 @@ impl EntityBase for ArmorStandEntity { .await; self.break_and_drop_items().await; entity.kill(caller).await; - return true; } - } - let time = world.level_time.lock().await.query_gametime(); - - if time - self.last_hit_time.load(Ordering::Relaxed) > 5 { - // && !bl2 { - world - .send_entity_status(entity, EntityStatus::HitArmorStand) - .await; - world - .play_sound( - Sound::EntityArmorStandHit, - SoundCategory::Neutral, - &entity.block_pos.load().to_f64(), - ) - .await; - self.last_hit_time.store(time, Ordering::Relaxed); - } else { - world - .play_sound( - Sound::EntityArmorStandBreak, - SoundCategory::Neutral, - &entity.block_pos.load().to_f64(), - ) - .await; - self.break_and_drop_items().await; - entity.kill(caller).await; - } - - true + true + }) } } diff --git a/pumpkin/src/entity/decoration/end_crystal.rs b/pumpkin/src/entity/decoration/end_crystal.rs index e7183a431..953b65b34 100644 --- a/pumpkin/src/entity/decoration/end_crystal.rs +++ b/pumpkin/src/entity/decoration/end_crystal.rs @@ -1,10 +1,8 @@ use core::f32; use std::sync::Arc; -use crate::entity::{Entity, EntityBase, NBTStorage, living::LivingEntity}; -use async_trait::async_trait; +use crate::entity::{Entity, EntityBase, EntityBaseFuture, NBTStorage, living::LivingEntity}; use pumpkin_data::damage::DamageType; -use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::java::client::play::{MetaDataType, Metadata}; use pumpkin_util::math::vector3::Vector3; @@ -26,14 +24,8 @@ impl EndCrystalEntity { } } -#[async_trait] -impl NBTStorage for EndCrystalEntity { - async fn write_nbt(&self, _nbt: &mut NbtCompound) {} +impl NBTStorage for EndCrystalEntity {} - async fn read_nbt_non_mut(&self, _nbt: &NbtCompound) {} -} - -#[async_trait] impl EntityBase for EndCrystalEntity { fn get_entity(&self) -> &Entity { &self.entity @@ -43,22 +35,24 @@ impl EntityBase for EndCrystalEntity { None } - async fn damage_with_context( - &self, + fn damage_with_context<'a>( + &'a self, _caller: Arc, _amount: f32, damage_type: DamageType, _position: Option>, - _source: Option<&dyn EntityBase>, - _cause: Option<&dyn EntityBase>, - ) -> bool { - if damage_type != DamageType::EXPLOSION { - self.entity.world.explode(self.entity.pos.load(), 6.0).await; - } + _source: Option<&'a dyn EntityBase>, + _cause: Option<&'a dyn EntityBase>, + ) -> EntityBaseFuture<'a, bool> { + Box::pin(async move { + if damage_type != DamageType::EXPLOSION { + self.entity.world.explode(self.entity.pos.load(), 6.0).await; + } - // TODO - self.entity.remove().await; - true + // TODO + self.entity.remove().await; + true + }) } fn as_nbt_storage(&self) -> &dyn NBTStorage { diff --git a/pumpkin/src/entity/decoration/painting.rs b/pumpkin/src/entity/decoration/painting.rs index 580b346ad..4b01491f0 100644 --- a/pumpkin/src/entity/decoration/painting.rs +++ b/pumpkin/src/entity/decoration/painting.rs @@ -1,8 +1,9 @@ use core::f32; use std::sync::{Arc, atomic::Ordering}; -use crate::entity::{Entity, EntityBase, NBTStorage, living::LivingEntity}; -use async_trait::async_trait; +use crate::entity::{ + Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture, living::LivingEntity, +}; use pumpkin_data::damage::DamageType; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::vector3::Vector3; @@ -17,19 +18,21 @@ impl PaintingEntity { } } -#[async_trait] impl NBTStorage for PaintingEntity { - async fn write_nbt(&self, nbt: &mut NbtCompound) { - nbt.put_byte("facing", self.entity.data.load(Ordering::Relaxed) as i8); + fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async { + nbt.put_byte("facing", self.entity.data.load(Ordering::Relaxed) as i8); + }) } - async fn read_nbt_non_mut(&self, _nbt: &NbtCompound) { - // TODO - self.entity.data.store(3, Ordering::Relaxed); + fn read_nbt_non_mut<'a>(&'a self, _nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async { + // TODO + self.entity.data.store(3, Ordering::Relaxed); + }) } } -#[async_trait] impl EntityBase for PaintingEntity { fn get_entity(&self) -> &Entity { &self.entity @@ -39,18 +42,20 @@ impl EntityBase for PaintingEntity { None } - async fn damage_with_context( - &self, + fn damage_with_context<'a>( + &'a self, _caller: Arc, _amount: f32, _damage_type: DamageType, _position: Option>, - _source: Option<&dyn EntityBase>, - _cause: Option<&dyn EntityBase>, - ) -> bool { - // TODO - self.entity.remove().await; - true + _source: Option<&'a dyn EntityBase>, + _cause: Option<&'a dyn EntityBase>, + ) -> EntityBaseFuture<'a, bool> { + Box::pin(async { + // TODO + self.entity.remove().await; + true + }) } fn as_nbt_storage(&self) -> &dyn NBTStorage { diff --git a/pumpkin/src/entity/effect/mod.rs b/pumpkin/src/entity/effect/mod.rs index 039d077b0..5ea57f458 100644 --- a/pumpkin/src/entity/effect/mod.rs +++ b/pumpkin/src/entity/effect/mod.rs @@ -1,56 +1,60 @@ -use crate::entity::{NBTStorage, NBTStorageInit}; -use async_trait::async_trait; +use crate::entity::{NBTInitFuture, NBTStorage, NBTStorageInit, NbtFuture}; use pumpkin_data::effect::StatusEffect; use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::tag::NbtTag; -#[async_trait] impl NBTStorage for pumpkin_data::potion::Effect { - async fn write_nbt(&self, nbt: &mut NbtCompound) { - nbt.put("id", self.effect_type.minecraft_name); - if self.amplifier > 0 { - nbt.put("amplifier", NbtTag::Int(i32::from(self.amplifier))); - } - nbt.put("duration", NbtTag::Int(self.duration)); - if self.ambient { - nbt.put("ambient", NbtTag::Byte(1)); - } - if !self.show_particles { - nbt.put("show_particles", NbtTag::Byte(0)); - } - let show_icon: i8 = i8::from(self.show_icon); - nbt.put("show_icon", NbtTag::Byte(show_icon)); - } -} - -#[async_trait] -impl NBTStorageInit for pumpkin_data::potion::Effect { - async fn create_from_nbt(nbt: &mut NbtCompound) -> Option { - let Some(effect_id) = nbt.get_string("id") else { - log::warn!("Unable to read effect. Effect id is not present"); - return None; - }; - let Some(effect_type) = StatusEffect::from_minecraft_name(effect_id) else { - log::warn!("Unable to read effect. Unknown effect type: {effect_id}"); - return None; - }; - let Some(show_icon) = nbt.get_byte("show_icon") else { - log::warn!("Unable to read effect. Show icon is not present"); - return None; - }; - let amplifier = nbt.get_int("amplifier").unwrap_or(0) as u8; - let duration = nbt.get_int("duration").unwrap_or(0); - let ambient = nbt.get_byte("ambient").unwrap_or(0) == 1; - let show_particles = nbt.get_byte("show_particles").unwrap_or(1) == 1; - let show_icon = show_icon == 1; - Some(Self { - effect_type, - duration, - amplifier, - ambient, - show_particles, - show_icon, - blend: false, + fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async { + nbt.put("id", self.effect_type.minecraft_name); + if self.amplifier > 0 { + nbt.put("amplifier", NbtTag::Int(i32::from(self.amplifier))); + } + nbt.put("duration", NbtTag::Int(self.duration)); + if self.ambient { + nbt.put("ambient", NbtTag::Byte(1)); + } + if !self.show_particles { + nbt.put("show_particles", NbtTag::Byte(0)); + } + let show_icon: i8 = i8::from(self.show_icon); + nbt.put("show_icon", NbtTag::Byte(show_icon)); + }) + } +} + +impl NBTStorageInit for pumpkin_data::potion::Effect { + fn create_from_nbt<'a>(nbt: &'a mut NbtCompound) -> NBTInitFuture<'a, Self> + where + Self: 'a, + { + Box::pin(async move { + let Some(effect_id) = nbt.get_string("id") else { + log::warn!("Unable to read effect. Effect id is not present"); + return None; + }; + let Some(effect_type) = StatusEffect::from_minecraft_name(effect_id) else { + log::warn!("Unable to read effect. Unknown effect type: {effect_id}"); + return None; + }; + let Some(show_icon) = nbt.get_byte("show_icon") else { + log::warn!("Unable to read effect. Show icon is not present"); + return None; + }; + let amplifier = nbt.get_int("amplifier").unwrap_or(0) as u8; + let duration = nbt.get_int("duration").unwrap_or(0); + let ambient = nbt.get_byte("ambient").unwrap_or(0) == 1; + let show_particles = nbt.get_byte("show_particles").unwrap_or(1) == 1; + let show_icon = show_icon == 1; + Some(Self { + effect_type, + duration, + amplifier, + ambient, + show_particles, + show_icon, + blend: false, + }) }) } } diff --git a/pumpkin/src/entity/experience_orb.rs b/pumpkin/src/entity/experience_orb.rs index aa7e1a062..791ea5fec 100644 --- a/pumpkin/src/entity/experience_orb.rs +++ b/pumpkin/src/entity/experience_orb.rs @@ -4,12 +4,11 @@ use std::sync::{ atomic::{AtomicU32, Ordering}, }; -use async_trait::async_trait; use pumpkin_data::entity::EntityType; use pumpkin_util::math::vector3::Vector3; use uuid::Uuid; -use crate::{server::Server, world::World}; +use crate::{entity::EntityBaseFuture, server::Server, world::World}; use super::{Entity, EntityBase, NBTStorage, living::LivingEntity, player::Player}; @@ -75,54 +74,61 @@ impl ExperienceOrbEntity { impl NBTStorage for ExperienceOrbEntity {} -#[async_trait] impl EntityBase for ExperienceOrbEntity { - async fn tick(&self, caller: Arc, server: &Server) { - let entity = &self.entity; - entity.tick(caller.clone(), server).await; - let bounding_box = entity.bounding_box.load(); + fn tick<'a>( + &'a self, + caller: Arc, + server: &'a Server, + ) -> EntityBaseFuture<'a, ()> { + Box::pin(async move { + let entity = &self.entity; + entity.tick(caller.clone(), server).await; + let bounding_box = entity.bounding_box.load(); - let original_velo = entity.velocity.load(); + let original_velo = entity.velocity.load(); - let mut velo = original_velo; + let mut velo = original_velo; - let no_clip = !self - .entity - .world - .is_space_empty(bounding_box.expand(-1.0e-7, -1.0e-7, -1.0e-7)) - .await; - // TODO: isSubmergedIn - if !no_clip { - velo.y -= self.get_gravity(); - } + let no_clip = !self + .entity + .world + .is_space_empty(bounding_box.expand(-1.0e-7, -1.0e-7, -1.0e-7)) + .await; + // TODO: isSubmergedIn + if !no_clip { + velo.y -= self.get_gravity(); + } - entity.velocity.store(velo); + entity.velocity.store(velo); - entity.move_entity(caller.clone(), velo).await; + entity.move_entity(caller.clone(), velo).await; - entity.tick_block_collisions(&caller, server).await; + entity.tick_block_collisions(&caller, server).await; - let age = self.orb_age.fetch_add(1, Ordering::Relaxed); - if age >= 6000 { - self.entity.remove().await; - } + let age = self.orb_age.fetch_add(1, Ordering::Relaxed); + if age >= 6000 { + self.entity.remove().await; + } + }) } fn get_entity(&self) -> &Entity { &self.entity } - async fn on_player_collision(&self, player: &Arc) { - if player.living_entity.health.load() > 0.0 { - let mut delay = player.experience_pick_up_delay.lock().await; - if *delay == 0 { - *delay = 2; - player.living_entity.pickup(&self.entity, 1).await; - player.add_experience_points(self.amount as i32).await; - // TODO: pickingCount for merging - self.entity.remove().await; + fn on_player_collision<'a>(&'a self, player: &'a Arc) -> EntityBaseFuture<'a, ()> { + Box::pin(async move { + if player.living_entity.health.load() > 0.0 { + let mut delay = player.experience_pick_up_delay.lock().await; + if *delay == 0 { + *delay = 2; + player.living_entity.pickup(&self.entity, 1).await; + player.add_experience_points(self.amount as i32).await; + // TODO: pickingCount for merging + self.entity.remove().await; + } } - } + }) } fn get_living_entity(&self) -> Option<&LivingEntity> { diff --git a/pumpkin/src/entity/falling.rs b/pumpkin/src/entity/falling.rs index 73556becc..05650765a 100644 --- a/pumpkin/src/entity/falling.rs +++ b/pumpkin/src/entity/falling.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::entity::EntityType; use pumpkin_protocol::java::client::play::{MetaDataType, Metadata}; @@ -8,7 +7,7 @@ use std::sync::{Arc, atomic::Ordering}; use uuid::Uuid; use crate::{ - entity::{Entity, EntityBase, NBTStorage, living::LivingEntity}, + entity::{Entity, EntityBase, EntityBaseFuture, NBTStorage, living::LivingEntity}, server::Server, world::World, }; @@ -53,48 +52,55 @@ impl FallingEntity { impl NBTStorage for FallingEntity {} -#[async_trait] impl EntityBase for FallingEntity { - async fn tick(&self, caller: Arc, server: &Server) { - let entity = &self.entity; - entity.tick(caller.clone(), server).await; + fn tick<'a>( + &'a self, + caller: Arc, + server: &'a Server, + ) -> EntityBaseFuture<'a, ()> { + Box::pin(async move { + let entity = &self.entity; + entity.tick(caller.clone(), server).await; - let original_velo = entity.velocity.load(); - let mut velo = original_velo; - velo.y -= self.get_gravity(); + let original_velo = entity.velocity.load(); + let mut velo = original_velo; + velo.y -= self.get_gravity(); - entity.velocity.store(velo); + entity.velocity.store(velo); - entity.move_entity(caller.clone(), velo).await; - entity.tick_block_collisions(&caller, server).await; - if entity.on_ground.load(Ordering::Relaxed) { - entity.velocity.store(velo.multiply(0.7, -0.5, 0.7)); - entity - .world - .set_block_state( - &self.entity.block_pos.load(), - self.block_state_id, - BlockFlags::NOTIFY_ALL, - ) - .await; - entity.remove().await; - } + entity.move_entity(caller.clone(), velo).await; + entity.tick_block_collisions(&caller, server).await; + if entity.on_ground.load(Ordering::Relaxed) { + entity.velocity.store(velo.multiply(0.7, -0.5, 0.7)); + entity + .world + .set_block_state( + &self.entity.block_pos.load(), + self.block_state_id, + BlockFlags::NOTIFY_ALL, + ) + .await; + entity.remove().await; + } - entity.velocity.store(velo.multiply(0.98, 0.98, 0.98)); + entity.velocity.store(velo.multiply(0.98, 0.98, 0.98)); - entity.send_pos_rot().await; + entity.send_pos_rot().await; - entity.send_velocity().await; + entity.send_velocity().await; + }) } - async fn init_data_tracker(&self) { - self.entity - .send_meta_data(&[Metadata::new( - 8, - MetaDataType::BlockPos, - self.entity.block_pos.load(), - )]) - .await; + fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { + Box::pin(async move { + self.entity + .send_meta_data(&[Metadata::new( + 8, + MetaDataType::BlockPos, + self.entity.block_pos.load(), + )]) + .await; + }) } fn get_entity(&self) -> &Entity { diff --git a/pumpkin/src/entity/hunger.rs b/pumpkin/src/entity/hunger.rs index 2a0d1e370..901d84284 100644 --- a/pumpkin/src/entity/hunger.rs +++ b/pumpkin/src/entity/hunger.rs @@ -1,7 +1,8 @@ use std::sync::Arc; +use crate::entity::NbtFuture; + use super::{EntityBase, NBTStorage, NBTStorageInit, player::Player}; -use async_trait::async_trait; use crossbeam::atomic::AtomicCell; use pumpkin_data::damage::DamageType; use pumpkin_nbt::compound::NbtCompound; @@ -106,26 +107,28 @@ impl HungerManager { } } -#[async_trait] impl NBTStorage for HungerManager { // TODO: Proper value checks - - async fn write_nbt(&self, nbt: &mut NbtCompound) { - nbt.put_int("foodLevel", self.level.load().into()); - nbt.put_float("foodSaturationLevel", self.saturation.load()); - nbt.put_float("foodExhaustionLevel", self.exhaustion.load()); - nbt.put_int("foodTickTimer", self.tick_timer.load() as i32); + fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async { + nbt.put_int("foodLevel", self.level.load().into()); + nbt.put_float("foodSaturationLevel", self.saturation.load()); + nbt.put_float("foodExhaustionLevel", self.exhaustion.load()); + nbt.put_int("foodTickTimer", self.tick_timer.load() as i32); + }) } - async fn read_nbt(&mut self, nbt: &mut NbtCompound) { - self.level - .store(nbt.get_int("foodLevel").unwrap_or(20) as u8); - self.saturation - .store(nbt.get_float("foodSaturationLevel").unwrap_or(5.0)); - self.exhaustion - .store(nbt.get_float("foodExhaustionLevel").unwrap_or(0.0)); - self.tick_timer - .store(nbt.get_int("foodTickTimer").unwrap_or(0) as u32); + fn read_nbt<'a>(&'a mut self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async move { + self.level + .store(nbt.get_int("foodLevel").unwrap_or(20) as u8); + self.saturation + .store(nbt.get_float("foodSaturationLevel").unwrap_or(5.0)); + self.exhaustion + .store(nbt.get_float("foodExhaustionLevel").unwrap_or(0.0)); + self.tick_timer + .store(nbt.get_int("foodTickTimer").unwrap_or(0) as u32); + }) } } diff --git a/pumpkin/src/entity/item.rs b/pumpkin/src/entity/item.rs index 25af5806e..bcdff2cac 100644 --- a/pumpkin/src/entity/item.rs +++ b/pumpkin/src/entity/item.rs @@ -7,7 +7,6 @@ use std::sync::{ }, }; -use async_trait::async_trait; use crossbeam::atomic::AtomicCell; use pumpkin_data::damage::DamageType; use pumpkin_protocol::{ @@ -18,7 +17,7 @@ use pumpkin_util::math::vector3::Vector3; use pumpkin_world::item::ItemStack; use tokio::sync::Mutex; -use crate::server::Server; +use crate::{entity::EntityBaseFuture, server::Server}; use super::{Entity, EntityBase, NBTStorage, living::LivingEntity, player::Player}; @@ -194,132 +193,137 @@ impl ItemEntity { impl NBTStorage for ItemEntity {} -#[async_trait] impl EntityBase for ItemEntity { - async fn tick(&self, caller: Arc, server: &Server) { - let entity = &self.entity; - entity.tick(caller.clone(), server).await; - { - let mut delay = self.pickup_delay.lock().await; - *delay = delay.saturating_sub(1); - }; - - let original_velo = entity.velocity.load(); - - let mut velo = original_velo; - - if entity.touching_water.load(Ordering::SeqCst) && entity.water_height.load() > 0.1 { - velo.x *= 0.99; - - velo.z *= 0.99; - - if velo.y < 0.06 { - velo.y += 5.0e-4; - } - } else if entity.touching_lava.load(Ordering::SeqCst) && entity.lava_height.load() > 0.1 { - velo.x *= 0.95; - - velo.z *= 0.95; - - if velo.y < 0.06 { - velo.y += 5.0e-4; - } - } else { - velo.y -= self.get_gravity(); - } - - entity.velocity.store(velo); - - let pos = entity.pos.load(); - - let bounding_box = entity.bounding_box.load(); - - let no_clip = !self - .entity - .world - .is_space_empty(bounding_box.expand(-1.0e-7, -1.0e-7, -1.0e-7)) - .await; - - entity.no_clip.store(no_clip, Ordering::Relaxed); - - if no_clip { - entity - .push_out_of_blocks(Vector3::new( - pos.x, - f64::midpoint(bounding_box.min.y, bounding_box.max.y), - pos.z, - )) - .await; - } - - let mut velo = entity.velocity.load(); // In case push_out_of_blocks modifies it - - let mut tick_move = - !entity.on_ground.load(Ordering::SeqCst) || velo.horizontal_length_squared() > 1.0e-5; - - if !tick_move { - let Ok(item_age) = i32::try_from(self.item_age.load(Ordering::Relaxed)) else { - entity.remove().await; - - return; + fn tick<'a>( + &'a self, + caller: Arc, + server: &'a Server, + ) -> EntityBaseFuture<'a, ()> { + Box::pin(async move { + let entity = &self.entity; + entity.tick(caller.clone(), server).await; + { + let mut delay = self.pickup_delay.lock().await; + *delay = delay.saturating_sub(1); }; - tick_move = (item_age + entity.entity_id) % 4 == 0; - } + let original_velo = entity.velocity.load(); - if tick_move { - entity.move_entity(caller.clone(), velo).await; + let mut velo = original_velo; - entity.tick_block_collisions(&caller, server).await; + if entity.touching_water.load(Ordering::SeqCst) && entity.water_height.load() > 0.1 { + velo.x *= 0.99; - let mut friction = 0.98; + velo.z *= 0.99; - let on_ground = entity.on_ground.load(Ordering::SeqCst); + if velo.y < 0.06 { + velo.y += 5.0e-4; + } + } else if entity.touching_lava.load(Ordering::SeqCst) && entity.lava_height.load() > 0.1 + { + velo.x *= 0.95; - if on_ground { - let block_affecting_velo = entity.get_block_with_y_offset(0.999_999).await.1; + velo.z *= 0.95; - friction *= f64::from(block_affecting_velo.slipperiness) * 0.98; - } - - velo = velo.multiply(friction, 0.98, friction); - - if on_ground && velo.y < 0.0 { - velo = velo.multiply(1.0, -0.5, 1.0); + if velo.y < 0.06 { + velo.y += 5.0e-4; + } + } else { + velo.y -= self.get_gravity(); } entity.velocity.store(velo); - } - if !self.never_despawn.load(Ordering::Relaxed) { - let age = self.item_age.fetch_add(1, Ordering::Relaxed) + 1; + let pos = entity.pos.load(); - if age >= 6000 { - entity.remove().await; + let bounding_box = entity.bounding_box.load(); - return; + let no_clip = !self + .entity + .world + .is_space_empty(bounding_box.expand(-1.0e-7, -1.0e-7, -1.0e-7)) + .await; + + entity.no_clip.store(no_clip, Ordering::Relaxed); + + if no_clip { + entity + .push_out_of_blocks(Vector3::new( + pos.x, + f64::midpoint(bounding_box.min.y, bounding_box.max.y), + pos.z, + )) + .await; } - let n = if entity - .last_pos - .load() - .sub(&entity.pos.load()) - .length_squared() - == 0.0 - { - 40 - } else { - 2 - }; + let mut velo = entity.velocity.load(); // In case push_out_of_blocks modifies it - if age.is_multiple_of(n) && self.can_merge().await { - self.try_merge().await; + let mut tick_move = !entity.on_ground.load(Ordering::SeqCst) + || velo.horizontal_length_squared() > 1.0e-5; + + if !tick_move { + let Ok(item_age) = i32::try_from(self.item_age.load(Ordering::Relaxed)) else { + entity.remove().await; + + return; + }; + + tick_move = (item_age + entity.entity_id) % 4 == 0; } - } - entity.update_fluid_state(&caller).await; + if tick_move { + entity.move_entity(caller.clone(), velo).await; - let velocity_dirty = entity.velocity_dirty.swap(false, Ordering::SeqCst) + entity.tick_block_collisions(&caller, server).await; + + let mut friction = 0.98; + + let on_ground = entity.on_ground.load(Ordering::SeqCst); + + if on_ground { + let block_affecting_velo = entity.get_block_with_y_offset(0.999_999).await.1; + + friction *= f64::from(block_affecting_velo.slipperiness) * 0.98; + } + + velo = velo.multiply(friction, 0.98, friction); + + if on_ground && velo.y < 0.0 { + velo = velo.multiply(1.0, -0.5, 1.0); + } + + entity.velocity.store(velo); + } + + if !self.never_despawn.load(Ordering::Relaxed) { + let age = self.item_age.fetch_add(1, Ordering::Relaxed) + 1; + + if age >= 6000 { + entity.remove().await; + + return; + } + + let n = if entity + .last_pos + .load() + .sub(&entity.pos.load()) + .length_squared() + == 0.0 + { + 40 + } else { + 2 + }; + + if age.is_multiple_of(n) && self.can_merge().await { + self.try_merge().await; + } + } + + entity.update_fluid_state(&caller).await; + + let velocity_dirty = entity.velocity_dirty.swap(false, Ordering::SeqCst) || entity.touching_water.load(Ordering::SeqCst) @@ -333,87 +337,94 @@ impl EntityBase for ItemEntity { || entity.velocity.load() != original_velo; - if velocity_dirty { - entity.send_pos_rot().await; + if velocity_dirty { + entity.send_pos_rot().await; - entity.send_velocity().await; - } + entity.send_velocity().await; + } + }) } - async fn init_data_tracker(&self) { - self.entity - .send_meta_data(&[Metadata::new( - 8, - MetaDataType::ItemStack, - &ItemStackSerializer::from(self.item_stack.lock().await.clone()), - )]) - .await; + fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { + Box::pin(async { + self.entity + .send_meta_data(&[Metadata::new( + 8, + MetaDataType::ItemStack, + &ItemStackSerializer::from(self.item_stack.lock().await.clone()), + )]) + .await; + }) } - async fn damage_with_context( - &self, + fn damage_with_context<'a>( + &'a self, _caller: Arc, amount: f32, _damage_type: DamageType, _position: Option>, - _source: Option<&dyn EntityBase>, - _cause: Option<&dyn EntityBase>, - ) -> bool { - //TODO: invulnerability, e.g. ancient debris - self.health.store(self.health.load() - amount); - if self.health.load() <= 0.0 { - self.entity.remove().await; - } - true + _source: Option<&'a dyn EntityBase>, + _cause: Option<&'a dyn EntityBase>, + ) -> EntityBaseFuture<'a, bool> { + Box::pin(async move { + //TODO: invulnerability, e.g. ancient debris + self.health.store(self.health.load() - amount); + if self.health.load() <= 0.0 { + self.entity.remove().await; + } + true + }) } - async fn damage( + fn damage( &self, _caller: Arc, _amount: f32, _damage_type: DamageType, - ) -> bool { - false + ) -> EntityBaseFuture<'_, bool> { + Box::pin(async { false }) } - async fn on_player_collision(&self, player: &Arc) { - let can_pickup = { - let delay = self.pickup_delay.lock().await; - *delay == 0 - }; + fn on_player_collision<'a>(&'a self, player: &'a Arc) -> EntityBaseFuture<'a, ()> { + Box::pin(async { + let can_pickup = { + let delay = self.pickup_delay.lock().await; + *delay == 0 + }; - if can_pickup - && player.living_entity.health.load() > 0.0 - && (player - .inventory - .insert_stack_anywhere(&mut *self.item_stack.lock().await) - .await - || player.is_creative()) - { - player - .client - .enqueue_packet(&CTakeItemEntity::new( - self.entity.entity_id.into(), - player.entity_id().into(), - self.item_stack.lock().await.item_count.into(), - )) - .await; - player - .current_screen_handler - .lock() - .await - .lock() - .await - .send_content_updates() - .await; + if can_pickup + && player.living_entity.health.load() > 0.0 + && (player + .inventory + .insert_stack_anywhere(&mut *self.item_stack.lock().await) + .await + || player.is_creative()) + { + player + .client + .enqueue_packet(&CTakeItemEntity::new( + self.entity.entity_id.into(), + player.entity_id().into(), + self.item_stack.lock().await.item_count.into(), + )) + .await; + player + .current_screen_handler + .lock() + .await + .lock() + .await + .send_content_updates() + .await; - if self.item_stack.lock().await.is_empty() { - self.entity.remove().await; - } else { - // Update entity - self.init_data_tracker().await; + if self.item_stack.lock().await.is_empty() { + self.entity.remove().await; + } else { + // Update entity + self.init_data_tracker().await; + } } - } + }) } fn get_entity(&self) -> &Entity { diff --git a/pumpkin/src/entity/living.rs b/pumpkin/src/entity/living.rs index bcbc1e80a..244baf877 100644 --- a/pumpkin/src/entity/living.rs +++ b/pumpkin/src/entity/living.rs @@ -13,9 +13,9 @@ use std::{collections::HashMap, sync::atomic::AtomicI32}; use super::{Entity, NBTStorage}; use super::{EntityBase, NBTStorageInit}; +use crate::entity::{EntityBaseFuture, NbtFuture}; use crate::server::Server; use crate::world::loot::{LootContextParameters, LootTableExt}; -use async_trait::async_trait; use crossbeam::atomic::AtomicCell; use pumpkin_config::advanced_config; use pumpkin_data::Block; @@ -78,13 +78,6 @@ pub struct LivingEntity { livings_flags: AtomicU8, } -#[async_trait] -pub trait LivingEntityTrait: EntityBase { - async fn on_actually_hurt(&self, _amount: f32, _damage_type: DamageType) { - //TODO: wolves, etc... - } -} - impl LivingEntity { const USING_ITEM_FLAG: i32 = 1; const OFF_HAND_ACTIVE_FLAG: i32 = 2; @@ -914,150 +907,152 @@ impl LivingEntity { } } -impl LivingEntityTrait for LivingEntity {} - -#[async_trait] impl NBTStorage for LivingEntity { - async fn write_nbt(&self, nbt: &mut NbtCompound) { - self.entity.write_nbt(nbt).await; - nbt.put("Health", NbtTag::Float(self.health.load())); - nbt.put("fall_distance", NbtTag::Float(self.fall_distance.load())); - { - let effects = self.active_effects.lock().await; - if !effects.is_empty() { - // Iterate effects and create Box<[NbtTag]> - let mut effects_list = Vec::with_capacity(effects.len()); - for effect in effects.values() { - let mut effect_nbt = pumpkin_nbt::compound::NbtCompound::new(); - effect.write_nbt(&mut effect_nbt).await; - effects_list.push(NbtTag::Compound(effect_nbt)); + fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async move { + self.entity.write_nbt(nbt).await; + nbt.put("Health", NbtTag::Float(self.health.load())); + nbt.put("fall_distance", NbtTag::Float(self.fall_distance.load())); + { + let effects = self.active_effects.lock().await; + if !effects.is_empty() { + // Iterate effects and create Box<[NbtTag]> + let mut effects_list = Vec::with_capacity(effects.len()); + for effect in effects.values() { + let mut effect_nbt = pumpkin_nbt::compound::NbtCompound::new(); + effect.write_nbt(&mut effect_nbt).await; + effects_list.push(NbtTag::Compound(effect_nbt)); + } + nbt.put("active_effects", NbtTag::List(effects_list)); } - nbt.put("active_effects", NbtTag::List(effects_list)); } - } - //TODO: write equipment - // todo more... + //TODO: write equipment + // todo more... + }) } - async fn read_nbt_non_mut(&self, nbt: &NbtCompound) { - self.entity.read_nbt_non_mut(nbt).await; - self.health.store(nbt.get_float("Health").unwrap_or(0.0)); - self.fall_distance - .store(nbt.get_float("fall_distance").unwrap_or(0.0)); - { - let mut active_effects = self.active_effects.lock().await; - let nbt_effects = nbt.get_list("active_effects"); - if let Some(nbt_effects) = nbt_effects { - for effect in nbt_effects { - if let NbtTag::Compound(effect_nbt) = effect { - let effect = Effect::create_from_nbt(&mut effect_nbt.clone()).await; - if effect.is_none() { - log::warn!("Unable to read effect from nbt"); - continue; + fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async { + self.entity.read_nbt_non_mut(nbt).await; + self.health.store(nbt.get_float("Health").unwrap_or(0.0)); + self.fall_distance + .store(nbt.get_float("fall_distance").unwrap_or(0.0)); + { + let mut active_effects = self.active_effects.lock().await; + let nbt_effects = nbt.get_list("active_effects"); + if let Some(nbt_effects) = nbt_effects { + for effect in nbt_effects { + if let NbtTag::Compound(effect_nbt) = effect { + let effect = Effect::create_from_nbt(&mut effect_nbt.clone()).await; + if effect.is_none() { + log::warn!("Unable to read effect from nbt"); + continue; + } + let mut effect = effect.unwrap(); + effect.blend = true; // TODO: change, is taken from effect give command + active_effects.insert(effect.effect_type, effect); } - let mut effect = effect.unwrap(); - effect.blend = true; // TODO: change, is taken from effect give command - active_effects.insert(effect.effect_type, effect); } } } - } + }) // todo more... } } -#[async_trait] impl EntityBase for LivingEntity { - async fn damage_with_context( - &self, + fn damage_with_context<'a>( + &'a self, caller: Arc, amount: f32, damage_type: DamageType, position: Option>, - source: Option<&dyn EntityBase>, - cause: Option<&dyn EntityBase>, - ) -> bool { - // Check invulnerability before applying damage - if self.entity.is_invulnerable_to(&damage_type) { - return false; - } - - if self.health.load() <= 0.0 || self.dead.load(Relaxed) { - return false; // Dying or dead - } - - if amount < 0.0 { - return false; - } - - if (damage_type == DamageType::IN_FIRE || damage_type == DamageType::ON_FIRE) - && self.has_effect(&StatusEffect::FIRE_RESISTANCE).await - { - return false; // Fire resistance - } - - let world = &self.entity.world; - - let last_damage = self.last_damage_taken.load(); - let play_sound; - let mut damage_amount = if self.hurt_cooldown.load(Relaxed) > 10 { - if amount <= last_damage { + source: Option<&'a dyn EntityBase>, + cause: Option<&'a dyn EntityBase>, + ) -> EntityBaseFuture<'a, bool> { + Box::pin(async move { + // Check invulnerability before applying damage + if self.entity.is_invulnerable_to(&damage_type) { return false; } - play_sound = false; - amount - self.last_damage_taken.load() - } else { - self.hurt_cooldown.store(20, Relaxed); - play_sound = true; - amount - }; - self.last_damage_taken.store(amount); - damage_amount = damage_amount.max(0.0); - let config = &advanced_config().pvp; + if self.health.load() <= 0.0 || self.dead.load(Relaxed) { + return false; // Dying or dead + } - if config.hurt_animation { - let entity_id = VarInt(self.entity.entity_id); - world - .broadcast_packet_all(&CHurtAnimation::new(entity_id, self.entity.yaw.load())) - .await; - } + if amount < 0.0 { + return false; + } - self.entity - .world - .broadcast_packet_all(&CDamageEvent::new( - self.entity.entity_id.into(), - damage_type.id.into(), - source.map(|e| e.get_entity().entity_id.into()), - cause.map(|e| e.get_entity().entity_id.into()), - position, - )) - .await; + if (damage_type == DamageType::IN_FIRE || damage_type == DamageType::ON_FIRE) + && self.has_effect(&StatusEffect::FIRE_RESISTANCE).await + { + return false; // Fire resistance + } + + let world = &self.entity.world; + + let last_damage = self.last_damage_taken.load(); + let play_sound; + let mut damage_amount = if self.hurt_cooldown.load(Relaxed) > 10 { + if amount <= last_damage { + return false; + } + play_sound = false; + amount - self.last_damage_taken.load() + } else { + self.hurt_cooldown.store(20, Relaxed); + play_sound = true; + amount + }; + self.last_damage_taken.store(amount); + damage_amount = damage_amount.max(0.0); + + let config = &advanced_config().pvp; + + if config.hurt_animation { + let entity_id = VarInt(self.entity.entity_id); + world + .broadcast_packet_all(&CHurtAnimation::new(entity_id, self.entity.yaw.load())) + .await; + } - if play_sound { self.entity .world - .play_sound( - // Sound::EntityPlayerHurt, - Sound::EntityGenericHurt, - SoundCategory::Players, - &self.entity.pos.load(), - ) + .broadcast_packet_all(&CDamageEvent::new( + self.entity.entity_id.into(), + damage_type.id.into(), + source.map(|e| e.get_entity().entity_id.into()), + cause.map(|e| e.get_entity().entity_id.into()), + position, + )) .await; - // todo: calculate knockback - } - let new_health = self.health.load() - damage_amount; - if damage_amount > 0.0 { - self.on_actually_hurt(damage_amount, damage_type).await; - self.set_health(new_health).await; - } + if play_sound { + self.entity + .world + .play_sound( + // Sound::EntityPlayerHurt, + Sound::EntityGenericHurt, + SoundCategory::Players, + &self.entity.pos.load(), + ) + .await; + // todo: calculate knockback + } - if new_health <= 0.0 && !self.try_use_death_protector(&caller).await { - self.on_death(damage_type, source, cause).await; - } + let new_health = self.health.load() - damage_amount; + if damage_amount > 0.0 { + //self.on_actually_hurt(damage_amount, damage_type).await; + self.set_health(new_health).await; + } - true + if new_health <= 0.0 && !self.try_use_death_protector(&caller).await { + self.on_death(damage_type, source, cause).await; + } + + true + }) } fn get_gravity(&self) -> f64 { @@ -1065,57 +1060,63 @@ impl EntityBase for LivingEntity { GRAVITY } - async fn tick(&self, caller: Arc, server: &Server) { - self.entity.tick(caller.clone(), server).await; - self.tick_movement(server, caller.clone()).await; - // TODO - if caller.get_player().is_none() { - self.entity.send_pos_rot().await; - self.entity.send_velocity().await; - } - self.tick_effects().await; - // Current active item - { - let item_in_use = self.item_in_use.lock().await.clone(); - if let Some(item) = item_in_use.as_ref() - && self.item_use_time.fetch_sub(1, Ordering::Relaxed) <= 0 + fn tick<'a>( + &'a self, + caller: Arc, + server: &'a Server, + ) -> EntityBaseFuture<'a, ()> { + Box::pin(async move { + self.entity.tick(caller.clone(), server).await; + self.tick_movement(server, caller.clone()).await; + // TODO + if caller.get_player().is_none() { + self.entity.send_pos_rot().await; + self.entity.send_velocity().await; + } + self.tick_effects().await; + // Current active item { - // Consume item - if let Some(food) = item.get_data_component::() - && let Some(player) = caller.get_player() + let item_in_use = self.item_in_use.lock().await.clone(); + if let Some(item) = item_in_use.as_ref() + && self.item_use_time.fetch_sub(1, Ordering::Relaxed) <= 0 { - player - .hunger_manager - .eat(player, food.nutrition as u8, food.saturation) + // Consume item + if let Some(food) = item.get_data_component::() + && let Some(player) = caller.get_player() + { + player + .hunger_manager + .eat(player, food.nutrition as u8, food.saturation) + .await; + } + if let Some(player) = caller.get_player() { + player + .inventory + .held_item() + .lock() + .await + .decrement_unless_creative(player.gamemode.load(), 1); + } + + self.clear_active_hand().await; + } + } + + if self.hurt_cooldown.load(Relaxed) > 0 { + self.hurt_cooldown.fetch_sub(1, Relaxed); + } + if self.health.load() <= 0.0 { + let time = self.death_time.fetch_add(1, Relaxed); + if time == 20 { + // Spawn Death particles + self.entity + .world + .send_entity_status(&self.entity, EntityStatus::AddDeathParticles) .await; + self.entity.remove().await; } - if let Some(player) = caller.get_player() { - player - .inventory - .held_item() - .lock() - .await - .decrement_unless_creative(player.gamemode.load(), 1); - } - - self.clear_active_hand().await; } - } - - if self.hurt_cooldown.load(Relaxed) > 0 { - self.hurt_cooldown.fetch_sub(1, Relaxed); - } - if self.health.load() <= 0.0 { - let time = self.death_time.fetch_add(1, Relaxed); - if time == 20 { - // Spawn Death particles - self.entity - .world - .send_entity_status(&self.entity, EntityStatus::AddDeathParticles) - .await; - self.entity.remove().await; - } - } + }) } fn get_entity(&self) -> &Entity { diff --git a/pumpkin/src/entity/mob/mod.rs b/pumpkin/src/entity/mob/mod.rs index d4b658a11..44c5ca176 100644 --- a/pumpkin/src/entity/mob/mod.rs +++ b/pumpkin/src/entity/mob/mod.rs @@ -1,13 +1,14 @@ use super::{Entity, EntityBase, NBTStorage, ai::path::Navigator, living::LivingEntity}; +use crate::entity::EntityBaseFuture; use crate::entity::ai::control::look_control::LookControl; use crate::entity::ai::goal::goal_selector::GoalSelector; use crate::server::Server; use crate::world::World; -use async_trait::async_trait; use crossbeam::atomic::AtomicCell; use pumpkin_data::damage::DamageType; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; +use std::pin::Pin; use std::sync::Arc; use std::sync::atomic::AtomicI32; use std::sync::atomic::Ordering::Relaxed; @@ -84,53 +85,60 @@ pub trait Mob: EntityBase + Send + Sync { } } -#[async_trait] impl EntityBase for T { - async fn tick(&self, caller: Arc, server: &Server) { - let mob_entity = self.get_mob_entity(); - mob_entity.living_entity.tick(caller, server).await; + fn tick<'a>( + &'a self, + caller: Arc, + server: &'a Server, + ) -> EntityBaseFuture<'a, ()> { + Box::pin(async move { + let mob_entity = self.get_mob_entity(); + mob_entity.living_entity.tick(caller, server).await; - let age = mob_entity.living_entity.entity.age.load(Relaxed); - if (age + mob_entity.living_entity.entity.entity_id) % 2 != 0 && age > 1 { - mob_entity - .target_selector - .lock() - .await - .tick_goals(self, false) - .await; - mob_entity - .goals_selector - .lock() - .await - .tick_goals(self, false) - .await; - } else { - mob_entity.target_selector.lock().await.tick(self).await; - mob_entity.goals_selector.lock().await.tick(self).await; - } + let age = mob_entity.living_entity.entity.age.load(Relaxed); + if (age + mob_entity.living_entity.entity.entity_id) % 2 != 0 && age > 1 { + mob_entity + .target_selector + .lock() + .await + .tick_goals(self, false) + .await; + mob_entity + .goals_selector + .lock() + .await + .tick_goals(self, false) + .await; + } else { + mob_entity.target_selector.lock().await.tick(self).await; + mob_entity.goals_selector.lock().await.tick(self).await; + } - let mut navigator = mob_entity.navigator.lock().await; - navigator.tick(&mob_entity.living_entity).await; - drop(navigator); + let mut navigator = mob_entity.navigator.lock().await; + navigator.tick(&mob_entity.living_entity).await; + drop(navigator); - let mut look_control = mob_entity.look_control.lock().await; - look_control.tick(self).await; - drop(look_control); + let mut look_control = mob_entity.look_control.lock().await; + look_control.tick(self).await; + drop(look_control); + }) } - async fn damage_with_context( - &self, + fn damage_with_context<'a>( + &'a self, caller: Arc, amount: f32, damage_type: DamageType, position: Option>, - source: Option<&dyn EntityBase>, - cause: Option<&dyn EntityBase>, - ) -> bool { - self.get_mob_entity() - .living_entity - .damage_with_context(caller, amount, damage_type, position, source, cause) - .await + source: Option<&'a dyn EntityBase>, + cause: Option<&'a dyn EntityBase>, + ) -> EntityBaseFuture<'a, bool> { + Box::pin(async move { + self.get_mob_entity() + .living_entity + .damage_with_context(caller, amount, damage_type, position, source, cause) + .await + }) } fn get_entity(&self) -> &Entity { @@ -152,7 +160,7 @@ impl EntityBase for T { #[allow(dead_code)] const DEFAULT_PATHFINDING_FAVOR: f32 = 0.0; -#[async_trait] + pub trait PathAwareEntity: Mob + Send + Sync { fn get_pathfinding_favor(&self, _block_pos: BlockPos, _world: Arc) -> f32 { 0.0 @@ -166,9 +174,11 @@ pub trait PathAwareEntity: Mob + Send + Sync { ) >= 0.0 } - async fn is_navigation(&self) -> bool { - let navigator = self.get_mob_entity().navigator.lock().await; - !navigator.is_idle() + fn is_navigation<'a>(&'a self) -> Pin + Send + 'a>> { + Box::pin(async { + let navigator = self.get_mob_entity().navigator.lock().await; + !navigator.is_idle() + }) } // TODO: implement diff --git a/pumpkin/src/entity/mob/zombie.rs b/pumpkin/src/entity/mob/zombie.rs index be531451c..582a00ab8 100644 --- a/pumpkin/src/entity/mob/zombie.rs +++ b/pumpkin/src/entity/mob/zombie.rs @@ -1,20 +1,22 @@ use super::{Mob, MobEntity}; use crate::entity::ai::goal::look_around_goal::LookAroundGoal; use crate::entity::ai::goal::move_to_target_pos_goal::MoveToTargetPos; -use crate::entity::ai::goal::step_and_destroy_block_goal::{StepAndDestroyBlockGoal, Stepping}; +use crate::entity::ai::goal::step_and_destroy_block_goal::{ + StepAndDestroyBlockGoal, Stepping, SteppingFuture, +}; use crate::entity::ai::goal::zombie_attack_goal::ZombieAttackGoal; -use crate::entity::ai::goal::{Controls, Goal, ParentHandle}; +use crate::entity::ai::goal::{Controls, Goal, GoalFuture, ParentHandle}; use crate::entity::{ Entity, NBTStorage, ai::goal::{active_target_goal::ActiveTargetGoal, look_at_entity::LookAtEntityGoal}, }; use crate::world::World; -use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::entity::EntityType; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_util::math::position::BlockPos; use rand::{Rng, rng}; +use std::pin::Pin; use std::sync::{Arc, Weak}; pub struct Zombie { @@ -87,26 +89,31 @@ impl DestroyEggGoal { } } -#[async_trait] impl Goal for DestroyEggGoal { - async fn can_start(&mut self, mob: &dyn Mob) -> bool { - self.step_and_destroy_block_goal.can_start(mob).await + fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { self.step_and_destroy_block_goal.can_start(mob).await }) } - async fn should_continue(&self, mob: &dyn Mob) -> bool { - self.step_and_destroy_block_goal.should_continue(mob).await + fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> { + Box::pin(async { self.step_and_destroy_block_goal.should_continue(mob).await }) } - async fn start(&mut self, mob: &dyn Mob) { - self.step_and_destroy_block_goal.start(mob).await; + fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + self.step_and_destroy_block_goal.start(mob).await; + }) } - async fn stop(&mut self, mob: &dyn Mob) { - self.step_and_destroy_block_goal.stop(mob).await; + fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + self.step_and_destroy_block_goal.stop(mob).await; + }) } - async fn tick(&mut self, mob: &dyn Mob) { - self.step_and_destroy_block_goal.tick(mob).await; + fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> { + Box::pin(async { + self.step_and_destroy_block_goal.tick(mob).await; + }) } fn should_run_every_tick(&self) -> bool { @@ -118,41 +125,57 @@ impl Goal for DestroyEggGoal { } } -#[async_trait] impl Stepping for DestroyEggGoal { - async fn tick_stepping(&self, world: Arc, block_pos: BlockPos) { - let random = rng().random::(); - world - .play_sound_raw( - Sound::EntityZombieDestroyEgg as u16, - SoundCategory::Hostile, - &block_pos.0.to_f64(), - 0.7, - 0.9 + random * 0.2, - ) - .await; + fn tick_stepping(&self, world: Arc, block_pos: BlockPos) -> SteppingFuture<'_> { + Box::pin(async move { + let random = rng().random::(); + + // NOTE: block_pos.0.to_f64() is assumed to be the correct way to get Vector3 + let pos_f64 = (block_pos.0).to_f64(); + + world + .play_sound_raw( + Sound::EntityZombieDestroyEgg as u16, + SoundCategory::Hostile, + &pos_f64, + 0.7, + 0.9 + random * 0.2, + ) + .await; + }) } - async fn on_destroy_block(&self, world: Arc, block_pos: BlockPos) { - let random = rng().random::(); - world - .play_sound_raw( - Sound::EntityTurtleEggBreak as u16, - SoundCategory::Blocks, - &block_pos.0.to_f64(), - 0.7, - 0.9 + random * 0.2, - ) - .await; + fn on_destroy_block(&self, world: Arc, block_pos: BlockPos) -> SteppingFuture<'_> { + Box::pin(async move { + let random = rng().random::(); + + // NOTE: block_pos.0.to_f64() is assumed to be the correct way to get Vector3 + let pos_f64 = (block_pos.0).to_f64(); + + world + .play_sound_raw( + Sound::EntityTurtleEggBreak as u16, + SoundCategory::Blocks, + &pos_f64, + 0.7, + 0.9 + random * 0.2, + ) + .await; + }) } } -#[async_trait] impl MoveToTargetPos for DestroyEggGoal { - async fn is_target_pos(&self, world: Arc, block_pos: BlockPos) -> bool { - self.step_and_destroy_block_goal - .is_target_pos(world, block_pos) - .await + fn is_target_pos<'a>( + &'a self, + world: Arc, + block_pos: BlockPos, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + self.step_and_destroy_block_goal + .is_target_pos(world, block_pos) + .await + }) } fn get_desired_distance_to_target(&self) -> f64 { diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 96cbe4512..319e3a3c3 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -1,7 +1,6 @@ use crate::entity::item::ItemEntity; use crate::world::World; use crate::{server::Server, world::portal::PortalManager}; -use async_trait::async_trait; use bytes::BufMut; use crossbeam::atomic::AtomicCell; use living::LivingEntity; @@ -42,6 +41,7 @@ use pumpkin_world::entity::entity_data_flags::DATA_POSE; use serde::Serialize; use std::collections::BTreeMap; use std::f32::consts::PI; +use std::pin::Pin; use std::sync::{ Arc, atomic::{ @@ -69,7 +69,10 @@ pub mod r#type; mod combat; pub mod predicate; -#[async_trait] +pub type EntityBaseFuture<'a, T> = Pin + Send + 'a>>; + +pub type TeleportFuture = Pin + Send>>; + pub trait EntityBase: Send + Sync + NBTStorage { /// Called every tick for this entity. /// @@ -78,26 +81,40 @@ pub trait EntityBase: Send + Sync + NBTStorage { /// but in some scenarios (e.g., interactions or events), it might be a different entity. /// /// The `server` parameter provides access to the game server instance. - async fn tick(&self, caller: Arc, server: &Server) { - if let Some(living) = self.get_living_entity() { - living.tick(caller, server).await; - } else { - self.get_entity().tick(caller, server).await; - } + fn tick<'a>( + &'a self, + caller: Arc, + server: &'a Server, + ) -> EntityBaseFuture<'a, ()> { + Box::pin(async move { + if let Some(living) = self.get_living_entity() { + living.tick(caller, server).await; + } else { + self.get_entity().tick(caller, server).await; + } + }) } - async fn init_data_tracker(&self) {} + fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { + Box::pin(async {}) + } - async fn teleport( + // This method takes ownership of Arc, so the lifetime bounds are different. + fn teleport( self: Arc, position: Vector3, yaw: Option, pitch: Option, world: Arc, - ) { - self.get_entity() - .teleport(position, yaw, pitch, world) - .await; + ) -> TeleportFuture + where + Self: 'static, + { + Box::pin(async move { + self.get_entity() + .teleport(position, yaw, pitch, world) + .await; + }) } fn is_pushed_by_fluids(&self) -> bool { @@ -109,14 +126,16 @@ pub trait EntityBase: Send + Sync + NBTStorage { } /// Returns if damage was successful or not - async fn damage( + fn damage( &self, caller: Arc, amount: f32, damage_type: DamageType, - ) -> bool { - self.damage_with_context(caller, amount, damage_type, None, None, None) - .await + ) -> EntityBaseFuture<'_, bool> { + Box::pin(async move { + self.damage_with_context(caller, amount, damage_type, None, None, None) + .await + }) } fn is_spectator(&self) -> bool { @@ -135,21 +154,26 @@ pub trait EntityBase: Send + Sync + NBTStorage { false } - async fn damage_with_context( - &self, + fn damage_with_context<'a>( + &'a self, _caller: Arc, _amount: f32, _damage_type: DamageType, _position: Option>, - _source: Option<&dyn EntityBase>, - _cause: Option<&dyn EntityBase>, - ) -> bool { - // Just do nothing - false + _source: Option<&'a dyn EntityBase>, + _cause: Option<&'a dyn EntityBase>, + ) -> EntityBaseFuture<'a, bool> { + Box::pin(async { + // Just do nothing + false + }) } /// Called when a player collides with a entity - async fn on_player_collision(&self, _player: &Arc) {} + fn on_player_collision<'a>(&'a self, _player: &'a Arc) -> EntityBaseFuture<'a, ()> { + Box::pin(async {}) + } + fn get_entity(&self) -> &Entity; fn get_living_entity(&self) -> Option<&LivingEntity>; @@ -172,36 +196,41 @@ pub trait EntityBase: Send + Sync + NBTStorage { [], )) } - async fn get_display_name(&self) -> TextComponent { - // TODO: team color - let entity = self.get_entity(); - let mut name = entity - .custom_name - .clone() - .unwrap_or(TextComponent::translate( - format!("entity.minecraft.{}", entity.entity_type.resource_name), - [], + + fn get_display_name(&self) -> EntityBaseFuture<'_, TextComponent> { + Box::pin(async move { + // TODO: team color + let entity = self.get_entity(); + let mut name = entity + .custom_name + .clone() + .unwrap_or(TextComponent::translate( + format!("entity.minecraft.{}", entity.entity_type.resource_name), + [], + )); + let name_clone = name.clone(); + name = name.hover_event(HoverEvent::show_entity( + entity.entity_uuid.to_string(), + entity.entity_type.resource_name.into(), + Some(name_clone), )); - let name_clone = name.clone(); - name = name.hover_event(HoverEvent::show_entity( - entity.entity_uuid.to_string(), - entity.entity_type.resource_name.into(), - Some(name_clone), - )); - name = name.insertion(entity.entity_uuid.to_string()); - name + name = name.insertion(entity.entity_uuid.to_string()); + name + }) } /// Kills the Entity. - async fn kill(&self, caller: Arc) { - if let Some(living) = self.get_living_entity() { - living - .damage(caller, f32::MAX, DamageType::GENERIC_KILL) - .await; - } else { - // TODO this should be removed once all entities are implemented - self.get_entity().remove().await; - } + fn kill(&self, caller: Arc) -> EntityBaseFuture<'_, ()> { + Box::pin(async move { + if let Some(living) = self.get_living_entity() { + living + .damage(caller, f32::MAX, DamageType::GENERIC_KILL) + .await; + } else { + // TODO this should be removed once all entities are implemented + self.get_entity().remove().await; + } + }) } /// Returns itself as the nbt storage for saving and loading data. @@ -1749,125 +1778,137 @@ impl Entity { } } -#[async_trait] impl NBTStorage for Entity { - async fn write_nbt(&self, nbt: &mut NbtCompound) { - let position = self.pos.load(); - nbt.put_string( - "id", - format!("minecraft:{}", self.entity_type.resource_name), - ); - let uuid = self.entity_uuid.as_u128(); - nbt.put( - "UUID", - NbtTag::IntArray(vec![ - (uuid >> 96) as i32, - ((uuid >> 64) & 0xFFFF_FFFF) as i32, - ((uuid >> 32) & 0xFFFF_FFFF) as i32, - (uuid & 0xFFFF_FFFF) as i32, - ]), - ); - nbt.put( - "Pos", - NbtTag::List(vec![ - position.x.into(), - position.y.into(), - position.z.into(), - ]), - ); - let velocity = self.velocity.load(); - nbt.put( - "Motion", - NbtTag::List(vec![ - velocity.x.into(), - velocity.y.into(), - velocity.z.into(), - ]), - ); - nbt.put( - "Rotation", - NbtTag::List(vec![self.yaw.load().into(), self.pitch.load().into()]), - ); - nbt.put_short("Fire", self.fire_ticks.load(Relaxed) as i16); - nbt.put_bool("OnGround", self.on_ground.load(Relaxed)); - nbt.put_bool("Invulnerable", self.invulnerable.load(Relaxed)); - nbt.put_int("PortalCooldown", self.portal_cooldown.load(Relaxed) as i32); - if self.has_visual_fire.load(Relaxed) { - nbt.put_bool("HasVisualFire", true); - } + fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async move { + let position = self.pos.load(); + nbt.put_string( + "id", + format!("minecraft:{}", self.entity_type.resource_name), + ); + let uuid = self.entity_uuid.as_u128(); + nbt.put( + "UUID", + NbtTag::IntArray(vec![ + (uuid >> 96) as i32, + ((uuid >> 64) & 0xFFFF_FFFF) as i32, + ((uuid >> 32) & 0xFFFF_FFFF) as i32, + (uuid & 0xFFFF_FFFF) as i32, + ]), + ); + nbt.put( + "Pos", + NbtTag::List(vec![ + position.x.into(), + position.y.into(), + position.z.into(), + ]), + ); + let velocity = self.velocity.load(); + nbt.put( + "Motion", + NbtTag::List(vec![ + velocity.x.into(), + velocity.y.into(), + velocity.z.into(), + ]), + ); + nbt.put( + "Rotation", + NbtTag::List(vec![self.yaw.load().into(), self.pitch.load().into()]), + ); + nbt.put_short("Fire", self.fire_ticks.load(Relaxed) as i16); + nbt.put_bool("OnGround", self.on_ground.load(Relaxed)); + nbt.put_bool("Invulnerable", self.invulnerable.load(Relaxed)); + nbt.put_int("PortalCooldown", self.portal_cooldown.load(Relaxed) as i32); + if self.has_visual_fire.load(Relaxed) { + nbt.put_bool("HasVisualFire", true); + } - // todo more... + // todo more... + }) } - async fn read_nbt_non_mut(&self, nbt: &NbtCompound) { - let position = nbt.get_list("Pos").unwrap(); - let x = position[0].extract_double().unwrap_or(0.0); - let y = position[1].extract_double().unwrap_or(0.0); - let z = position[2].extract_double().unwrap_or(0.0); - let pos = Vector3::new(x, y, z); - self.set_pos(pos); - self.first_loaded_chunk_position.store(Some(pos.to_i32())); - let velocity = nbt.get_list("Motion").unwrap(); - let x = velocity[0].extract_double().unwrap_or(0.0); - let y = velocity[1].extract_double().unwrap_or(0.0); - let z = velocity[2].extract_double().unwrap_or(0.0); - self.velocity.store(Vector3::new(x, y, z)); - let rotation = nbt.get_list("Rotation").unwrap(); - let yaw = rotation[0].extract_float().unwrap_or(0.0); - let pitch = rotation[1].extract_float().unwrap_or(0.0); - self.set_rotation(yaw, pitch); - self.head_yaw.store(yaw); - self.fire_ticks - .store(i32::from(nbt.get_short("Fire").unwrap_or(0)), Relaxed); - self.on_ground - .store(nbt.get_bool("OnGround").unwrap_or(false), Relaxed); - self.invulnerable - .store(nbt.get_bool("Invulnerable").unwrap_or(false), Relaxed); - self.portal_cooldown - .store(nbt.get_int("PortalCooldown").unwrap_or(0) as u32, Relaxed); - self.has_visual_fire - .store(nbt.get_bool("HasVisualFire").unwrap_or(false), Relaxed); - // todo more... + fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async { + let position = nbt.get_list("Pos").unwrap(); + let x = position[0].extract_double().unwrap_or(0.0); + let y = position[1].extract_double().unwrap_or(0.0); + let z = position[2].extract_double().unwrap_or(0.0); + let pos = Vector3::new(x, y, z); + self.set_pos(pos); + self.first_loaded_chunk_position.store(Some(pos.to_i32())); + let velocity = nbt.get_list("Motion").unwrap(); + let x = velocity[0].extract_double().unwrap_or(0.0); + let y = velocity[1].extract_double().unwrap_or(0.0); + let z = velocity[2].extract_double().unwrap_or(0.0); + self.velocity.store(Vector3::new(x, y, z)); + let rotation = nbt.get_list("Rotation").unwrap(); + let yaw = rotation[0].extract_float().unwrap_or(0.0); + let pitch = rotation[1].extract_float().unwrap_or(0.0); + self.set_rotation(yaw, pitch); + self.head_yaw.store(yaw); + self.fire_ticks + .store(i32::from(nbt.get_short("Fire").unwrap_or(0)), Relaxed); + self.on_ground + .store(nbt.get_bool("OnGround").unwrap_or(false), Relaxed); + self.invulnerable + .store(nbt.get_bool("Invulnerable").unwrap_or(false), Relaxed); + self.portal_cooldown + .store(nbt.get_int("PortalCooldown").unwrap_or(0) as u32, Relaxed); + self.has_visual_fire + .store(nbt.get_bool("HasVisualFire").unwrap_or(false), Relaxed); + // todo more... + }) } } -#[async_trait] impl EntityBase for Entity { - async fn tick(&self, caller: Arc, _server: &Server) { - self.tick_portal(&caller).await; - self.update_fluid_state(&caller).await; - self.check_out_of_world(caller.clone()).await; - let fire_ticks = self.fire_ticks.load(Ordering::Relaxed); - if fire_ticks > 0 { - if self.entity_type.fire_immune { - self.fire_ticks.store(fire_ticks - 4, Ordering::Relaxed); - if self.fire_ticks.load(Ordering::Relaxed) < 0 { - self.extinguish(); - } - } else { - if fire_ticks % 20 == 0 { - caller - .damage(caller.clone(), 1.0, DamageType::ON_FIRE) - .await; - } + fn tick<'a>( + &'a self, + caller: Arc, + _server: &'a Server, + ) -> EntityBaseFuture<'a, ()> { + Box::pin(async move { + self.tick_portal(&caller).await; + self.update_fluid_state(&caller).await; + self.check_out_of_world(caller.clone()).await; + let fire_ticks = self.fire_ticks.load(Ordering::Relaxed); + if fire_ticks > 0 { + if self.entity_type.fire_immune { + self.fire_ticks.store(fire_ticks - 4, Ordering::Relaxed); + if self.fire_ticks.load(Ordering::Relaxed) < 0 { + self.extinguish(); + } + } else { + if fire_ticks % 20 == 0 { + caller + .damage(caller.clone(), 1.0, DamageType::ON_FIRE) + .await; + } - self.fire_ticks.store(fire_ticks - 1, Ordering::Relaxed); + self.fire_ticks.store(fire_ticks - 1, Ordering::Relaxed); + } } - } - self.set_on_fire(self.fire_ticks.load(Ordering::Relaxed) > 0) - .await; - // TODO: Tick + self.set_on_fire(self.fire_ticks.load(Ordering::Relaxed) > 0) + .await; + // TODO: Tick + }) } - async fn teleport( + fn teleport( self: Arc, position: Vector3, yaw: Option, pitch: Option, world: Arc, - ) { + ) -> TeleportFuture { // TODO: handle world change - self.teleport(position, yaw, pitch, world).await; + Box::pin(async move { + self.get_entity() + .teleport(position, yaw, pitch, world) + .await; + }) } fn get_entity(&self) -> &Entity { @@ -1883,22 +1924,32 @@ impl EntityBase for Entity { } } -#[async_trait] -pub trait NBTStorage: Send + Sync { - async fn write_nbt(&self, _nbt: &mut NbtCompound) {} +pub type NbtFuture<'a, T> = Pin + Send + 'a>>; - async fn read_nbt(&mut self, nbt: &mut NbtCompound) { - self.read_nbt_non_mut(nbt).await; +pub trait NBTStorage: Send + Sync { + fn write_nbt<'a>(&'a self, _nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async {}) } - async fn read_nbt_non_mut(&self, _nbt: &NbtCompound) {} + fn read_nbt<'a>(&'a mut self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async move { + self.read_nbt_non_mut(nbt).await; + }) + } + + fn read_nbt_non_mut<'a>(&'a self, _nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async {}) + } } -#[async_trait] +pub type NBTInitFuture<'a, T> = Pin> + Send + 'a>>; + pub trait NBTStorageInit: Send + Sync + Sized { - /// Creates an instance of the type from NBT data. If the NBT data is invalid or cannot be parsed, it returns `None`. - async fn create_from_nbt(_nbt: &mut NbtCompound) -> Option { - None + fn create_from_nbt<'a>(_nbt: &'a mut NbtCompound) -> NBTInitFuture<'a, Self> + where + Self: 'a, + { + Box::pin(async move { None }) } } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 6f50907c6..a092bb262 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -7,7 +7,6 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU8, AtomicU32, Ordering}; use std::time::{Duration, Instant}; -use async_trait::async_trait; use crossbeam::atomic::AtomicCell; use crossbeam::channel::Receiver; use log::warn; @@ -39,8 +38,7 @@ use pumpkin_inventory::player::{ player_inventory::PlayerInventory, player_screen_handler::PlayerScreenHandler, }; use pumpkin_inventory::screen_handler::{ - InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFactory, - ScreenHandlerListener, + InventoryPlayer, PlayerFuture, ScreenHandler, ScreenHandlerFactory, ScreenHandlerListener, }; use pumpkin_inventory::sync_handler::SyncHandler; use pumpkin_macros::send_cancellable; @@ -79,6 +77,7 @@ use crate::block::blocks::bed::BedBlock; use crate::command::client_suggestions; use crate::command::dispatcher::CommandDispatcher; use crate::data::op_data::OPERATOR_CONFIG; +use crate::entity::{EntityBaseFuture, NbtFuture, TeleportFuture}; use crate::net::{ClientPlatform, GameProfile}; use crate::net::{DisconnectReason, PlayerConfig}; use crate::plugin::player::player_change_world::PlayerChangeWorldEvent; @@ -407,17 +406,7 @@ impl Player { ) -> Self { struct ScreenListener; - #[async_trait] - impl ScreenHandlerListener for ScreenListener { - async fn on_slot_update( - &self, - _screen_handler: &ScreenHandlerBehaviour, - _slot: u8, - _stack: ItemStack, - ) { - //println!("Slot updated: {slot:?}, {stack:?}"); - } - } + impl ScreenHandlerListener for ScreenListener {} let player_uuid = gameprofile.id; @@ -2022,7 +2011,7 @@ impl Player { let not_in_sync = packet.revision.0 != (behaviour.revision.load(Ordering::Relaxed) as i32); - screen_handler.disable_sync().await; + screen_handler.disable_sync(); screen_handler .on_slot_click( i32::from(slot), @@ -2037,7 +2026,7 @@ impl Player { } screen_handler.set_received_cursor_hash(packet.carried_item); - screen_handler.enable_sync().await; + screen_handler.enable_sync(); if not_in_sync { screen_handler.update_to_client().await; @@ -2086,293 +2075,307 @@ impl PartialEq for Player { } } -#[async_trait] impl NBTStorage for Player { - async fn write_nbt(&self, nbt: &mut NbtCompound) { - nbt.put_int("DataVersion", DATA_VERSION); - self.living_entity.write_nbt(nbt).await; - self.inventory.write_nbt(nbt).await; - self.ender_chest_inventory.write_nbt(nbt).await; + fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async move { + nbt.put_int("DataVersion", DATA_VERSION); + self.living_entity.write_nbt(nbt).await; + self.inventory.write_nbt(nbt).await; + self.ender_chest_inventory.write_nbt(nbt).await; - self.abilities.lock().await.write_nbt(nbt).await; + self.abilities.lock().await.write_nbt(nbt).await; - // Store total XP instead of individual components - let total_exp = experience::points_to_level(self.experience_level.load(Ordering::Relaxed)) - + self.experience_points.load(Ordering::Relaxed); - nbt.put_int("XpTotal", total_exp); - nbt.put_byte("playerGameType", self.gamemode.load() as i8); - if let Some(previous_gamemode) = self.previous_gamemode.load() { - nbt.put_byte("previousPlayerGameType", previous_gamemode as i8); - } + // Store total XP instead of individual components + let total_exp = + experience::points_to_level(self.experience_level.load(Ordering::Relaxed)) + + self.experience_points.load(Ordering::Relaxed); + nbt.put_int("XpTotal", total_exp); + nbt.put_byte("playerGameType", self.gamemode.load() as i8); + if let Some(previous_gamemode) = self.previous_gamemode.load() { + nbt.put_byte("previousPlayerGameType", previous_gamemode as i8); + } - nbt.put_bool( - "HasPlayedBefore", - self.has_played_before.load(Ordering::Relaxed), - ); + nbt.put_bool( + "HasPlayedBefore", + self.has_played_before.load(Ordering::Relaxed), + ); - // Store food level, saturation, exhaustion, and tick timer - self.hunger_manager.write_nbt(nbt).await; + // Store food level, saturation, exhaustion, and tick timer + self.hunger_manager.write_nbt(nbt).await; - nbt.put_string( - "Dimension", - self.world().dimension_type.resource_location().to_string(), - ); + nbt.put_string( + "Dimension", + self.world().dimension_type.resource_location().to_string(), + ); + }) } - async fn read_nbt(&mut self, nbt: &mut NbtCompound) { - self.living_entity.read_nbt(nbt).await; - self.inventory.read_nbt_non_mut(nbt).await; - self.ender_chest_inventory.read_nbt_non_mut(nbt).await; - self.abilities.lock().await.read_nbt(nbt).await; + fn read_nbt<'a>(&'a mut self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async move { + self.living_entity.read_nbt(nbt).await; + self.inventory.read_nbt_non_mut(nbt).await; + self.ender_chest_inventory.read_nbt_non_mut(nbt).await; + self.abilities.lock().await.read_nbt(nbt).await; - self.gamemode.store( - GameMode::try_from(nbt.get_byte("playerGameType").unwrap_or(0)) - .unwrap_or(GameMode::Survival), - ); + self.gamemode.store( + GameMode::try_from(nbt.get_byte("playerGameType").unwrap_or(0)) + .unwrap_or(GameMode::Survival), + ); - self.previous_gamemode.store( - nbt.get_byte("previousPlayerGameType") - .and_then(|byte| GameMode::try_from(byte).ok()), - ); + self.previous_gamemode.store( + nbt.get_byte("previousPlayerGameType") + .and_then(|byte| GameMode::try_from(byte).ok()), + ); - self.has_played_before.store( - nbt.get_bool("HasPlayedBefore").unwrap_or(false), - Ordering::Relaxed, - ); + self.has_played_before.store( + nbt.get_bool("HasPlayedBefore").unwrap_or(false), + Ordering::Relaxed, + ); - // Load food level, saturation, exhaustion, and tick timer - self.hunger_manager.read_nbt(nbt).await; + // Load food level, saturation, exhaustion, and tick timer + self.hunger_manager.read_nbt(nbt).await; - // Load from total XP - let total_exp = nbt.get_int("XpTotal").unwrap_or(0); - let (level, points) = experience::total_to_level_and_points(total_exp); - let progress = experience::progress_in_level(level, points); - self.experience_level.store(level, Ordering::Relaxed); - self.experience_progress.store(progress); - self.experience_points.store(points, Ordering::Relaxed); + // Load from total XP + let total_exp = nbt.get_int("XpTotal").unwrap_or(0); + let (level, points) = experience::total_to_level_and_points(total_exp); + let progress = experience::progress_in_level(level, points); + self.experience_level.store(level, Ordering::Relaxed); + self.experience_progress.store(progress); + self.experience_points.store(points, Ordering::Relaxed); + }) } } impl NBTStorageInit for Player {} -#[async_trait] impl NBTStorage for PlayerInventory { - async fn write_nbt(&self, nbt: &mut NbtCompound) { - // Save the selected slot (hotbar) - nbt.put_int("SelectedItemSlot", i32::from(self.get_selected_slot())); + fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async move { + // Save the selected slot (hotbar) + nbt.put_int("SelectedItemSlot", i32::from(self.get_selected_slot())); - // Create inventory list with the correct capacity (inventory size) - let mut items: Vec = Vec::with_capacity(41); - for (i, item) in self.main_inventory.iter().enumerate() { - let stack = item.lock().await; - if !stack.is_empty() { - let mut item_compound = NbtCompound::new(); - item_compound.put_byte("Slot", i as i8); - stack.write_item_stack(&mut item_compound); - drop(stack); - items.push(NbtTag::Compound(item_compound)); + // Create inventory list with the correct capacity (inventory size) + let mut items: Vec = Vec::with_capacity(41); + for (i, item) in self.main_inventory.iter().enumerate() { + let stack = item.lock().await; + if !stack.is_empty() { + let mut item_compound = NbtCompound::new(); + item_compound.put_byte("Slot", i as i8); + stack.write_item_stack(&mut item_compound); + drop(stack); + items.push(NbtTag::Compound(item_compound)); + } } - } - let mut equipment_compound = NbtCompound::new(); - for slot in self.equipment_slots.values() { - let stack_binding = self.entity_equipment.lock().await.get(slot); - let stack = stack_binding.lock().await; - if !stack.is_empty() { - let mut item_compound = NbtCompound::new(); - stack.write_item_stack(&mut item_compound); - drop(stack); - match slot { - EquipmentSlot::OffHand(_) => { - equipment_compound.put_component("offhand", item_compound); - } - EquipmentSlot::Feet(_) => { - equipment_compound.put_component("feet", item_compound); - } - EquipmentSlot::Legs(_) => { - equipment_compound.put_component("legs", item_compound); - } - EquipmentSlot::Chest(_) => { - equipment_compound.put_component("chest", item_compound); - } - EquipmentSlot::Head(_) => { - equipment_compound.put_component("head", item_compound); - } - _ => { - warn!("Invalid equipment slot for a player {slot:?}"); + let mut equipment_compound = NbtCompound::new(); + for slot in self.equipment_slots.values() { + let stack_binding = self.entity_equipment.lock().await.get(slot); + let stack = stack_binding.lock().await; + if !stack.is_empty() { + let mut item_compound = NbtCompound::new(); + stack.write_item_stack(&mut item_compound); + drop(stack); + match slot { + EquipmentSlot::OffHand(_) => { + equipment_compound.put_component("offhand", item_compound); + } + EquipmentSlot::Feet(_) => { + equipment_compound.put_component("feet", item_compound); + } + EquipmentSlot::Legs(_) => { + equipment_compound.put_component("legs", item_compound); + } + EquipmentSlot::Chest(_) => { + equipment_compound.put_component("chest", item_compound); + } + EquipmentSlot::Head(_) => { + equipment_compound.put_component("head", item_compound); + } + _ => { + warn!("Invalid equipment slot for a player {slot:?}"); + } } } } - } - nbt.put_component("equipment", equipment_compound); - nbt.put("Inventory", NbtTag::List(items)); + nbt.put_component("equipment", equipment_compound); + nbt.put("Inventory", NbtTag::List(items)); + }) } - async fn read_nbt_non_mut(&self, nbt: &NbtCompound) { - // Read selected hotbar slot - self.set_selected_slot(nbt.get_int("SelectedItemSlot").unwrap_or(0) as u8); - // Process inventory list - if let Some(inventory_list) = nbt.get_list("Inventory") { - for tag in inventory_list { - if let Some(item_compound) = tag.extract_compound() - && let Some(slot_byte) = item_compound.get_byte("Slot") - { - let slot = slot_byte as usize; - if let Some(item_stack) = ItemStack::read_item_stack(item_compound) { - self.set_stack(slot, item_stack).await; + fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async { + // Read selected hotbar slot + self.set_selected_slot(nbt.get_int("SelectedItemSlot").unwrap_or(0) as u8); + // Process inventory list + if let Some(inventory_list) = nbt.get_list("Inventory") { + for tag in inventory_list { + if let Some(item_compound) = tag.extract_compound() + && let Some(slot_byte) = item_compound.get_byte("Slot") + { + let slot = slot_byte as usize; + if let Some(item_stack) = ItemStack::read_item_stack(item_compound) { + self.set_stack(slot, item_stack).await; + } } } } - } - if let Some(equipment) = nbt.get_compound("equipment") { - if let Some(offhand) = equipment.get_compound("offhand") - && let Some(item_stack) = ItemStack::read_item_stack(offhand) - { - self.set_stack(40, item_stack).await; - } + if let Some(equipment) = nbt.get_compound("equipment") { + if let Some(offhand) = equipment.get_compound("offhand") + && let Some(item_stack) = ItemStack::read_item_stack(offhand) + { + self.set_stack(40, item_stack).await; + } - if let Some(head) = equipment.get_compound("head") - && let Some(item_stack) = ItemStack::read_item_stack(head) - { - self.set_stack(39, item_stack).await; - } + if let Some(head) = equipment.get_compound("head") + && let Some(item_stack) = ItemStack::read_item_stack(head) + { + self.set_stack(39, item_stack).await; + } - if let Some(chest) = equipment.get_compound("chest") - && let Some(item_stack) = ItemStack::read_item_stack(chest) - { - self.set_stack(38, item_stack).await; - } + if let Some(chest) = equipment.get_compound("chest") + && let Some(item_stack) = ItemStack::read_item_stack(chest) + { + self.set_stack(38, item_stack).await; + } - if let Some(legs) = equipment.get_compound("legs") - && let Some(item_stack) = ItemStack::read_item_stack(legs) - { - self.set_stack(37, item_stack).await; - } + if let Some(legs) = equipment.get_compound("legs") + && let Some(item_stack) = ItemStack::read_item_stack(legs) + { + self.set_stack(37, item_stack).await; + } - if let Some(feet) = equipment.get_compound("feet") - && let Some(item_stack) = ItemStack::read_item_stack(feet) - { - self.set_stack(36, item_stack).await; + if let Some(feet) = equipment.get_compound("feet") + && let Some(item_stack) = ItemStack::read_item_stack(feet) + { + self.set_stack(36, item_stack).await; + } } - } + }) } } impl NBTStorageInit for PlayerInventory {} -#[async_trait] impl NBTStorage for EnderChestInventory { - async fn write_nbt(&self, nbt: &mut NbtCompound) { - // Create item list with the correct capacity (inventory size) - let mut items: Vec = Vec::with_capacity(Self::INVENTORY_SIZE); - for (i, item) in self.items.iter().enumerate() { - let stack = item.lock().await; - if !stack.is_empty() { - let mut item_compound = NbtCompound::new(); - item_compound.put_byte("Slot", i as i8); - stack.write_item_stack(&mut item_compound); - drop(stack); - items.push(NbtTag::Compound(item_compound)); + fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async { + // Create item list with the correct capacity (inventory size) + let mut items: Vec = Vec::with_capacity(Self::INVENTORY_SIZE); + for (i, item) in self.items.iter().enumerate() { + let stack = item.lock().await; + if !stack.is_empty() { + let mut item_compound = NbtCompound::new(); + item_compound.put_byte("Slot", i as i8); + stack.write_item_stack(&mut item_compound); + drop(stack); + items.push(NbtTag::Compound(item_compound)); + } } - } - nbt.put("EnderItems", NbtTag::List(items)); + nbt.put("EnderItems", NbtTag::List(items)); + }) } - async fn read_nbt_non_mut(&self, nbt: &NbtCompound) { - // Process item list - if let Some(item_list) = nbt.get_list("EnderItems") { - for tag in item_list { - if let Some(item_compound) = tag.extract_compound() - && let Some(slot_byte) = item_compound.get_byte("Slot") - { - let slot = slot_byte as usize; - if let Some(item_stack) = ItemStack::read_item_stack(item_compound) { - self.set_stack(slot, item_stack).await; + fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async { + // Process item list + if let Some(item_list) = nbt.get_list("EnderItems") { + for tag in item_list { + if let Some(item_compound) = tag.extract_compound() + && let Some(slot_byte) = item_compound.get_byte("Slot") + { + let slot = slot_byte as usize; + if let Some(item_stack) = ItemStack::read_item_stack(item_compound) { + self.set_stack(slot, item_stack).await; + } } } } - } + }) } } impl NBTStorageInit for EnderChestInventory {} -#[async_trait] impl EntityBase for Player { - async fn damage_with_context( - &self, + fn damage_with_context<'a>( + &'a self, caller: Arc, amount: f32, damage_type: DamageType, position: Option>, - source: Option<&dyn EntityBase>, - cause: Option<&dyn EntityBase>, - ) -> bool { - if self.abilities.lock().await.invulnerable && damage_type != DamageType::GENERIC_KILL { - return false; - } - let dyn_self = self - .living_entity - .entity - .world - .get_entity_by_id(self.living_entity.entity.entity_id) - .await - .expect("Entity not found in world"); - let result = self - .living_entity - .damage_with_context(caller, amount, damage_type, position, source, cause) - .await; - if result { - let health = self.living_entity.health.load(); - if health <= 0.0 { - let death_message = - LivingEntity::get_death_message(&*dyn_self, damage_type, source, cause).await; - self.handle_killed(death_message).await; + source: Option<&'a dyn EntityBase>, + cause: Option<&'a dyn EntityBase>, + ) -> EntityBaseFuture<'a, bool> { + Box::pin(async move { + if self.abilities.lock().await.invulnerable && damage_type != DamageType::GENERIC_KILL { + return false; } - } - result + let dyn_self = self + .living_entity + .entity + .world + .get_entity_by_id(self.living_entity.entity.entity_id) + .await + .expect("Entity not found in world"); + let result = self + .living_entity + .damage_with_context(caller, amount, damage_type, position, source, cause) + .await; + if result { + let health = self.living_entity.health.load(); + if health <= 0.0 { + let death_message = + LivingEntity::get_death_message(&*dyn_self, damage_type, source, cause) + .await; + self.handle_killed(death_message).await; + } + } + result + }) } - async fn teleport( + fn teleport( self: Arc, position: Vector3, yaw: Option, pitch: Option, world: Arc, - ) { - if Arc::ptr_eq(&world, self.world()) { - // Same world - let yaw = yaw.unwrap_or(self.living_entity.entity.yaw.load()); - let pitch = pitch.unwrap_or(self.living_entity.entity.pitch.load()); - send_cancellable! {{ - PlayerTeleportEvent { - player: self.clone(), - from: self.living_entity.entity.pos.load(), - to: position, - cancelled: false, - }; - 'after: { - let position = event.to; - let entity = self.get_entity(); - self.request_teleport(position, yaw, pitch).await; - entity - .world - .broadcast_packet_except(&[self.gameprofile.id], &CEntityPositionSync::new( - self.living_entity.entity.entity_id.into(), - position, - Vector3::new(0.0, 0.0, 0.0), - yaw, - pitch, - entity.on_ground.load(Ordering::SeqCst), - )) - .await; - } - }} - } else { - self.teleport_world(world, position, yaw, pitch).await; - } + ) -> TeleportFuture { + Box::pin(async move { + if Arc::ptr_eq(&world, self.world()) { + // Same world + let yaw = yaw.unwrap_or(self.living_entity.entity.yaw.load()); + let pitch = pitch.unwrap_or(self.living_entity.entity.pitch.load()); + send_cancellable! {{ + PlayerTeleportEvent { + player: self.clone(), + from: self.living_entity.entity.pos.load(), + to: position, + cancelled: false, + }; + 'after: { + let position = event.to; + let entity = self.get_entity(); + self.request_teleport(position, yaw, pitch).await; + entity + .world + .broadcast_packet_except(&[self.gameprofile.id], &CEntityPositionSync::new( + self.living_entity.entity.entity_id.into(), + position, + Vector3::new(0.0, 0.0, 0.0), + yaw, + pitch, + entity.on_ground.load(Ordering::SeqCst), + )) + .await; + } + }} + } else { + self.teleport_world(world, position, yaw, pitch).await; + } + }) } fn get_entity(&self) -> &Entity { @@ -2396,7 +2399,7 @@ impl EntityBase for Player { TextComponent::text(self.gameprofile.name.clone()) } - async fn get_display_name(&self) -> TextComponent { + fn get_display_name(&self) -> EntityBaseFuture<'_, TextComponent> { let name = self.get_name(); let name_clone = name.clone(); let mut name = name.click_event(ClickEvent::SuggestCommand { @@ -2407,7 +2410,7 @@ impl EntityBase for Player { self.living_entity.entity.entity_type.resource_name.into(), Some(name_clone), )); - name.insertion(self.gameprofile.name.clone()) + Box::pin(async move { name.insertion(self.gameprofile.name.clone()) }) } fn as_nbt_storage(&self) -> &dyn NBTStorage { @@ -2442,30 +2445,33 @@ pub struct Abilities { pub walk_speed: f32, } -#[async_trait] impl NBTStorage for Abilities { - async fn write_nbt(&self, nbt: &mut NbtCompound) { - let mut component = NbtCompound::new(); - component.put_bool("invulnerable", self.invulnerable); - component.put_bool("flying", self.flying); - component.put_bool("mayfly", self.allow_flying); - component.put_bool("instabuild", self.creative); - component.put_bool("mayBuild", self.allow_modify_world); - component.put_float("flySpeed", self.fly_speed); - component.put_float("walkSpeed", self.walk_speed); - nbt.put_component("abilities", component); + fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async { + let mut component = NbtCompound::new(); + component.put_bool("invulnerable", self.invulnerable); + component.put_bool("flying", self.flying); + component.put_bool("mayfly", self.allow_flying); + component.put_bool("instabuild", self.creative); + component.put_bool("mayBuild", self.allow_modify_world); + component.put_float("flySpeed", self.fly_speed); + component.put_float("walkSpeed", self.walk_speed); + nbt.put_component("abilities", component); + }) } - async fn read_nbt(&mut self, nbt: &mut NbtCompound) { - if let Some(component) = nbt.get_compound("abilities") { - self.invulnerable = component.get_bool("invulnerable").unwrap_or(false); - self.flying = component.get_bool("flying").unwrap_or(false); - self.allow_flying = component.get_bool("mayfly").unwrap_or(false); - self.creative = component.get_bool("instabuild").unwrap_or(false); - self.allow_modify_world = component.get_bool("mayBuild").unwrap_or(false); - self.fly_speed = component.get_float("flySpeed").unwrap_or(0.05); - self.walk_speed = component.get_float("walkSpeed").unwrap_or(0.1); - } + fn read_nbt<'a>(&'a mut self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { + Box::pin(async move { + if let Some(component) = nbt.get_compound("abilities") { + self.invulnerable = component.get_bool("invulnerable").unwrap_or(false); + self.flying = component.get_bool("flying").unwrap_or(false); + self.allow_flying = component.get_bool("mayfly").unwrap_or(false); + self.creative = component.get_bool("instabuild").unwrap_or(false); + self.allow_modify_world = component.get_bool("mayBuild").unwrap_or(false); + self.fly_speed = component.get_float("flySpeed").unwrap_or(0.05); + self.walk_speed = component.get_float("walkSpeed").unwrap_or(0.1); + } + }) } } @@ -2674,12 +2680,14 @@ impl MessageCache { } } -#[async_trait] impl InventoryPlayer for Player { - async fn drop_item(&self, item: ItemStack, _retain_ownership: bool) { - self.drop_item(item).await; + fn drop_item(&self, item: ItemStack, _retain_ownership: bool) -> PlayerFuture<'_, ()> { + Box::pin(async move { + self.drop_item(item).await; + }) } + // Synchronous methods remain unchanged fn has_infinite_materials(&self) -> bool { self.gamemode.load() == GameMode::Creative } @@ -2688,55 +2696,85 @@ impl InventoryPlayer for Player { self.inventory.clone() } - async fn enqueue_inventory_packet(&self, packet: &CSetContainerContent) { - self.client.enqueue_packet(packet).await; + fn enqueue_inventory_packet<'a>( + &'a self, + packet: &'a CSetContainerContent, + ) -> PlayerFuture<'a, ()> { + Box::pin(async move { + self.client.enqueue_packet(packet).await; + }) } - async fn enqueue_slot_packet(&self, packet: &CSetContainerSlot) { - self.client.enqueue_packet(packet).await; + fn enqueue_slot_packet<'a>(&'a self, packet: &'a CSetContainerSlot) -> PlayerFuture<'a, ()> { + Box::pin(async move { + self.client.enqueue_packet(packet).await; + }) } - async fn enqueue_cursor_packet(&self, packet: &CSetCursorItem) { - self.client.enqueue_packet(packet).await; + fn enqueue_cursor_packet<'a>(&'a self, packet: &'a CSetCursorItem) -> PlayerFuture<'a, ()> { + Box::pin(async move { + self.client.enqueue_packet(packet).await; + }) } - async fn enqueue_property_packet(&self, packet: &CSetContainerProperty) { - self.client.enqueue_packet(packet).await; + fn enqueue_property_packet<'a>( + &'a self, + packet: &'a CSetContainerProperty, + ) -> PlayerFuture<'a, ()> { + Box::pin(async move { + self.client.enqueue_packet(packet).await; + }) } - async fn enqueue_slot_set_packet(&self, packet: &CSetPlayerInventory) { - self.client.enqueue_packet(packet).await; + fn enqueue_slot_set_packet<'a>( + &'a self, + packet: &'a CSetPlayerInventory, + ) -> PlayerFuture<'a, ()> { + Box::pin(async move { + self.client.enqueue_packet(packet).await; + }) } - async fn enqueue_set_held_item_packet(&self, packet: &CSetSelectedSlot) { - self.client.enqueue_packet(packet).await; + fn enqueue_set_held_item_packet<'a>( + &'a self, + packet: &'a CSetSelectedSlot, + ) -> PlayerFuture<'a, ()> { + Box::pin(async move { + self.client.enqueue_packet(packet).await; + }) } - async fn enqueue_equipment_change(&self, slot: &EquipmentSlot, stack: &ItemStack) { - self.world() - .broadcast_packet_except( - &[self.get_entity().entity_uuid], - &CSetEquipment::new( - self.entity_id().into(), - vec![( - slot.discriminant(), - ItemStackSerializer::from(stack.clone()), - )], - ), - ) - .await; - - if let Some(equippable) = stack.get_data_component::() - && let Some(sound) = Sound::from_name( - equippable - .equip_sound - .strip_prefix("minecraft:") - .unwrap_or(equippable.equip_sound), - ) - { + fn enqueue_equipment_change<'a>( + &'a self, + slot: &'a EquipmentSlot, + stack: &'a ItemStack, + ) -> PlayerFuture<'a, ()> { + Box::pin(async move { self.world() - .play_sound(sound, SoundCategory::Players, &self.position()) + .broadcast_packet_except( + &[self.get_entity().entity_uuid], + &CSetEquipment::new( + self.entity_id().into(), + vec![( + slot.discriminant(), + ItemStackSerializer::from(stack.clone()), + )], + ), + ) .await; - } + + if let Some(equippable) = stack.get_data_component::() + && let Some(sound) = Sound::from_name( + equippable + .equip_sound + .strip_prefix("minecraft:") + .unwrap_or(equippable.equip_sound), + ) + { + self.world() + .play_sound(sound, SoundCategory::Players, &self.position()) + .await; + } + }) } } diff --git a/pumpkin/src/entity/projectile/mod.rs b/pumpkin/src/entity/projectile/mod.rs index 63886c4ce..c9718bcfb 100644 --- a/pumpkin/src/entity/projectile/mod.rs +++ b/pumpkin/src/entity/projectile/mod.rs @@ -4,7 +4,6 @@ use std::{ }; use super::{Entity, EntityBase, NBTStorage, living::LivingEntity}; -use async_trait::async_trait; use pumpkin_util::math::vector3::Vector3; pub mod wind_charge; @@ -80,7 +79,6 @@ impl ThrownItemEntity { impl NBTStorage for ThrownItemEntity {} -#[async_trait] impl EntityBase for ThrownItemEntity { fn get_entity(&self) -> &Entity { &self.entity diff --git a/pumpkin/src/entity/projectile/wind_charge.rs b/pumpkin/src/entity/projectile/wind_charge.rs index d055196ef..90156d553 100644 --- a/pumpkin/src/entity/projectile/wind_charge.rs +++ b/pumpkin/src/entity/projectile/wind_charge.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_util::math::vector3::Vector3; use std::{ f64, @@ -10,8 +9,8 @@ use std::{ use crate::{ entity::{ - Entity, EntityBase, NBTStorage, living::LivingEntity, projectile::ThrownItemEntity, - projectile_deflection::ProjectileDeflectionType, + Entity, EntityBase, EntityBaseFuture, NBTStorage, living::LivingEntity, + projectile::ThrownItemEntity, projectile_deflection::ProjectileDeflectionType, }, server::Server, }; @@ -96,14 +95,19 @@ impl WindChargeEntity { impl NBTStorage for WindChargeEntity {} -#[async_trait] impl EntityBase for WindChargeEntity { - async fn tick(&self, caller: Arc, server: &Server) { - if self.get_deflect_cooldown() > 0 { - self.set_deflect_cooldown(self.get_deflect_cooldown() - 1); - } + fn tick<'a>( + &'a self, + caller: Arc, + server: &'a Server, + ) -> EntityBaseFuture<'a, ()> { + Box::pin(async move { + if self.get_deflect_cooldown() > 0 { + self.set_deflect_cooldown(self.get_deflect_cooldown() - 1); + } - self.thrown_item_entity.tick(caller, server).await; + self.thrown_item_entity.tick(caller, server).await; + }) } fn get_entity(&self) -> &Entity { diff --git a/pumpkin/src/entity/tnt.rs b/pumpkin/src/entity/tnt.rs index 55984adba..85c4fc22e 100644 --- a/pumpkin/src/entity/tnt.rs +++ b/pumpkin/src/entity/tnt.rs @@ -1,6 +1,5 @@ use super::{Entity, EntityBase, NBTStorage, living::LivingEntity}; -use crate::server::Server; -use async_trait::async_trait; +use crate::{entity::EntityBaseFuture, server::Server}; use core::f32; use pumpkin_data::Block; use pumpkin_protocol::{ @@ -37,63 +36,70 @@ impl TNTEntity { impl NBTStorage for TNTEntity {} -#[async_trait] impl EntityBase for TNTEntity { - async fn tick(&self, caller: Arc, server: &Server) { - let entity = &self.entity; - let original_velo = entity.velocity.load(); + fn tick<'a>( + &'a self, + caller: Arc, + server: &'a Server, + ) -> EntityBaseFuture<'a, ()> { + Box::pin(async move { + let entity = &self.entity; + let original_velo = entity.velocity.load(); - let mut velo = original_velo; - velo.y -= self.get_gravity(); + let mut velo = original_velo; + velo.y -= self.get_gravity(); - entity.move_entity(caller.clone(), velo).await; - entity.tick_block_collisions(&caller, server).await; - entity.velocity.store(velo.multiply(0.98, 0.98, 0.98)); - if entity.on_ground.load(Ordering::Relaxed) { - entity.velocity.store(velo.multiply(0.7, -0.5, 0.7)); - } - let velocity_dirty = entity.velocity_dirty.swap(false, Ordering::SeqCst); + entity.move_entity(caller.clone(), velo).await; + entity.tick_block_collisions(&caller, server).await; + entity.velocity.store(velo.multiply(0.98, 0.98, 0.98)); + if entity.on_ground.load(Ordering::Relaxed) { + entity.velocity.store(velo.multiply(0.7, -0.5, 0.7)); + } + let velocity_dirty = entity.velocity_dirty.swap(false, Ordering::SeqCst); - if velocity_dirty { - entity.send_pos_rot().await; + if velocity_dirty { + entity.send_pos_rot().await; - entity.send_velocity().await; - } + entity.send_velocity().await; + } - let fuse = self.fuse.fetch_sub(1, Relaxed); - if fuse == 0 { - self.entity.remove().await; - self.entity - .world - .explode(self.entity.pos.load(), self.power) - .await; - } else { - entity.update_fluid_state(&caller).await; - } + let fuse = self.fuse.fetch_sub(1, Relaxed); + if fuse == 0 { + self.entity.remove().await; + self.entity + .world + .explode(self.entity.pos.load(), self.power) + .await; + } else { + entity.update_fluid_state(&caller).await; + } + }) } - async fn init_data_tracker(&self) { - // TODO: Yes, this is the wrong function, but we need to send this after spawning the entity. - let pos: f64 = rand::random::() * TAU; + fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> { + Box::pin(async { + // TODO: Yes, this is the wrong function, but we need to send this after spawning the entity. + let pos: f64 = rand::random::() * TAU; - self.entity - .set_velocity(Vector3::new(-pos.sin() * 0.02, 0.2, -pos.cos() * 0.02)) - .await; - // We can merge multiple `Metadata`s into one meta packet. - self.entity - .send_meta_data(&[ - Metadata::new( - 8, - MetaDataType::Integer, - VarInt(self.fuse.load(Relaxed) as i32), - ), - Metadata::new( - 9, - MetaDataType::BlockState, - VarInt(i32::from(Block::TNT.default_state.id)), - ), - ]) - .await; + self.entity + .set_velocity(Vector3::new(-pos.sin() * 0.02, 0.2, -pos.cos() * 0.02)) + .await; + // We can merge multiple `Metadata`s into one meta packet. + self.entity + .send_meta_data(&[ + Metadata::new( + 8, + MetaDataType::Integer, + VarInt(self.fuse.load(Relaxed) as i32), + ), + Metadata::new( + 9, + MetaDataType::BlockState, + VarInt(i32::from(Block::TNT.default_state.id)), + ), + ]) + .await; + }) } fn get_entity(&self) -> &Entity { diff --git a/pumpkin/src/item/items/armor_stand.rs b/pumpkin/src/item/items/armor_stand.rs index f33357545..dc2ba37fd 100644 --- a/pumpkin/src/item/items/armor_stand.rs +++ b/pumpkin/src/item/items/armor_stand.rs @@ -1,3 +1,4 @@ +use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; @@ -5,7 +6,6 @@ use crate::entity::decoration::armor_stand::ArmorStandEntity; use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; use crate::server::Server; -use async_trait::async_trait; use pumpkin_data::entity::EntityType; use pumpkin_data::item::Item; use pumpkin_data::sound::{Sound, SoundCategory}; @@ -38,67 +38,68 @@ impl ItemMetadata for ArmorStandItem { } } -#[async_trait] impl ItemBehaviour for ArmorStandItem { - async fn use_on_block( - &self, - _item: &mut ItemStack, - player: &Player, + fn use_on_block<'a>( + &'a self, + _item: &'a mut ItemStack, + player: &'a Player, location: BlockPos, face: BlockDirection, - _block: &Block, - _server: &Server, - ) { - let world = player.world(); - let position = Self::calculate_placement_position(&location, face).to_f64(); + _block: &'a Block, + _server: &'a Server, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let world = player.world(); + let position = Self::calculate_placement_position(&location, face).to_f64(); - let bottom_center = Vector3::new(position.x, position.y, position.z); + let bottom_center = Vector3::new(position.x, position.y, position.z); - let armor_stand_dimensions = EntityType::ARMOR_STAND.dimension; - let width = f64::from(armor_stand_dimensions[0]); - let height = f64::from(armor_stand_dimensions[1]); + let armor_stand_dimensions = EntityType::ARMOR_STAND.dimension; + let width = f64::from(armor_stand_dimensions[0]); + let height = f64::from(armor_stand_dimensions[1]); - let bounding_box = BoundingBox::new( - Vector3::new( - bottom_center.x - width / 2.0, - bottom_center.y, - bottom_center.z - width / 2.0, - ), - Vector3::new( - bottom_center.x + width / 2.0, - bottom_center.y + height, - bottom_center.z + width / 2.0, - ), - ); - - if world.is_space_empty(bounding_box).await - && world.get_entities_at_box(&bounding_box).await.is_empty() - { - let (player_yaw, _) = player.rotation(); - let rotation = ((wrap_degrees(player_yaw - 180.0) + 22.5) / 45.0).floor() * 45.0; - - let entity = Entity::new( - Uuid::new_v4(), - world.clone(), - position, - &EntityType::ARMOR_STAND, - false, + let bounding_box = BoundingBox::new( + Vector3::new( + bottom_center.x - width / 2.0, + bottom_center.y, + bottom_center.z - width / 2.0, + ), + Vector3::new( + bottom_center.x + width / 2.0, + bottom_center.y + height, + bottom_center.z + width / 2.0, + ), ); - entity.set_rotation(rotation, 0.0); + if world.is_space_empty(bounding_box).await + && world.get_entities_at_box(&bounding_box).await.is_empty() + { + let (player_yaw, _) = player.rotation(); + let rotation = ((wrap_degrees(player_yaw - 180.0) + 22.5) / 45.0).floor() * 45.0; - world - .play_sound( - Sound::EntityArmorStandPlace, - SoundCategory::Blocks, - &entity.pos.load(), - ) - .await; + let entity = Entity::new( + Uuid::new_v4(), + world.clone(), + position, + &EntityType::ARMOR_STAND, + false, + ); - let armor_stand = ArmorStandEntity::new(entity); + entity.set_rotation(rotation, 0.0); - world.spawn_entity(Arc::new(armor_stand)).await; - } + world + .play_sound( + Sound::EntityArmorStandPlace, + SoundCategory::Blocks, + &entity.pos.load(), + ) + .await; + + let armor_stand = ArmorStandEntity::new(entity); + + world.spawn_entity(Arc::new(armor_stand)).await; + } + }) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/pumpkin/src/item/items/axe.rs b/pumpkin/src/item/items/axe.rs index d857a13c1..32d9902e0 100644 --- a/pumpkin/src/item/items/axe.rs +++ b/pumpkin/src/item/items/axe.rs @@ -1,7 +1,8 @@ +use std::pin::Pin; + use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; use crate::server::Server; -use async_trait::async_trait; use pumpkin_data::BlockDirection; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::{OakDoorLikeProperties, PaleOakWoodLikeProperties}; @@ -19,63 +20,64 @@ impl ItemMetadata for AxeItem { } } -#[async_trait] impl ItemBehaviour for AxeItem { #[allow(clippy::too_many_lines)] - async fn use_on_block( - &self, - _item: &mut ItemStack, - player: &Player, + fn use_on_block<'a>( + &'a self, + _item: &'a mut ItemStack, + player: &'a Player, location: BlockPos, _face: BlockDirection, - block: &Block, - _server: &Server, - ) { - // I tried to follow mojang order of doing things. - let world = player.world(); - let replacement_block = try_use_axe(block); - // First we try to strip the block. by getting his equivalent and applying it the axis. + block: &'a Block, + _server: &'a Server, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + // I tried to follow mojang order of doing things. + let world = player.world(); + let replacement_block = try_use_axe(block); + // First we try to strip the block. by getting his equivalent and applying it the axis. - // If there is a strip equivalent. - if replacement_block != 0 { - let new_block = &Block::from_id(replacement_block); - let new_state_id = if block.has_tag(&tag::Block::MINECRAFT_LOGS) { - let log_information = world.get_block_state_id(&location).await; - let log_props = PaleOakWoodLikeProperties::from_state_id(log_information, block); - // create new properties for the new log. - let mut new_log_properties = PaleOakWoodLikeProperties::default(new_block); - new_log_properties.axis = log_props.axis; + // If there is a strip equivalent. + if replacement_block != 0 { + let new_block = &Block::from_id(replacement_block); + let new_state_id = if block.has_tag(&tag::Block::MINECRAFT_LOGS) { + let log_information = world.get_block_state_id(&location).await; + let log_props = + PaleOakWoodLikeProperties::from_state_id(log_information, block); + // create new properties for the new log. + let mut new_log_properties = PaleOakWoodLikeProperties::default(new_block); + new_log_properties.axis = log_props.axis; - // create new properties for the new log. + // create new properties for the new log. - // Set old axis to the new log. - new_log_properties.axis = log_props.axis; - new_log_properties.to_state_id(new_block) + // Set old axis to the new log. + new_log_properties.axis = log_props.axis; + new_log_properties.to_state_id(new_block) + } + // Let's check if It's a door + else if block.has_tag(&tag::Block::MINECRAFT_DOORS) { + // get block state of the old log. + let door_information = world.get_block_state_id(&location).await; + // get the log properties + let door_props = OakDoorLikeProperties::from_state_id(door_information, block); + // create new properties for the new log. + let mut new_door_properties = OakDoorLikeProperties::default(new_block); + // Set old axis to the new log. + new_door_properties.facing = door_props.facing; + new_door_properties.open = door_props.open; + new_door_properties.half = door_props.half; + new_door_properties.hinge = door_props.hinge; + new_door_properties.powered = door_props.powered; + new_door_properties.to_state_id(new_block) + } else { + new_block.default_state.id + }; + // TODO Implements trapdoors when It's implemented + world + .set_block_state(&location, new_state_id, BlockFlags::NOTIFY_ALL) + .await; } - // Let's check if It's a door - else if block.has_tag(&tag::Block::MINECRAFT_DOORS) { - // get block state of the old log. - let door_information = world.get_block_state_id(&location).await; - // get the log properties - let door_props = OakDoorLikeProperties::from_state_id(door_information, block); - // create new properties for the new log. - let mut new_door_properties = OakDoorLikeProperties::default(new_block); - // Set old axis to the new log. - new_door_properties.facing = door_props.facing; - new_door_properties.open = door_props.open; - new_door_properties.half = door_props.half; - new_door_properties.hinge = door_props.hinge; - new_door_properties.powered = door_props.powered; - new_door_properties.to_state_id(new_block) - } else { - new_block.default_state.id - }; - // TODO Implements trapdoors when It's implemented - world - .set_block_state(&location, new_state_id, BlockFlags::NOTIFY_ALL) - .await; - return; - } + }) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/pumpkin/src/item/items/bucket.rs b/pumpkin/src/item/items/bucket.rs index 8c0ea5078..afc75c79c 100644 --- a/pumpkin/src/item/items/bucket.rs +++ b/pumpkin/src/item/items/bucket.rs @@ -1,10 +1,9 @@ -use std::sync::Arc; +use std::{pin::Pin, sync::Arc}; use crate::{ entity::player::Player, item::{ItemBehaviour, ItemMetadata}, }; -use async_trait::async_trait; use pumpkin_data::{ Block, fluid::Fluid, @@ -94,113 +93,121 @@ fn set_waterlogged(block: &Block, state: u16, waterlogged: bool) -> u16 { block.from_properties(&props).to_state_id(block) } -#[async_trait] impl ItemBehaviour for EmptyBucketItem { - async fn normal_use(&self, _item: &Item, player: &Player) { - let world = player.world(); - let (start_pos, end_pos) = get_start_and_end_pos(player); + fn normal_use<'a>( + &'a self, + _block: &'a Item, + player: &'a Player, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let world = player.world(); + let (start_pos, end_pos) = get_start_and_end_pos(player); - let checker = async |pos: &BlockPos, world_inner: &Arc| { - let state_id = world_inner.get_block_state_id(pos).await; + let checker = async |pos: &BlockPos, world_inner: &Arc| { + let state_id = world_inner.get_block_state_id(pos).await; - let block = Block::from_state_id(state_id); + let block = Block::from_state_id(state_id); - if state_id == Block::AIR.default_state.id { - return false; - } + if state_id == Block::AIR.default_state.id { + return false; + } - (block.id != Block::WATER.id && block.id != Block::LAVA.id) - || ((block.id == Block::WATER.id && state_id == Block::WATER.default_state.id) - || (block.id == Block::LAVA.id && state_id == Block::LAVA.default_state.id)) - }; + (block.id != Block::WATER.id && block.id != Block::LAVA.id) + || ((block.id == Block::WATER.id && state_id == Block::WATER.default_state.id) + || (block.id == Block::LAVA.id && state_id == Block::LAVA.default_state.id)) + }; - let Some((block_pos, direction)) = world.raycast(start_pos, end_pos, checker).await else { - return; - }; + let Some((block_pos, direction)) = world.raycast(start_pos, end_pos, checker).await + else { + return; + }; - let (block, state) = world.get_block_and_state_id(&block_pos).await; + let (block, state) = world.get_block_and_state_id(&block_pos).await; - if block - .properties(state) - .and_then(|properties| { - properties - .to_props() - .into_iter() - .find(|p| p.0 == "waterlogged") - .map(|(_, value)| value == true.to_string()) - }) - .unwrap_or(false) - { - let state_id = set_waterlogged(block, state, false); - world - .set_block_state(&block_pos, state_id, BlockFlags::NOTIFY_NEIGHBORS) - .await; - world - .schedule_fluid_tick(&Fluid::WATER, block_pos, 5, TickPriority::Normal) - .await; - } else if state == Block::LAVA.default_state.id || state == Block::WATER.default_state.id { - world - .break_block(&block_pos, None, BlockFlags::NOTIFY_NEIGHBORS) - .await; - world - .set_block_state( - &block_pos, - Block::AIR.default_state.id, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; - } else { - let (block, state) = world - .get_block_and_state_id(&block_pos.offset(direction.to_offset())) - .await; - if waterlogged_check(block, state).is_some() { + if block + .properties(state) + .and_then(|properties| { + properties + .to_props() + .into_iter() + .find(|p| p.0 == "waterlogged") + .map(|(_, value)| value == true.to_string()) + }) + .unwrap_or(false) + { let state_id = set_waterlogged(block, state, false); + world + .set_block_state(&block_pos, state_id, BlockFlags::NOTIFY_NEIGHBORS) + .await; + world + .schedule_fluid_tick(&Fluid::WATER, block_pos, 5, TickPriority::Normal) + .await; + } else if state == Block::LAVA.default_state.id + || state == Block::WATER.default_state.id + { + world + .break_block(&block_pos, None, BlockFlags::NOTIFY_NEIGHBORS) + .await; world .set_block_state( - &block_pos.offset(direction.to_offset()), - state_id, + &block_pos, + Block::AIR.default_state.id, BlockFlags::NOTIFY_NEIGHBORS, ) .await; - world - .schedule_fluid_tick( - &Fluid::WATER, - block_pos.offset(direction.to_offset()), - 5, - TickPriority::Normal, - ) - .await; } else { - return; - } - } - - let item = if state == Block::LAVA.default_state.id { - &Item::LAVA_BUCKET - } else { - &Item::WATER_BUCKET - }; - - if player.gamemode.load() == GameMode::Creative { - //Check if player already has the item in their inventory - for i in 0..player.inventory.main_inventory.len() { - if player.inventory.main_inventory[i].lock().await.item.id == item.id { + let (block, state) = world + .get_block_and_state_id(&block_pos.offset(direction.to_offset())) + .await; + if waterlogged_check(block, state).is_some() { + let state_id = set_waterlogged(block, state, false); + world + .set_block_state( + &block_pos.offset(direction.to_offset()), + state_id, + BlockFlags::NOTIFY_NEIGHBORS, + ) + .await; + world + .schedule_fluid_tick( + &Fluid::WATER, + block_pos.offset(direction.to_offset()), + 5, + TickPriority::Normal, + ) + .await; + } else { return; } } - //If not, add it to the inventory - let mut item_stack = ItemStack::new(1, item); - player - .inventory - .insert_stack_anywhere(&mut item_stack) - .await; - } else { - let item_stack = ItemStack::new(1, item); - player - .inventory - .set_stack(player.inventory.get_selected_slot().into(), item_stack) - .await; - } + + let item = if state == Block::LAVA.default_state.id { + &Item::LAVA_BUCKET + } else { + &Item::WATER_BUCKET + }; + + if player.gamemode.load() == GameMode::Creative { + //Check if player already has the item in their inventory + for i in 0..player.inventory.main_inventory.len() { + if player.inventory.main_inventory[i].lock().await.item.id == item.id { + return; + } + } + //If not, add it to the inventory + let mut item_stack = ItemStack::new(1, item); + player + .inventory + .insert_stack_anywhere(&mut item_stack) + .await; + } else { + let item_stack = ItemStack::new(1, item); + player + .inventory + .set_stack(player.inventory.get_selected_slot().into(), item_stack) + .await; + } + }) } fn as_any(&self) -> &dyn std::any::Any { @@ -208,97 +215,102 @@ impl ItemBehaviour for EmptyBucketItem { } } -#[async_trait] impl ItemBehaviour for FilledBucketItem { - async fn normal_use(&self, item: &Item, player: &Player) { - let world = player.world(); - let (start_pos, end_pos) = get_start_and_end_pos(player); - let checker = async |pos: &BlockPos, world_inner: &Arc| { - let state_id = world_inner.get_block_state_id(pos).await; - if Fluid::from_state_id(state_id).is_some() { - return false; - } - state_id != Block::AIR.id - }; - - let Some((pos, direction)) = world.raycast(start_pos, end_pos, checker).await else { - return; - }; - - if item.id != Item::LAVA_BUCKET.id - && world.dimension_type == VanillaDimensionType::TheNether - { - world - .play_sound_raw( - Sound::BlockFireExtinguish as u16, - SoundCategory::Blocks, - &player.position(), - 0.5, - 2.6 + (rand::random::() - rand::random::()) * 0.8, - ) - .await; - return; - } - let (block, state) = world.get_block_and_state_id(&pos).await; - if waterlogged_check(block, state).is_some() && item.id == Item::WATER_BUCKET.id { - let state_id = set_waterlogged(block, state, true); - world - .set_block_state(&pos, state_id, BlockFlags::NOTIFY_NEIGHBORS) - .await; - world - .schedule_fluid_tick(&Fluid::WATER, pos, 5, TickPriority::Normal) - .await; - } else { - let (block, state) = world - .get_block_and_state(&pos.offset(direction.to_offset())) - .await; - - if waterlogged_check(block, state.id).is_some() { - if item.id == Item::LAVA_BUCKET.id { - return; + fn normal_use<'a>( + &'a self, + item: &'a Item, + player: &'a Player, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let world = player.world(); + let (start_pos, end_pos) = get_start_and_end_pos(player); + let checker = async |pos: &BlockPos, world_inner: &Arc| { + let state_id = world_inner.get_block_state_id(pos).await; + if Fluid::from_state_id(state_id).is_some() { + return false; } - let state_id = set_waterlogged(block, state.id, true); + state_id != Block::AIR.id + }; + let Some((pos, direction)) = world.raycast(start_pos, end_pos, checker).await else { + return; + }; + + if item.id != Item::LAVA_BUCKET.id + && world.dimension_type == VanillaDimensionType::TheNether + { world - .set_block_state( - &pos.offset(direction.to_offset()), - state_id, - BlockFlags::NOTIFY_NEIGHBORS, + .play_sound_raw( + Sound::BlockFireExtinguish as u16, + SoundCategory::Blocks, + &player.position(), + 0.5, + 2.6 + (rand::random::() - rand::random::()) * 0.8, ) .await; - world - .schedule_fluid_tick( - &Fluid::WATER, - pos.offset(direction.to_offset()), - 5, - TickPriority::Normal, - ) - .await; - } else if state.id == Block::AIR.default_state.id || state.is_liquid() { - world - .set_block_state( - &pos.offset(direction.to_offset()), - if item.id == Item::LAVA_BUCKET.id { - Block::LAVA.default_state.id - } else { - Block::WATER.default_state.id - }, - BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; - } else { return; } - } + let (block, state) = world.get_block_and_state_id(&pos).await; + if waterlogged_check(block, state).is_some() && item.id == Item::WATER_BUCKET.id { + let state_id = set_waterlogged(block, state, true); + world + .set_block_state(&pos, state_id, BlockFlags::NOTIFY_NEIGHBORS) + .await; + world + .schedule_fluid_tick(&Fluid::WATER, pos, 5, TickPriority::Normal) + .await; + } else { + let (block, state) = world + .get_block_and_state(&pos.offset(direction.to_offset())) + .await; - //TODO: Spawn entity if applicable - if player.gamemode.load() != GameMode::Creative { - let item_stack = ItemStack::new(1, &Item::BUCKET); - player - .inventory - .set_stack(player.inventory.get_selected_slot().into(), item_stack) - .await; - } + if waterlogged_check(block, state.id).is_some() { + if item.id == Item::LAVA_BUCKET.id { + return; + } + let state_id = set_waterlogged(block, state.id, true); + + world + .set_block_state( + &pos.offset(direction.to_offset()), + state_id, + BlockFlags::NOTIFY_NEIGHBORS, + ) + .await; + world + .schedule_fluid_tick( + &Fluid::WATER, + pos.offset(direction.to_offset()), + 5, + TickPriority::Normal, + ) + .await; + } else if state.id == Block::AIR.default_state.id || state.is_liquid() { + world + .set_block_state( + &pos.offset(direction.to_offset()), + if item.id == Item::LAVA_BUCKET.id { + Block::LAVA.default_state.id + } else { + Block::WATER.default_state.id + }, + BlockFlags::NOTIFY_NEIGHBORS, + ) + .await; + } else { + return; + } + } + + //TODO: Spawn entity if applicable + if player.gamemode.load() != GameMode::Creative { + let item_stack = ItemStack::new(1, &Item::BUCKET); + player + .inventory + .set_stack(player.inventory.get_selected_slot().into(), item_stack) + .await; + } + }) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/pumpkin/src/item/items/dye.rs b/pumpkin/src/item/items/dye.rs index 75be8fe6e..d8cfc1ab2 100644 --- a/pumpkin/src/item/items/dye.rs +++ b/pumpkin/src/item/items/dye.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use async_trait::async_trait; use pumpkin_data::tag; use pumpkin_util::GameMode; use pumpkin_world::block::entities::{ @@ -22,7 +21,6 @@ impl ItemMetadata for DyeItem { } } -#[async_trait] impl ItemBehaviour for DyeItem { fn can_mine(&self, player: &Player) -> bool { player.gamemode.load() != GameMode::Creative diff --git a/pumpkin/src/item/items/egg.rs b/pumpkin/src/item/items/egg.rs index 982553700..0d7a09f18 100644 --- a/pumpkin/src/item/items/egg.rs +++ b/pumpkin/src/item/items/egg.rs @@ -1,10 +1,10 @@ +use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; use crate::entity::player::Player; use crate::entity::projectile::ThrownItemEntity; use crate::item::{ItemBehaviour, ItemMetadata}; -use async_trait::async_trait; use pumpkin_data::entity::EntityType; use pumpkin_data::item::Item; use pumpkin_data::sound::Sound; @@ -20,31 +20,36 @@ impl ItemMetadata for EggItem { const POWER: f32 = 1.5; -#[async_trait] impl ItemBehaviour for EggItem { - async fn normal_use(&self, _block: &Item, player: &Player) { - let position = player.position(); - let world = player.world(); - world - .play_sound( - Sound::EntityEggThrow, - pumpkin_data::sound::SoundCategory::Players, - &position, - ) - .await; - // TODO: Implement eggs the right way, so there is a chance of spawning chickens - let entity = Entity::new( - Uuid::new_v4(), - world.clone(), - position, - &EntityType::EGG, - false, - ); - let egg = ThrownItemEntity::new(entity, &player.living_entity.entity); - let yaw = player.living_entity.entity.yaw.load(); - let pitch = player.living_entity.entity.pitch.load(); - egg.set_velocity_from(&player.living_entity.entity, pitch, yaw, 0.0, POWER, 1.0); - world.spawn_entity(Arc::new(egg)).await; + fn normal_use<'a>( + &'a self, + _block: &'a Item, + player: &'a Player, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let position = player.position(); + let world = player.world(); + world + .play_sound( + Sound::EntityEggThrow, + pumpkin_data::sound::SoundCategory::Players, + &position, + ) + .await; + // TODO: Implement eggs the right way, so there is a chance of spawning chickens + let entity = Entity::new( + Uuid::new_v4(), + world.clone(), + position, + &EntityType::EGG, + false, + ); + let egg = ThrownItemEntity::new(entity, &player.living_entity.entity); + let yaw = player.living_entity.entity.yaw.load(); + let pitch = player.living_entity.entity.pitch.load(); + egg.set_velocity_from(&player.living_entity.entity, pitch, yaw, 0.0, POWER, 1.0); + world.spawn_entity(Arc::new(egg)).await; + }) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/pumpkin/src/item/items/end_crystal.rs b/pumpkin/src/item/items/end_crystal.rs index 3ccf795fb..6697a7f5a 100644 --- a/pumpkin/src/item/items/end_crystal.rs +++ b/pumpkin/src/item/items/end_crystal.rs @@ -1,3 +1,4 @@ +use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; @@ -5,7 +6,6 @@ use crate::entity::decoration::end_crystal::EndCrystalEntity; use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; use crate::server::Server; -use async_trait::async_trait; use pumpkin_data::entity::EntityType; use pumpkin_data::item::Item; use pumpkin_data::{Block, BlockDirection}; @@ -21,35 +21,36 @@ impl ItemMetadata for EndCrystalItem { } } -#[async_trait] impl ItemBehaviour for EndCrystalItem { - async fn use_on_block( - &self, - item: &mut ItemStack, - player: &Player, + fn use_on_block<'a>( + &'a self, + item: &'a mut ItemStack, + player: &'a Player, location: BlockPos, _face: BlockDirection, - _block: &Block, - _server: &Server, - ) { - let world = player.world(); - let block = world.get_block(&location).await; - if block != &Block::OBSIDIAN && block != &Block::BEDROCK { - return; - } + _block: &'a Block, + _server: &'a Server, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let world = player.world(); + let block = world.get_block(&location).await; + if block != &Block::OBSIDIAN && block != &Block::BEDROCK { + return; + } - let location = location.up(); - let entity = Entity::new( - Uuid::new_v4(), - world.clone(), - location.to_f64(), - &EntityType::END_CRYSTAL, - false, - ); - let end_crystal = Arc::new(EndCrystalEntity::new(entity)); - world.spawn_entity(end_crystal.clone()).await; - end_crystal.set_show_bottom(false).await; - item.decrement_unless_creative(player.gamemode.load(), 1); + let location = location.up(); + let entity = Entity::new( + Uuid::new_v4(), + world.clone(), + location.to_f64(), + &EntityType::END_CRYSTAL, + false, + ); + let end_crystal = Arc::new(EndCrystalEntity::new(entity)); + world.spawn_entity(end_crystal.clone()).await; + end_crystal.set_show_bottom(false).await; + item.decrement_unless_creative(player.gamemode.load(), 1); + }) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/pumpkin/src/item/items/ender_eye.rs b/pumpkin/src/item/items/ender_eye.rs index d3a1ab821..a458205e6 100644 --- a/pumpkin/src/item/items/ender_eye.rs +++ b/pumpkin/src/item/items/ender_eye.rs @@ -1,3 +1,4 @@ +use std::pin::Pin; use std::sync::Arc; use crate::{server::Server, world::portal::end::EndPortal}; @@ -8,7 +9,6 @@ use pumpkin_world::world::BlockFlags; use crate::item::{ItemBehaviour, ItemMetadata}; use crate::{entity::player::Player, world::World}; -use async_trait::async_trait; pub struct EnderEyeItem; @@ -18,63 +18,67 @@ impl ItemMetadata for EnderEyeItem { } } -#[async_trait] impl ItemBehaviour for EnderEyeItem { - async fn use_on_block( - &self, - _item: &mut ItemStack, - player: &Player, + fn use_on_block<'a>( + &'a self, + _item: &'a mut ItemStack, + player: &'a Player, location: BlockPos, _face: BlockDirection, - block: &Block, - _server: &Server, - ) { - if block.id != Block::END_PORTAL_FRAME.id { - return; - } + block: &'a Block, + _server: &'a Server, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + if block.id != Block::END_PORTAL_FRAME.id { + return; + } - let world = player.world(); - let state_id = world.get_block_state_id(&location).await; - let original_props = block.properties(state_id).unwrap().to_props(); + let world = player.world(); + let state_id = world.get_block_state_id(&location).await; + let original_props = block.properties(state_id).unwrap().to_props(); - let props: Vec<(&str, &str)> = original_props - .iter() - .map(|(key, value)| { - if *key == "eye" { - (*key, "true") - } else { - (*key, *value) - } - }) - .collect(); + let props: Vec<(&str, &str)> = original_props + .iter() + .map(|(key, value)| { + if *key == "eye" { + (*key, "true") + } else { + (*key, *value) + } + }) + .collect(); - let new_state_id = block.from_properties(&props).to_state_id(block); - world - .set_block_state(&location, new_state_id, BlockFlags::empty()) - .await; + let new_state_id = block.from_properties(&props).to_state_id(block); + world + .set_block_state(&location, new_state_id, BlockFlags::empty()) + .await; - EndPortal::get_new_portal(world, location).await; - - return; + EndPortal::get_new_portal(world, location).await; + }) } - async fn normal_use(&self, _item: &Item, player: &Player) { - let world = player.world(); - let (start_pos, end_pos) = self.get_start_and_end_pos(player); - let checker = async |pos: &BlockPos, world_inner: &Arc| { - let state_id = world_inner.get_block_state_id(pos).await; - state_id != Block::AIR.default_state.id - }; + fn normal_use<'a>( + &'a self, + _item: &'a Item, + player: &'a Player, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let world = player.world(); + let (start_pos, end_pos) = self.get_start_and_end_pos(player); + let checker = async |pos: &BlockPos, world_inner: &Arc| { + let state_id = world_inner.get_block_state_id(pos).await; + state_id != Block::AIR.default_state.id + }; - let Some((block_pos, _direction)) = world.raycast(start_pos, end_pos, checker).await else { - return; - }; + let Some((block_pos, _direction)) = world.raycast(start_pos, end_pos, checker).await + else { + return; + }; - let block = world.get_block(&block_pos).await; + let block = world.get_block(&block_pos).await; - if block == &Block::END_PORTAL_FRAME { - return; - } + if block == &Block::END_PORTAL_FRAME {} + }) //TODO Throw the Ender Eye in the direction of the stronghold. } diff --git a/pumpkin/src/item/items/glowing_ink_sac.rs b/pumpkin/src/item/items/glowing_ink_sac.rs index 76c6d526a..d305c8350 100644 --- a/pumpkin/src/item/items/glowing_ink_sac.rs +++ b/pumpkin/src/item/items/glowing_ink_sac.rs @@ -1,6 +1,5 @@ use std::sync::{Arc, atomic::Ordering}; -use async_trait::async_trait; use pumpkin_data::item::Item; use pumpkin_world::block::entities::{BlockEntity, sign::Text}; @@ -17,7 +16,6 @@ impl ItemMetadata for GlowingInkSacItem { } } -#[async_trait] impl ItemBehaviour for GlowingInkSacItem { fn as_any(&self) -> &dyn std::any::Any { self diff --git a/pumpkin/src/item/items/hoe.rs b/pumpkin/src/item/items/hoe.rs index 63cc3765b..cd75bdf20 100644 --- a/pumpkin/src/item/items/hoe.rs +++ b/pumpkin/src/item/items/hoe.rs @@ -3,7 +3,6 @@ use crate::entity::item::ItemEntity; use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; use crate::server::Server; -use async_trait::async_trait; use pumpkin_data::BlockDirection; use pumpkin_data::entity::EntityType; use pumpkin_data::item::Item; @@ -11,6 +10,7 @@ use pumpkin_data::{Block, tag}; use pumpkin_util::math::position::BlockPos; use pumpkin_world::item::ItemStack; use pumpkin_world::world::BlockFlags; +use std::pin::Pin; use std::sync::Arc; use uuid::Uuid; @@ -22,79 +22,80 @@ impl ItemMetadata for HoeItem { } } -#[async_trait] impl ItemBehaviour for HoeItem { - async fn use_on_block( - &self, - _item: &mut ItemStack, - player: &Player, + fn use_on_block<'a>( + &'a self, + _item: &'a mut ItemStack, + player: &'a Player, location: BlockPos, face: BlockDirection, - block: &Block, - _server: &Server, - ) { - // Yes, Minecraft does hardcode these - if block == &Block::GRASS_BLOCK - || block == &Block::DIRT_PATH - || block == &Block::DIRT - || block == &Block::COARSE_DIRT - || block == &Block::ROOTED_DIRT - { - let mut future_block = block; - let world = player.world(); + block: &'a Block, + _server: &'a Server, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + // Yes, Minecraft does hardcode these + if block == &Block::GRASS_BLOCK + || block == &Block::DIRT_PATH + || block == &Block::DIRT + || block == &Block::COARSE_DIRT + || block == &Block::ROOTED_DIRT + { + let mut future_block = block; + let world = player.world(); - //Only rooted can be right-clicked on the bottom of the block - if face == BlockDirection::Down { + //Only rooted can be right-clicked on the bottom of the block + if face == BlockDirection::Down { + if block == &Block::ROOTED_DIRT { + future_block = &Block::DIRT; + } + } else { + // grass, dirt && dirt path become farmland + if (block == &Block::GRASS_BLOCK + || block == &Block::DIRT_PATH + || block == &Block::DIRT) + && world.get_block_state(&location.up()).await.is_air() + { + future_block = &Block::FARMLAND; + } + //Coarse dirt and rooted dirt become dirt + else if block == &Block::COARSE_DIRT || block == &Block::ROOTED_DIRT { + future_block = &Block::DIRT; + } + } + + world + .set_block_state( + &location, + future_block.default_state.id, + BlockFlags::NOTIFY_ALL, + ) + .await; + + //Also rooted_dirt drop a hanging_root if block == &Block::ROOTED_DIRT { - future_block = &Block::DIRT; - } - } else { - // grass, dirt && dirt path become farmland - if (block == &Block::GRASS_BLOCK - || block == &Block::DIRT_PATH - || block == &Block::DIRT) - && world.get_block_state(&location.up()).await.is_air() - { - future_block = &Block::FARMLAND; - } - //Coarse dirt and rooted dirt become dirt - else if block == &Block::COARSE_DIRT || block == &Block::ROOTED_DIRT { - future_block = &Block::DIRT; + let location = match face { + BlockDirection::Up => location.up().to_f64(), + BlockDirection::Down => location.down().to_f64(), + BlockDirection::North => location.up().to_f64().add_raw(0.0, -0.4, -1.0), + BlockDirection::South => location.up().to_f64().add_raw(0.0, -0.4, 1.0), + BlockDirection::West => location.up().to_f64().add_raw(-1.0, -0.4, 0.0), + BlockDirection::East => location.up().to_f64().add_raw(1.0, -0.4, 0.0), + }; + let entity = Entity::new( + Uuid::new_v4(), + world.clone(), + location, + &EntityType::ITEM, + false, + ); + // TODO: Merge stacks together + let item_entity = Arc::new( + ItemEntity::new(entity, ItemStack::new(1, &Item::HANGING_ROOTS)).await, + ); + world.spawn_entity(item_entity).await; } } - - world - .set_block_state( - &location, - future_block.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - - //Also rooted_dirt drop a hanging_root - if block == &Block::ROOTED_DIRT { - let location = match face { - BlockDirection::Up => location.up().to_f64(), - BlockDirection::Down => location.down().to_f64(), - BlockDirection::North => location.up().to_f64().add_raw(0.0, -0.4, -1.0), - BlockDirection::South => location.up().to_f64().add_raw(0.0, -0.4, 1.0), - BlockDirection::West => location.up().to_f64().add_raw(-1.0, -0.4, 0.0), - BlockDirection::East => location.up().to_f64().add_raw(1.0, -0.4, 0.0), - }; - let entity = Entity::new( - Uuid::new_v4(), - world.clone(), - location, - &EntityType::ITEM, - false, - ); - // TODO: Merge stacks together - let item_entity = Arc::new( - ItemEntity::new(entity, ItemStack::new(1, &Item::HANGING_ROOTS)).await, - ); - world.spawn_entity(item_entity).await; - } - } + }) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/pumpkin/src/item/items/honeycomb.rs b/pumpkin/src/item/items/honeycomb.rs index 0a2a9b718..f050be81a 100644 --- a/pumpkin/src/item/items/honeycomb.rs +++ b/pumpkin/src/item/items/honeycomb.rs @@ -1,3 +1,4 @@ +use std::pin::Pin; use std::sync::Arc; use std::sync::atomic::Ordering; @@ -6,7 +7,6 @@ use crate::block::registry::BlockActionResult; use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; use crate::server::Server; -use async_trait::async_trait; use pumpkin_data::BlockDirection; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::OakDoorLikeProperties; @@ -28,54 +28,54 @@ impl ItemMetadata for HoneyCombItem { } } -#[async_trait] impl ItemBehaviour for HoneyCombItem { - async fn use_on_block( - &self, - _item: &mut ItemStack, - player: &Player, + fn use_on_block<'a>( + &'a self, + _item: &'a mut ItemStack, + player: &'a Player, location: BlockPos, _face: BlockDirection, - block: &Block, - _server: &Server, - ) { - let world = player.world(); + block: &'a Block, + _server: &'a Server, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let world = player.world(); - // First we try to strip the block. by getting his equivalent and applying it the axis. - let replacement_block = get_waxed_equivalent(block); - // If there is a strip equivalent. - if let Some(replacement_block) = replacement_block { - // get block state of the old log. - // get the log properties - // create new properties for the new log. - let new_block = &Block::from_id(replacement_block); - - let new_state_id = if block.has_tag(&tag::Block::MINECRAFT_DOORS) - && block.has_tag(&tag::Block::MINECRAFT_DOORS) - { + // First we try to strip the block. by getting his equivalent and applying it the axis. + let replacement_block = get_waxed_equivalent(block); + // If there is a strip equivalent. + if let Some(replacement_block) = replacement_block { // get block state of the old log. - let door_information = world.get_block_state_id(&location).await; // get the log properties - let door_props = OakDoorLikeProperties::from_state_id(door_information, block); // create new properties for the new log. - let mut new_door_properties = OakDoorLikeProperties::default(new_block); - // Set old axis to the new log. - new_door_properties.facing = door_props.facing; - new_door_properties.open = door_props.open; - new_door_properties.half = door_props.half; - new_door_properties.hinge = door_props.hinge; - new_door_properties.powered = door_props.powered; - new_door_properties.to_state_id(new_block) - } else { - new_block.default_state.id - }; + let new_block = &Block::from_id(replacement_block); - // TODO Implements trapdoors - world - .set_block_state(&location, new_state_id, BlockFlags::NOTIFY_ALL) - .await; - return; - } + let new_state_id = if block.has_tag(&tag::Block::MINECRAFT_DOORS) + && block.has_tag(&tag::Block::MINECRAFT_DOORS) + { + // get block state of the old log. + let door_information = world.get_block_state_id(&location).await; + // get the log properties + let door_props = OakDoorLikeProperties::from_state_id(door_information, block); + // create new properties for the new log. + let mut new_door_properties = OakDoorLikeProperties::default(new_block); + // Set old axis to the new log. + new_door_properties.facing = door_props.facing; + new_door_properties.open = door_props.open; + new_door_properties.half = door_props.half; + new_door_properties.hinge = door_props.hinge; + new_door_properties.powered = door_props.powered; + new_door_properties.to_state_id(new_block) + } else { + new_block.default_state.id + }; + + // TODO Implements trapdoors + world + .set_block_state(&location, new_state_id, BlockFlags::NOTIFY_ALL) + .await; + } + }) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/pumpkin/src/item/items/ignite/fire_charge.rs b/pumpkin/src/item/items/ignite/fire_charge.rs index a65611a17..7618b08ba 100644 --- a/pumpkin/src/item/items/ignite/fire_charge.rs +++ b/pumpkin/src/item/items/ignite/fire_charge.rs @@ -1,6 +1,6 @@ +use std::pin::Pin; use std::sync::Arc; -use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::BlockDirection; use pumpkin_data::item::Item; @@ -24,34 +24,34 @@ impl ItemMetadata for FireChargeItem { } } -#[async_trait] impl ItemBehaviour for FireChargeItem { - async fn use_on_block( - &self, - _item: &mut ItemStack, - player: &Player, + fn use_on_block<'a>( + &'a self, + _item: &'a mut ItemStack, + player: &'a Player, location: BlockPos, face: BlockDirection, - _block: &Block, - _server: &Server, - ) { - Ignition::ignite_block( - |world: Arc, pos: BlockPos, new_state_id: u16| async move { - world - .set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL) - .await; + block: &'a Block, + _server: &'a Server, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + Ignition::ignite_block( + |world: Arc, pos: BlockPos, new_state_id: u16| async move { + world + .set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL) + .await; - world - .play_block_sound(Sound::ItemFirechargeUse, SoundCategory::Blocks, pos) - .await; - }, - player, - location, - face, - _block, - _server, - ) - .await; + world + .play_block_sound(Sound::ItemFirechargeUse, SoundCategory::Blocks, pos) + .await; + }, + player, + location, + face, + block, + ) + .await; + }) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/pumpkin/src/item/items/ignite/flint_and_steel.rs b/pumpkin/src/item/items/ignite/flint_and_steel.rs index bb391b8b0..05f88c025 100644 --- a/pumpkin/src/item/items/ignite/flint_and_steel.rs +++ b/pumpkin/src/item/items/ignite/flint_and_steel.rs @@ -3,13 +3,13 @@ use crate::item::ItemBehaviour; use crate::item::ItemMetadata; use crate::server::Server; use crate::world::World; -use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::BlockDirection; use pumpkin_data::item::Item; use pumpkin_util::math::position::BlockPos; use pumpkin_world::item::ItemStack; use pumpkin_world::world::BlockFlags; +use std::pin::Pin; use std::sync::Arc; use crate::item::items::ignite::ignition::Ignition; @@ -22,30 +22,30 @@ impl ItemMetadata for FlintAndSteelItem { } } -#[async_trait] impl ItemBehaviour for FlintAndSteelItem { - async fn use_on_block( - &self, - _item: &mut ItemStack, - player: &Player, + fn use_on_block<'a>( + &'a self, + _item: &'a mut ItemStack, + player: &'a Player, location: BlockPos, face: BlockDirection, - block: &Block, - server: &Server, - ) { - Ignition::ignite_block( - |world: Arc, pos: BlockPos, new_state_id: u16| async move { - world - .set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL) - .await; - }, - player, - location, - face, - block, - server, - ) - .await; + block: &'a Block, + _server: &'a Server, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + Ignition::ignite_block( + |world: Arc, pos: BlockPos, new_state_id: u16| async move { + world + .set_block_state(&pos, new_state_id, BlockFlags::NOTIFY_ALL) + .await; + }, + player, + location, + face, + block, + ) + .await; + }) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/pumpkin/src/item/items/ignite/ignition.rs b/pumpkin/src/item/items/ignite/ignition.rs index 70ef52646..8c29721f7 100644 --- a/pumpkin/src/item/items/ignite/ignition.rs +++ b/pumpkin/src/item/items/ignite/ignition.rs @@ -1,7 +1,6 @@ use crate::block::blocks::fire::FireBlockBase; use crate::block::blocks::fire::fire::FireBlock; use crate::entity::player::Player; -use crate::server::Server; use crate::world::World; use pumpkin_data::fluid::Fluid; use pumpkin_data::{Block, BlockDirection}; @@ -17,7 +16,6 @@ impl Ignition { location: BlockPos, face: BlockDirection, block: &Block, - _server: &Server, ) where F: FnOnce(Arc, BlockPos, u16) -> Fut, Fut: Future, diff --git a/pumpkin/src/item/items/ink_sac.rs b/pumpkin/src/item/items/ink_sac.rs index 1736ee78b..04153fe10 100644 --- a/pumpkin/src/item/items/ink_sac.rs +++ b/pumpkin/src/item/items/ink_sac.rs @@ -1,6 +1,5 @@ use std::sync::{Arc, atomic::Ordering}; -use async_trait::async_trait; use pumpkin_data::item::Item; use pumpkin_world::block::entities::{BlockEntity, sign::Text}; @@ -17,7 +16,6 @@ impl ItemMetadata for InkSacItem { } } -#[async_trait] impl ItemBehaviour for InkSacItem { fn as_any(&self) -> &dyn std::any::Any { self diff --git a/pumpkin/src/item/items/mace.rs b/pumpkin/src/item/items/mace.rs index cad63a83b..933470e9f 100644 --- a/pumpkin/src/item/items/mace.rs +++ b/pumpkin/src/item/items/mace.rs @@ -1,6 +1,5 @@ use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; -use async_trait::async_trait; use pumpkin_data::item::Item; use pumpkin_util::GameMode; @@ -11,7 +10,6 @@ impl ItemMetadata for MaceItem { [Item::MACE.id].into() } } -#[async_trait] impl ItemBehaviour for MaceItem { fn can_mine(&self, player: &Player) -> bool { player.gamemode.load() != GameMode::Creative diff --git a/pumpkin/src/item/items/minecart.rs b/pumpkin/src/item/items/minecart.rs index f6e18a4da..a2bac8cbe 100644 --- a/pumpkin/src/item/items/minecart.rs +++ b/pumpkin/src/item/items/minecart.rs @@ -1,10 +1,10 @@ +use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; use crate::server::Server; -use async_trait::async_trait; use pumpkin_data::BlockDirection; use pumpkin_data::block_properties::{ BlockProperties, PoweredRailLikeProperties, RailLikeProperties, @@ -48,43 +48,44 @@ impl ItemMetadata for MinecartItem { } } -#[async_trait] impl ItemBehaviour for MinecartItem { - async fn use_on_block( - &self, - item: &mut ItemStack, - player: &Player, + fn use_on_block<'a>( + &'a self, + item: &'a mut ItemStack, + player: &'a Player, location: BlockPos, _face: BlockDirection, - block: &Block, - _server: &Server, - ) { - let world = player.world(); + block: &'a Block, + _server: &'a Server, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let world = player.world(); - if !block.has_tag(&tag::Block::MINECRAFT_RAILS) { - return; - } - let state_id = world.get_block_state_id(&location).await; - let is_ascending = if PoweredRailLikeProperties::handles_block_id(block.id) { - PoweredRailLikeProperties::from_state_id(state_id, block) - .shape - .is_ascending() - } else { - RailLikeProperties::from_state_id(state_id, block) - .shape - .is_ascending() - }; - let height = if is_ascending { 0.5 } else { 0.0 }; - let entity_type = Self::item_to_entity(item.item); - let pos = location.to_f64(); - let entity = Arc::new(Entity::new( - Uuid::new_v4(), - world.clone(), - Vector3::new(pos.x, pos.y + 0.0625 + height, pos.z), - entity_type, - false, - )); - world.spawn_entity(entity).await; + if !block.has_tag(&tag::Block::MINECRAFT_RAILS) { + return; + } + let state_id = world.get_block_state_id(&location).await; + let is_ascending = if PoweredRailLikeProperties::handles_block_id(block.id) { + PoweredRailLikeProperties::from_state_id(state_id, block) + .shape + .is_ascending() + } else { + RailLikeProperties::from_state_id(state_id, block) + .shape + .is_ascending() + }; + let height = if is_ascending { 0.5 } else { 0.0 }; + let entity_type = Self::item_to_entity(item.item); + let pos = location.to_f64(); + let entity = Arc::new(Entity::new( + Uuid::new_v4(), + world.clone(), + Vector3::new(pos.x, pos.y + 0.0625 + height, pos.z), + entity_type, + false, + )); + world.spawn_entity(entity).await; + }) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/pumpkin/src/item/items/name_tag.rs b/pumpkin/src/item/items/name_tag.rs index fe0440755..54faea7d1 100644 --- a/pumpkin/src/item/items/name_tag.rs +++ b/pumpkin/src/item/items/name_tag.rs @@ -1,9 +1,9 @@ +use std::pin::Pin; use std::sync::Arc; use crate::entity::EntityBase; use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; -use async_trait::async_trait; use pumpkin_data::data_component_impl::CustomNameImpl; use pumpkin_data::item::Item; use pumpkin_util::text::TextComponent; @@ -17,22 +17,23 @@ impl ItemMetadata for NameTagItem { } } -#[async_trait] impl ItemBehaviour for NameTagItem { - async fn use_on_entity( - &self, - item: &mut ItemStack, - player: &Player, + fn use_on_entity<'a>( + &'a self, + item: &'a mut ItemStack, + player: &'a Player, entity: Arc, - ) { - let entity = entity.get_entity(); - if entity.entity_type.saveable - && let Some(name) = item.get_data_component::() - { - // TODO - entity.set_custom_name(TextComponent::text(name.name)).await; - item.decrement_unless_creative(player.gamemode.load(), 1); - } + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let entity = entity.get_entity(); + if entity.entity_type.saveable + && let Some(name) = item.get_data_component::() + { + // TODO + entity.set_custom_name(TextComponent::text(name.name)).await; + item.decrement_unless_creative(player.gamemode.load(), 1); + } + }) } fn as_any(&self) -> &dyn std::any::Any { self diff --git a/pumpkin/src/item/items/shovel.rs b/pumpkin/src/item/items/shovel.rs index 6383e8b91..830c01d63 100644 --- a/pumpkin/src/item/items/shovel.rs +++ b/pumpkin/src/item/items/shovel.rs @@ -1,7 +1,8 @@ +use std::pin::Pin; + use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; use crate::server::Server; -use async_trait::async_trait; use pumpkin_data::BlockDirection; use pumpkin_data::block_properties::{BlockProperties, CampfireLikeProperties}; use pumpkin_data::sound::{Sound, SoundCategory}; @@ -20,67 +21,68 @@ impl ItemMetadata for ShovelItem { } } -#[async_trait] impl ItemBehaviour for ShovelItem { - async fn use_on_block( - &self, - _item: &mut ItemStack, - player: &Player, + fn use_on_block<'a>( + &'a self, + _item: &'a mut ItemStack, + player: &'a Player, location: BlockPos, face: BlockDirection, - block: &Block, - _server: &Server, - ) { - let world = player.world(); - // Yes, Minecraft does hardcode these - if (block == &Block::GRASS_BLOCK - || block == &Block::DIRT - || block == &Block::COARSE_DIRT - || block == &Block::ROOTED_DIRT - || block == &Block::PODZOL - || block == &Block::MYCELIUM) - && face != BlockDirection::Down - && world.get_block_state(&location.up()).await.is_air() - { - world - .set_block_state( - &location, - Block::DIRT_PATH.default_state.id, - BlockFlags::NOTIFY_ALL, - ) - .await; - } - if block == &Block::CAMPFIRE || block == &Block::SOUL_CAMPFIRE { - let mut campfire_props = CampfireLikeProperties::from_state_id( - world.get_block_state(&location).await.id, - block, - ); - if campfire_props.lit { - world - .sync_world_event(WorldEvent::FireExtinguished, location, 0) - .await; - - campfire_props.lit = false; + block: &'a Block, + _server: &'a Server, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let world = player.world(); + // Yes, Minecraft does hardcode these + if (block == &Block::GRASS_BLOCK + || block == &Block::DIRT + || block == &Block::COARSE_DIRT + || block == &Block::ROOTED_DIRT + || block == &Block::PODZOL + || block == &Block::MYCELIUM) + && face != BlockDirection::Down + && world.get_block_state(&location.up()).await.is_air() + { world .set_block_state( &location, - campfire_props.to_state_id(block), + Block::DIRT_PATH.default_state.id, BlockFlags::NOTIFY_ALL, ) .await; - let seed = rng().random::(); - player - .play_sound( - Sound::BlockFireExtinguish as u16, - SoundCategory::Ambient, - &location.to_f64(), - 0.5, - 2.0, - seed, - ) - .await; } - } + if block == &Block::CAMPFIRE || block == &Block::SOUL_CAMPFIRE { + let mut campfire_props = CampfireLikeProperties::from_state_id( + world.get_block_state(&location).await.id, + block, + ); + if campfire_props.lit { + world + .sync_world_event(WorldEvent::FireExtinguished, location, 0) + .await; + + campfire_props.lit = false; + world + .set_block_state( + &location, + campfire_props.to_state_id(block), + BlockFlags::NOTIFY_ALL, + ) + .await; + let seed = rng().random::(); + player + .play_sound( + Sound::BlockFireExtinguish as u16, + SoundCategory::Ambient, + &location.to_f64(), + 0.5, + 2.0, + seed, + ) + .await; + } + } + }) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/pumpkin/src/item/items/snowball.rs b/pumpkin/src/item/items/snowball.rs index 033f35d1e..7d5efcf34 100644 --- a/pumpkin/src/item/items/snowball.rs +++ b/pumpkin/src/item/items/snowball.rs @@ -1,10 +1,10 @@ +use std::pin::Pin; use std::sync::Arc; use crate::entity::Entity; use crate::entity::player::Player; use crate::entity::projectile::ThrownItemEntity; use crate::item::{ItemBehaviour, ItemMetadata}; -use async_trait::async_trait; use pumpkin_data::entity::EntityType; use pumpkin_data::item::Item; use pumpkin_data::sound::Sound; @@ -20,30 +20,35 @@ impl ItemMetadata for SnowBallItem { const POWER: f32 = 1.5; -#[async_trait] impl ItemBehaviour for SnowBallItem { - async fn normal_use(&self, _block: &Item, player: &Player) { - let position = player.position(); - let world = player.world(); - world - .play_sound( - Sound::EntitySnowballThrow, - pumpkin_data::sound::SoundCategory::Neutral, - &position, - ) - .await; - let entity = Entity::new( - Uuid::new_v4(), - world.clone(), - position, - &EntityType::SNOWBALL, - false, - ); - let snowball = ThrownItemEntity::new(entity, &player.living_entity.entity); - let yaw = player.living_entity.entity.yaw.load(); - let pitch = player.living_entity.entity.pitch.load(); - snowball.set_velocity_from(&player.living_entity.entity, pitch, yaw, 0.0, POWER, 1.0); - world.spawn_entity(Arc::new(snowball)).await; + fn normal_use<'a>( + &'a self, + _block: &'a Item, + player: &'a Player, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let position = player.position(); + let world = player.world(); + world + .play_sound( + Sound::EntitySnowballThrow, + pumpkin_data::sound::SoundCategory::Neutral, + &position, + ) + .await; + let entity = Entity::new( + Uuid::new_v4(), + world.clone(), + position, + &EntityType::SNOWBALL, + false, + ); + let snowball = ThrownItemEntity::new(entity, &player.living_entity.entity); + let yaw = player.living_entity.entity.yaw.load(); + let pitch = player.living_entity.entity.pitch.load(); + snowball.set_velocity_from(&player.living_entity.entity, pitch, yaw, 0.0, POWER, 1.0); + world.spawn_entity(Arc::new(snowball)).await; + }) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/pumpkin/src/item/items/spawn_egg.rs b/pumpkin/src/item/items/spawn_egg.rs index d4535366d..2d5db05af 100644 --- a/pumpkin/src/item/items/spawn_egg.rs +++ b/pumpkin/src/item/items/spawn_egg.rs @@ -1,8 +1,9 @@ +use std::pin::Pin; + use crate::entity::player::Player; use crate::entity::r#type::from_type; use crate::item::{ItemBehaviour, ItemMetadata}; use crate::server::Server; -use async_trait::async_trait; use pumpkin_data::entity::entity_from_egg; use pumpkin_data::{Block, BlockDirection}; use pumpkin_util::math::position::BlockPos; @@ -20,49 +21,50 @@ impl ItemMetadata for SpawnEggItem { } } -#[async_trait] impl ItemBehaviour for SpawnEggItem { - async fn use_on_block( - &self, - item: &mut ItemStack, - player: &Player, + fn use_on_block<'a>( + &'a self, + item: &'a mut ItemStack, + player: &'a Player, location: BlockPos, face: BlockDirection, - _block: &Block, - _server: &Server, - ) { - if let Some(entity_type) = entity_from_egg(item.item.id) { - let world = player.world(); + _block: &'a Block, + _server: &'a Server, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + if let Some(entity_type) = entity_from_egg(item.item.id) { + let world = player.world(); - if let Some(block_entity) = player.world().get_block_entity(&location).await - && let Some(spawner) = block_entity - .as_any() - .downcast_ref::() - { - spawner.set_entity_type(entity_type); - world.update_block_entity(&block_entity).await; + if let Some(block_entity) = player.world().get_block_entity(&location).await + && let Some(spawner) = block_entity + .as_any() + .downcast_ref::() + { + spawner.set_entity_type(entity_type); + world.update_block_entity(&block_entity).await; + item.decrement_unless_creative(player.gamemode.load(), 1); + return; + } + let pos = BlockPos(location.0 + face.to_offset()); + let pos = Vector3::new( + f64::from(pos.0.x) + 0.5, + f64::from(pos.0.y), + f64::from(pos.0.z) + 0.5, + ); + // Create rotation like Vanilla + let yaw = wrap_degrees(rand::random::() * 360.0) % 360.0; + + let mob = from_type(entity_type, pos, world, Uuid::new_v4()).await; + + // Set the rotation + mob.get_entity().set_rotation(yaw, 0.0); + + // Broadcast the new mob to all players + world.spawn_entity(mob).await; item.decrement_unless_creative(player.gamemode.load(), 1); - return; + // TODO: send/configure additional commands/data based on the type of entity (horse, slime, etc) } - let pos = BlockPos(location.0 + face.to_offset()); - let pos = Vector3::new( - f64::from(pos.0.x) + 0.5, - f64::from(pos.0.y), - f64::from(pos.0.z) + 0.5, - ); - // Create rotation like Vanilla - let yaw = wrap_degrees(rand::random::() * 360.0) % 360.0; - - let mob = from_type(entity_type, pos, world, Uuid::new_v4()).await; - - // Set the rotation - mob.get_entity().set_rotation(yaw, 0.0); - - // Broadcast the new mob to all players - world.spawn_entity(mob).await; - item.decrement_unless_creative(player.gamemode.load(), 1); - // TODO: send/configure additional commands/data based on the type of entity (horse, slime, etc) - } + }) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/pumpkin/src/item/items/swords.rs b/pumpkin/src/item/items/swords.rs index ab4008931..07c56dcdf 100644 --- a/pumpkin/src/item/items/swords.rs +++ b/pumpkin/src/item/items/swords.rs @@ -1,6 +1,5 @@ use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; -use async_trait::async_trait; use pumpkin_data::tag; use pumpkin_util::GameMode; @@ -12,7 +11,6 @@ impl ItemMetadata for SwordItem { } } -#[async_trait] impl ItemBehaviour for SwordItem { fn can_mine(&self, player: &Player) -> bool { player.gamemode.load() != GameMode::Creative diff --git a/pumpkin/src/item/items/trident.rs b/pumpkin/src/item/items/trident.rs index ad8f85db3..1a8df5fb8 100644 --- a/pumpkin/src/item/items/trident.rs +++ b/pumpkin/src/item/items/trident.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::item::Item; use pumpkin_util::GameMode; @@ -15,7 +14,6 @@ impl ItemMetadata for TridentItem { } } -#[async_trait] impl ItemBehaviour for TridentItem { fn can_mine(&self, player: &Player) -> bool { player.gamemode.load() != GameMode::Creative diff --git a/pumpkin/src/item/items/wind_charge.rs b/pumpkin/src/item/items/wind_charge.rs index 178f74155..f91f1fe7c 100644 --- a/pumpkin/src/item/items/wind_charge.rs +++ b/pumpkin/src/item/items/wind_charge.rs @@ -1,7 +1,7 @@ +use std::pin::Pin; use std::sync::Arc; use crate::entity::player::Player; -use async_trait::async_trait; use pumpkin_data::entity::EntityType; use pumpkin_data::item::Item; use pumpkin_data::sound::Sound; @@ -22,41 +22,53 @@ impl ItemMetadata for WindChargeItem { const POWER: f32 = 1.5; -#[async_trait] impl ItemBehaviour for WindChargeItem { - async fn normal_use(&self, _block: &Item, player: &Player) { - let world = player.world(); - let position = player.position(); + fn normal_use<'a>( + &'a self, + _block: &'a Item, + player: &'a Player, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let world = player.world(); + let position = player.position(); - // TODO: Implement Cooldown to throw the item + // TODO: Implement Cooldown to throw the item - world - .play_sound( - Sound::EntityWindChargeThrow, - pumpkin_data::sound::SoundCategory::Neutral, - &position, - ) - .await; + world + .play_sound( + Sound::EntityWindChargeThrow, + pumpkin_data::sound::SoundCategory::Neutral, + &position, + ) + .await; - let entity = Entity::new( - Uuid::new_v4(), - world.clone(), - position, - &EntityType::WIND_CHARGE, - false, - ); + let entity = Entity::new( + Uuid::new_v4(), + world.clone(), + position, + &EntityType::WIND_CHARGE, + false, + ); - let wind_charge = ThrownItemEntity::new(entity, &player.living_entity.entity); - let yaw = player.living_entity.entity.yaw.load(); - let pitch = player.living_entity.entity.pitch.load(); + let wind_charge = ThrownItemEntity::new(entity, &player.living_entity.entity); + let yaw = player.living_entity.entity.yaw.load(); + let pitch = player.living_entity.entity.pitch.load(); - wind_charge.set_velocity_from(&player.living_entity.entity, pitch, yaw, 0.0, POWER, 1.0); - // TODO: player.incrementStat(Stats.USED) + wind_charge.set_velocity_from( + &player.living_entity.entity, + pitch, + yaw, + 0.0, + POWER, + 1.0, + ); + // TODO: player.incrementStat(Stats.USED) - // TODO: Implement that the projectile will explode on impact on ground - world - .spawn_entity(Arc::new(WindChargeEntity::new(wind_charge))) - .await; + // TODO: Implement that the projectile will explode on impact on ground + world + .spawn_entity(Arc::new(WindChargeEntity::new(wind_charge))) + .await; + }) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/pumpkin/src/item/mod.rs b/pumpkin/src/item/mod.rs index 84a54d4d9..4222e4457 100644 --- a/pumpkin/src/item/mod.rs +++ b/pumpkin/src/item/mod.rs @@ -2,12 +2,12 @@ pub mod items; pub mod registry; use std::any::Any; +use std::pin::Pin; use std::sync::Arc; use crate::entity::EntityBase; use crate::entity::player::Player; use crate::server::Server; -use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::BlockDirection; use pumpkin_data::item::Item; @@ -19,27 +19,34 @@ pub trait ItemMetadata { fn ids() -> Box<[u16]>; } -#[async_trait] pub trait ItemBehaviour: Send + Sync { - async fn normal_use(&self, _block: &Item, _player: &Player) {} - - async fn use_on_block( - &self, - _item: &mut ItemStack, - _player: &Player, - _location: BlockPos, - _face: BlockDirection, - _block: &Block, - _server: &Server, - ) { + fn normal_use<'a>( + &'a self, + _item: &'a Item, + _player: &'a Player, + ) -> Pin + Send + 'a>> { + Box::pin(async move {}) } - async fn use_on_entity( - &self, - _item: &mut ItemStack, - _player: &Player, + fn use_on_block<'a>( + &'a self, + _item: &'a mut ItemStack, + _player: &'a Player, + _location: BlockPos, + _face: BlockDirection, + _block: &'a Block, + _server: &'a Server, + ) -> Pin + Send + 'a>> { + Box::pin(async move {}) + } + + fn use_on_entity<'a>( + &'a self, + _item: &'a mut ItemStack, + _player: &'a Player, _entity: Arc, - ) { + ) -> Pin + Send + 'a>> { + Box::pin(async move {}) } fn can_mine(&self, _player: &Player) -> bool { diff --git a/pumpkin/src/plugin/loader/mod.rs b/pumpkin/src/plugin/loader/mod.rs index 9f42727a5..5f9e2ebd5 100644 --- a/pumpkin/src/plugin/loader/mod.rs +++ b/pumpkin/src/plugin/loader/mod.rs @@ -1,30 +1,36 @@ use crate::plugin::api::{Plugin, PluginMetadata}; -use async_trait::async_trait; -use std::{any::Any, path::Path}; +use std::{any::Any, path::Path, pin::Pin}; use thiserror::Error; pub mod native; -/// Common trait for all plugin loaders -#[async_trait] +pub type PluginLoadFuture<'a> = Pin< + Box< + dyn Future< + Output = Result< + ( + Box, + PluginMetadata<'static>, + Box, + ), + LoaderError, + >, + > + Send + + 'a, + >, +>; + +pub type PluginUnloadFuture<'a> = + Pin> + Send + 'a>>; + pub trait PluginLoader: Send + Sync { /// Load a plugin from the specified path - async fn load( - &self, - path: &Path, - ) -> Result< - ( - Box, - PluginMetadata<'static>, - Box, - ), - LoaderError, - >; + fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a>; /// Check if this loader can handle the given file fn can_load(&self, path: &Path) -> bool; - async fn unload(&self, data: Box) -> Result<(), LoaderError>; + fn unload(&self, data: Box) -> PluginUnloadFuture<'_>; /// Checks if the plugin can be safely unloaded. fn can_unload(&self) -> bool; diff --git a/pumpkin/src/plugin/loader/native.rs b/pumpkin/src/plugin/loader/native.rs index 079574d75..50ff3c015 100644 --- a/pumpkin/src/plugin/loader/native.rs +++ b/pumpkin/src/plugin/loader/native.rs @@ -2,47 +2,41 @@ use std::any::Any; use libloading::Library; -use super::{LoaderError, Path, Plugin, PluginLoader, PluginMetadata, async_trait}; +use crate::plugin::loader::{PluginLoadFuture, PluginUnloadFuture}; + +use super::{LoaderError, Path, Plugin, PluginLoader, PluginMetadata}; #[derive(Debug)] pub struct NativePluginLoader; -#[async_trait] impl PluginLoader for NativePluginLoader { - async fn load( - &self, - path: &Path, - ) -> Result< - ( - Box, - PluginMetadata<'static>, - Box, - ), - LoaderError, - > { - let path = path.to_owned(); - let library = tokio::task::spawn_blocking(move || unsafe { Library::new(&path) }) - .await - .map_err(|e| LoaderError::RuntimeError(e.to_string()))? - .map_err(|e| LoaderError::LibraryLoad(e.to_string()))?; + fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a> { + Box::pin(async { + let path = path.to_owned(); - let metadata = unsafe { - &**library - .get::<*const PluginMetadata>(b"METADATA") - .map_err(|_| LoaderError::MetadataMissing)? - }; + let library = unsafe { Library::new(&path) } + .map_err(|e| LoaderError::LibraryLoad(e.to_string()))?; - let plugin = unsafe { - library - .get:: Box>(b"plugin") - .map_err(|_| LoaderError::EntrypointMissing)? - }; + // 2. Extract Metadata (METADATA) + let metadata = unsafe { + &**library + .get::<*const PluginMetadata>(b"METADATA") + .map_err(|_| LoaderError::MetadataMissing)? + }; - Ok(( - plugin(), - metadata.clone(), - Box::new(library) as Box, - )) + // 3. Extract Plugin Factory (plugin) + let plugin_factory = unsafe { + library + .get:: Box>(b"plugin") + .map_err(|_| LoaderError::EntrypointMissing)? + }; + + Ok(( + plugin_factory(), + metadata.clone(), + Box::new(library) as Box, + )) + }) } fn can_load(&self, path: &Path) -> bool { @@ -60,14 +54,17 @@ impl PluginLoader for NativePluginLoader { } } - async fn unload(&self, data: Box) -> Result<(), LoaderError> { - match data.downcast::() { - Ok(_) => Ok(()), - Err(_) => Err(LoaderError::InvalidLoaderData), - } + fn unload(&self, data: Box) -> PluginUnloadFuture<'_> { + Box::pin(async { + data.downcast::() + .map_or(Err(LoaderError::InvalidLoaderData), |library| { + drop(library); + Ok(()) + }) + }) } - /// Windows specific issue + /// Windows specific issue: Windows locks DLLs, so we must indicate they cannot be unloaded. fn can_unload(&self) -> bool { !cfg!(target_os = "windows") } diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index b3cf4a914..ea3a02db8 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -1,4 +1,5 @@ use std::num::NonZeroU32; +use std::pin::Pin; use std::sync::Weak; use std::sync::atomic::Ordering::Relaxed; use std::time::Duration; @@ -32,7 +33,6 @@ use crate::{ server::Server, }; use crate::{block::BlockEvent, entity::item::ItemEntity}; -use async_trait::async_trait; use border::Worldborder; use explosion::Explosion; use pumpkin_config::BasicConfiguration; @@ -103,7 +103,7 @@ use pumpkin_util::{ math::{position::chunk_section_from_pos, vector2::Vector2}, random::{RandomImpl, get_seed, xoroshiro128::Xoroshiro}, }; -use pumpkin_world::world::GetBlockError; +use pumpkin_world::world::{GetBlockError, WorldFuture}; use pumpkin_world::{ BlockStateId, CURRENT_BEDROCK_MC_VERSION, GENERATION_SETTINGS, GeneratorSetting, biome, block::entities::BlockEntity, chunk::io::Dirtiable, inventory::Inventory, item::ItemStack, @@ -3141,100 +3141,143 @@ impl World { } } -#[async_trait] impl pumpkin_world::world::SimpleWorld for World { - async fn set_block_state( + fn set_block_state( self: Arc, position: &BlockPos, block_state_id: BlockStateId, flags: BlockFlags, - ) -> BlockStateId { - Self::set_block_state(&self, position, block_state_id, flags).await + ) -> WorldFuture<'_, BlockStateId> { + Box::pin(async move { Self::set_block_state(&self, position, block_state_id, flags).await }) } - async fn update_neighbor(self: Arc, neighbor_block_pos: &BlockPos, source_block: &Block) { - Self::update_neighbor(&self, neighbor_block_pos, source_block).await; + fn update_neighbor<'a>( + self: Arc, + neighbor_block_pos: &'a BlockPos, + source_block: &'a pumpkin_data::Block, + ) -> WorldFuture<'a, ()> { + Box::pin(async move { + Self::update_neighbor(&self, neighbor_block_pos, source_block).await; + }) } - async fn update_neighbors( + fn update_neighbors( self: Arc, block_pos: &BlockPos, except: Option, - ) { - Self::update_neighbors(&self, block_pos, except).await; + ) -> WorldFuture<'_, ()> { + Box::pin(async move { + Self::update_neighbors(&self, block_pos, except).await; + }) } - async fn is_space_empty(&self, bounding_box: BoundingBox) -> bool { - self.is_space_empty(bounding_box).await + fn is_space_empty(&self, bounding_box: BoundingBox) -> WorldFuture<'_, bool> { + Box::pin(async move { self.is_space_empty(bounding_box).await }) } - async fn add_synced_block_event(&self, pos: BlockPos, r#type: u8, data: u8) { - self.add_synced_block_event(pos, r#type, data).await; + fn add_synced_block_event(&self, pos: BlockPos, r#type: u8, data: u8) -> WorldFuture<'_, ()> { + Box::pin(async move { + self.add_synced_block_event(pos, r#type, data).await; + }) } - async fn sync_world_event(&self, world_event: WorldEvent, position: BlockPos, data: i32) { - self.sync_world_event(world_event, position, data).await; + fn sync_world_event( + &self, + world_event: WorldEvent, + position: BlockPos, + data: i32, + ) -> WorldFuture<'_, ()> { + Box::pin(async move { + self.sync_world_event(world_event, position, data).await; + }) } - async fn spawn_from_type( + fn spawn_from_type( self: Arc, entity_type: &'static EntityType, position: Vector3, - ) { - let mob = from_type(entity_type, position, &self, Uuid::new_v4()).await; - self.spawn_entity(mob).await; + ) -> WorldFuture<'static, ()> { + Box::pin(async move { + let mob = from_type(entity_type, position, &self, Uuid::new_v4()).await; + self.spawn_entity(mob).await; + }) } - async fn remove_block_entity(&self, block_pos: &BlockPos) { - self.remove_block_entity(block_pos).await; + fn remove_block_entity<'a>(&'a self, block_pos: &'a BlockPos) -> WorldFuture<'a, ()> { + Box::pin(async move { + self.remove_block_entity(block_pos).await; + }) } - async fn get_block_entity(&self, block_pos: &BlockPos) -> Option> { - self.get_block_entity(block_pos).await + fn get_block_entity<'a>( + &'a self, + block_pos: &'a BlockPos, + ) -> WorldFuture<'a, Option>> { + Box::pin(async move { self.get_block_entity(block_pos).await }) } - async fn get_world_age(&self) -> i64 { - self.level_time.lock().await.world_age + fn get_world_age(&self) -> WorldFuture<'_, i64> { + Box::pin(async move { + // Note: MutexGuard must be released before returning the future's result. + let level_time_guard = self.level_time.lock().await; + level_time_guard.world_age + }) } - async fn play_sound(&self, sound: Sound, category: SoundCategory, position: &Vector3) { - self.play_sound(sound, category, position).await; - } - - async fn play_sound_fine( - &self, + fn play_sound<'a>( + &'a self, sound: Sound, category: SoundCategory, - position: &Vector3, + position: &'a Vector3, + ) -> WorldFuture<'a, ()> { + Box::pin(async move { + self.play_sound(sound, category, position).await; + }) + } + + fn play_sound_fine<'a>( + &'a self, + sound: Sound, + category: SoundCategory, + position: &'a Vector3, volume: f32, pitch: f32, - ) { - self.play_sound_fine(sound, category, position, volume, pitch) - .await; + ) -> WorldFuture<'a, ()> { + Box::pin(async move { + self.play_sound_fine(sound, category, position, volume, pitch) + .await; + }) } - async fn scatter_inventory( + fn scatter_inventory<'a>( self: Arc, - position: &BlockPos, - inventory: &Arc, - ) { - Self::scatter_inventory(&self, position, inventory).await; + position: &'a BlockPos, + inventory: &'a Arc, + ) -> WorldFuture<'a, ()> { + Box::pin(async move { + Self::scatter_inventory(&self, position, inventory).await; + }) } } -#[async_trait] impl BlockAccessor for World { - async fn get_block(&self, position: &BlockPos) -> &'static Block { - Self::get_block(self, position).await + fn get_block<'a>( + &'a self, + position: &'a BlockPos, + ) -> Pin + Send + 'a>> { + Box::pin(async move { Self::get_block(self, position).await }) } - async fn get_block_state(&self, position: &BlockPos) -> &'static BlockState { - Self::get_block_state(self, position).await + fn get_block_state<'a>( + &'a self, + position: &'a BlockPos, + ) -> Pin + Send + 'a>> { + Box::pin(async move { Self::get_block_state(self, position).await }) } - async fn get_block_and_state( - &self, - position: &BlockPos, - ) -> (&'static Block, &'static BlockState) { - self.get_block_and_state(position).await + fn get_block_and_state<'a>( + &'a self, + position: &'a BlockPos, + ) -> Pin + Send + 'a>> { + Box::pin(async move { self.get_block_and_state(position).await }) } }