remove #[async_trait] from many many places
Compile time got improved by 77% (including last commit)
This commit is contained in:
parent
71392b4a38
commit
ae751f3a83
@ -86,54 +86,39 @@ pub struct PropertyStruct {
|
|||||||
|
|
||||||
impl ToTokens for PropertyStruct {
|
impl ToTokens for PropertyStruct {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
if self.values == vec!["true".to_string(), "false".to_string()] {
|
if self.values[0] == "true" && self.values[1] == "false" {
|
||||||
// For boolean properties, we'll use Rust's built-in bool type
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = Ident::new(&self.name, Span::call_site());
|
let name = Ident::new(&self.name, Span::call_site());
|
||||||
|
let count = self.values.len();
|
||||||
|
let variant_count = count as u16;
|
||||||
|
|
||||||
let variant_count = self.values.clone().len() as u16;
|
let is_number_values = !self.values.is_empty()
|
||||||
let values_index = (0..self.values.clone().len() as u16).collect::<Vec<_>>();
|
&& self.values.iter().all(|v| v.starts_with('L'))
|
||||||
|
&& self.values.iter().any(|v| v == "L1");
|
||||||
|
|
||||||
let ident_values = self
|
let mut variants = Vec::with_capacity(count);
|
||||||
.values
|
let mut literals = Vec::with_capacity(count);
|
||||||
.iter()
|
let mut indices = Vec::with_capacity(count);
|
||||||
.map(|value| Ident::new(&(value).to_upper_camel_case(), Span::call_site()));
|
|
||||||
|
|
||||||
let values_2 = ident_values.clone();
|
for (i, raw_value) in self.values.iter().enumerate() {
|
||||||
let values_3 = ident_values.clone();
|
let ident = Ident::new(&raw_value.to_upper_camel_case(), Span::call_site());
|
||||||
|
|
||||||
let is_number_values =
|
let literal_str = if is_number_values {
|
||||||
self.values.iter().all(|v| v.starts_with("L")) && self.values.iter().any(|v| v == "L1");
|
raw_value.strip_prefix('L').unwrap_or(raw_value)
|
||||||
|
|
||||||
let from_values = self.values.iter().map(|value| {
|
|
||||||
let ident = Ident::new(&(value).to_upper_camel_case(), Span::call_site());
|
|
||||||
let value = if is_number_values {
|
|
||||||
value.strip_prefix("L").unwrap()
|
|
||||||
} else {
|
} else {
|
||||||
value
|
raw_value.as_str()
|
||||||
};
|
};
|
||||||
quote! {
|
|
||||||
#value => Self::#ident
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let to_values = self.values.iter().map(|value| {
|
|
||||||
let ident = Ident::new(&(value).to_upper_camel_case(), Span::call_site());
|
|
||||||
let value = if is_number_values {
|
|
||||||
value.strip_prefix("L").unwrap()
|
|
||||||
} else {
|
|
||||||
value
|
|
||||||
};
|
|
||||||
quote! {
|
|
||||||
Self::#ident => #value
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
variants.push(ident);
|
||||||
|
literals.push(literal_str);
|
||||||
|
indices.push(i as u16);
|
||||||
|
}
|
||||||
tokens.extend(quote! {
|
tokens.extend(quote! {
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub enum #name {
|
pub enum #name {
|
||||||
#(#ident_values),*
|
#(#variants),*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnumVariants for #name {
|
impl EnumVariants for #name {
|
||||||
@ -143,30 +128,29 @@ impl ToTokens for PropertyStruct {
|
|||||||
|
|
||||||
fn to_index(&self) -> u16 {
|
fn to_index(&self) -> u16 {
|
||||||
match self {
|
match self {
|
||||||
#(Self::#values_2 => #values_index),*
|
#(Self::#variants => #indices),*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_index(index: u16) -> Self {
|
fn from_index(index: u16) -> Self {
|
||||||
match index {
|
match index {
|
||||||
#(#values_index => Self::#values_3,)*
|
#(#indices => Self::#variants,)*
|
||||||
_ => panic!("Invalid index: {index}"),
|
_ => panic!("Invalid index: {index}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_value(&self) -> &'static str {
|
fn to_value(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
#(#to_values),*
|
#(Self::#variants => #literals),*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_value(value: &str) -> Self {
|
fn from_value(value: &str) -> Self {
|
||||||
match value {
|
match value {
|
||||||
#(#from_values),*,
|
#(#literals => Self::#variants),*,
|
||||||
_ => panic!("Invalid value: {value}"),
|
_ => panic!("Invalid value: {value}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use std::any::Any;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::{any::Any, pin::Pin};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use pumpkin_world::{inventory::split_stack, item::ItemStack};
|
use pumpkin_world::{inventory::split_stack, item::ItemStack};
|
||||||
@ -82,11 +82,12 @@ impl RecipeInputInventory for CraftingInventory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Clearable for CraftingInventory {
|
impl Clearable for CraftingInventory {
|
||||||
async fn clear(&self) {
|
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
|
||||||
for slot in self.items.iter() {
|
Box::pin(async move {
|
||||||
*slot.lock().await = ItemStack::EMPTY.clone();
|
for item in self.items.iter() {
|
||||||
}
|
*item.lock().await = ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicU8, Ordering};
|
use std::sync::atomic::{AtomicU8, Ordering};
|
||||||
|
|
||||||
@ -6,7 +7,8 @@ use super::recipes::{RecipeFinderScreenHandler, RecipeInputInventory};
|
|||||||
use crate::crafting::crafting_inventory::CraftingInventory;
|
use crate::crafting::crafting_inventory::CraftingInventory;
|
||||||
use crate::player::player_inventory::PlayerInventory;
|
use crate::player::player_inventory::PlayerInventory;
|
||||||
use crate::screen_handler::{
|
use crate::screen_handler::{
|
||||||
InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerListener,
|
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture,
|
||||||
|
ScreenHandlerListener,
|
||||||
};
|
};
|
||||||
use crate::slot::{NormalSlot, Slot};
|
use crate::slot::{NormalSlot, Slot};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
@ -406,48 +408,54 @@ impl Slot for ResultSlot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ScreenHandlerListener for ResultSlot {
|
impl ScreenHandlerListener for ResultSlot {
|
||||||
async fn on_slot_update(
|
fn on_slot_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
screen_handler: &ScreenHandlerBehaviour,
|
screen_handler: &'a ScreenHandlerBehaviour,
|
||||||
slot: u8,
|
slot: u8,
|
||||||
_stack: ItemStack,
|
_stack: ItemStack,
|
||||||
) {
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
if (0..=(self.inventory.get_width() * self.inventory.get_height()))
|
Box::pin(async move {
|
||||||
.contains(&(slot as usize))
|
if (0..=(self.inventory.get_width() * self.inventory.get_height()))
|
||||||
{
|
.contains(&(slot as usize))
|
||||||
let result = self.refill_output().await;
|
{
|
||||||
|
let result = self.refill_output().await;
|
||||||
|
|
||||||
let next_revision = screen_handler.next_revision();
|
let next_revision = screen_handler.next_revision();
|
||||||
if let Some(sync_handler) = screen_handler.sync_handler.as_ref() {
|
if let Some(sync_handler) = screen_handler.sync_handler.as_ref() {
|
||||||
sync_handler
|
sync_handler
|
||||||
.update_slot(screen_handler, 0, &result, next_revision)
|
.update_slot(screen_handler, 0, &result, next_revision)
|
||||||
.await;
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AbstractCraftingScreenHandler.java
|
// AbstractCraftingScreenHandler.java
|
||||||
#[async_trait]
|
|
||||||
pub trait CraftingScreenHandler<I: RecipeInputInventory>:
|
pub trait CraftingScreenHandler<I: RecipeInputInventory>:
|
||||||
RecipeFinderScreenHandler + ScreenHandler
|
RecipeFinderScreenHandler + ScreenHandler
|
||||||
{
|
{
|
||||||
async fn add_recipe_slots(&mut self, crafing_inventory: Arc<dyn RecipeInputInventory>) {
|
fn add_recipe_slots<'a>(
|
||||||
let result_slot = Arc::new(ResultSlot::new(crafing_inventory.clone()));
|
&'a mut self,
|
||||||
self.add_slot(result_slot.clone());
|
crafing_inventory: Arc<dyn RecipeInputInventory>,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let result_slot = Arc::new(ResultSlot::new(crafing_inventory.clone()));
|
||||||
|
self.add_slot(result_slot.clone());
|
||||||
|
|
||||||
let width = crafing_inventory.get_width();
|
let width = crafing_inventory.get_width();
|
||||||
let height = crafing_inventory.get_height();
|
let height = crafing_inventory.get_height();
|
||||||
for i in 0..width {
|
for i in 0..width {
|
||||||
for j in 0..height {
|
for j in 0..height {
|
||||||
let input_slot = NormalSlot::new(crafing_inventory.clone(), j + i * width);
|
// Assuming j + i * width is the correct slot index calculation
|
||||||
self.add_slot(Arc::new(input_slot));
|
let input_slot = NormalSlot::new(crafing_inventory.clone(), j + i * width);
|
||||||
|
self.add_slot(Arc::new(input_slot));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
self.add_listener(result_slot).await;
|
self.add_listener(result_slot).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,13 +489,14 @@ impl CraftingTableScreenHandler {
|
|||||||
|
|
||||||
impl RecipeFinderScreenHandler for CraftingTableScreenHandler {}
|
impl RecipeFinderScreenHandler for CraftingTableScreenHandler {}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ScreenHandler for CraftingTableScreenHandler {
|
impl ScreenHandler for CraftingTableScreenHandler {
|
||||||
async fn on_closed(&mut self, player: &dyn InventoryPlayer) {
|
fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
|
||||||
self.default_on_closed(player).await;
|
Box::pin(async move {
|
||||||
//TODO: this.craftingResultInventory.clear();
|
self.default_on_closed(player).await;
|
||||||
self.drop_inventory(player, self.crafting_inventory.clone())
|
//TODO: this.craftingResultInventory.clear();
|
||||||
.await;
|
self.drop_inventory(player, self.crafting_inventory.clone())
|
||||||
|
.await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
@ -502,77 +511,83 @@ impl ScreenHandler for CraftingTableScreenHandler {
|
|||||||
&mut self.behaviour
|
&mut self.behaviour
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn quick_move(&mut self, player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack {
|
fn quick_move<'a>(
|
||||||
let slot = self.get_behaviour().slots[slot_index as usize].clone();
|
&'a mut self,
|
||||||
|
player: &'a dyn InventoryPlayer,
|
||||||
|
slot_index: i32,
|
||||||
|
) -> ItemStackFuture<'a> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let slot = self.get_behaviour().slots[slot_index as usize].clone();
|
||||||
|
|
||||||
if slot.has_stack().await {
|
if slot.has_stack().await {
|
||||||
let slot_stack = slot.get_stack().await;
|
let slot_stack = slot.get_stack().await;
|
||||||
let mut slot_stack = slot_stack.lock().await;
|
let mut slot_stack = slot_stack.lock().await;
|
||||||
let stack_prev = slot_stack.clone();
|
let stack_prev = slot_stack.clone();
|
||||||
|
|
||||||
if slot_index == 0 {
|
if slot_index == 0 {
|
||||||
// From crafting result slot - move to player inventory (slots 10-46)
|
// From crafting result slot - move to player inventory (slots 10-46)
|
||||||
if !self.insert_item(&mut slot_stack, 10, 46, true).await {
|
if !self.insert_item(&mut slot_stack, 10, 46, true).await {
|
||||||
return ItemStack::EMPTY.clone();
|
return ItemStack::EMPTY.clone();
|
||||||
}
|
}
|
||||||
} else if (1..=9).contains(&slot_index) {
|
} else if (1..=9).contains(&slot_index) {
|
||||||
// From crafting input slots - try to move to player inventory (slots 10-46)
|
// From crafting input slots - try to move to player inventory (slots 10-46)
|
||||||
if !self.insert_item(&mut slot_stack, 10, 46, false).await {
|
if !self.insert_item(&mut slot_stack, 10, 46, false).await {
|
||||||
return ItemStack::EMPTY.clone();
|
return ItemStack::EMPTY.clone();
|
||||||
}
|
}
|
||||||
} else if (10..46).contains(&slot_index) {
|
} else if (10..46).contains(&slot_index) {
|
||||||
// From player inventory - try to move to crafting input slots first (1-9)
|
// From player inventory - try to move to crafting input slots first (1-9)
|
||||||
if !self.insert_item(&mut slot_stack, 1, 10, false).await {
|
if !self.insert_item(&mut slot_stack, 1, 10, false).await {
|
||||||
// If that fails, try moving within player inventory
|
// If that fails, try moving within player inventory
|
||||||
if slot_index < 37 {
|
if slot_index < 37 {
|
||||||
// From main inventory to hotbar
|
// From main inventory to hotbar
|
||||||
if !self.insert_item(&mut slot_stack, 37, 46, false).await {
|
if !self.insert_item(&mut slot_stack, 37, 46, false).await {
|
||||||
return ItemStack::EMPTY.clone();
|
return ItemStack::EMPTY.clone();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// From hotbar to main inventory
|
// From hotbar to main inventory
|
||||||
if !self.insert_item(&mut slot_stack, 10, 37, false).await {
|
if !self.insert_item(&mut slot_stack, 10, 37, false).await {
|
||||||
return ItemStack::EMPTY.clone();
|
return ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Any other slot - try to move to player inventory
|
||||||
|
if !self.insert_item(&mut slot_stack, 10, 46, false).await {
|
||||||
|
return ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Any other slot - try to move to player inventory
|
let stack = slot_stack.clone();
|
||||||
if !self.insert_item(&mut slot_stack, 10, 46, false).await {
|
drop(slot_stack); // release the lock before calling other methods
|
||||||
|
|
||||||
|
if stack.is_empty() {
|
||||||
|
slot.set_stack_prev(ItemStack::EMPTY.clone(), stack_prev.clone())
|
||||||
|
.await;
|
||||||
|
} else {
|
||||||
|
slot.mark_dirty().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if stack.item_count == stack_prev.item_count {
|
||||||
|
// Nothing changed
|
||||||
return ItemStack::EMPTY.clone();
|
return ItemStack::EMPTY.clone();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let stack = slot_stack.clone();
|
slot.on_take_item(player, &stack).await;
|
||||||
drop(slot_stack); // release the lock before calling other methods
|
|
||||||
|
|
||||||
if stack.is_empty() {
|
if slot_index == 0 {
|
||||||
slot.set_stack_prev(ItemStack::EMPTY.clone(), stack_prev.clone())
|
slot.on_quick_move_crafted(stack.clone(), stack_prev.clone())
|
||||||
.await;
|
.await;
|
||||||
} else {
|
// For crafting result slot, drop any remaining items
|
||||||
slot.mark_dirty().await;
|
if !stack.is_empty() {
|
||||||
}
|
player.drop_item(stack, false).await;
|
||||||
|
}
|
||||||
if stack.item_count == stack_prev.item_count {
|
|
||||||
// Nothing changed
|
|
||||||
return ItemStack::EMPTY.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
slot.on_take_item(player, &stack).await;
|
|
||||||
|
|
||||||
if slot_index == 0 {
|
|
||||||
slot.on_quick_move_crafted(stack.clone(), stack_prev.clone())
|
|
||||||
.await;
|
|
||||||
// For crafting result slot, drop any remaining items
|
|
||||||
if !stack.is_empty() {
|
|
||||||
player.drop_item(stack, false).await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return stack_prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
return stack_prev;
|
ItemStack::EMPTY.clone()
|
||||||
}
|
})
|
||||||
|
|
||||||
ItemStack::EMPTY.clone()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::{any::Any, sync::Arc};
|
use std::{any::Any, pin::Pin, sync::Arc};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use pumpkin_world::{
|
use pumpkin_world::{
|
||||||
@ -96,10 +96,11 @@ impl Inventory for DoubleInventory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Clearable for DoubleInventory {
|
impl Clearable for DoubleInventory {
|
||||||
async fn clear(&self) {
|
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
|
||||||
self.first.clear().await;
|
Box::pin(async move {
|
||||||
self.second.clear().await;
|
self.first.clear().await;
|
||||||
|
self.second.clear().await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
use std::{any::Any, sync::Arc};
|
use std::{any::Any, pin::Pin, sync::Arc};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::fuels::is_fuel;
|
use pumpkin_data::fuels::is_fuel;
|
||||||
use pumpkin_world::{block::entities::BlockEntity, inventory::Inventory, item::ItemStack};
|
use pumpkin_world::{block::entities::BlockEntity, inventory::Inventory, item::ItemStack};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
player::player_inventory::PlayerInventory,
|
player::player_inventory::PlayerInventory,
|
||||||
screen_handler::{
|
screen_handler::{
|
||||||
InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerListener,
|
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour,
|
||||||
ScreenProperty,
|
ScreenHandlerFuture, ScreenHandlerListener, ScreenProperty,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -36,19 +35,20 @@ impl FurnaceScreenHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct FurnaceScreenListener;
|
struct FurnaceScreenListener;
|
||||||
#[async_trait]
|
|
||||||
impl ScreenHandlerListener for FurnaceScreenListener {
|
impl ScreenHandlerListener for FurnaceScreenListener {
|
||||||
async fn on_property_update(
|
fn on_property_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
screen_handler: &ScreenHandlerBehaviour,
|
screen_handler: &'a ScreenHandlerBehaviour,
|
||||||
property: u8,
|
property: u8,
|
||||||
value: i32,
|
value: i32,
|
||||||
) {
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
if let Some(sync_handler) = screen_handler.sync_handler.as_ref() {
|
Box::pin(async move {
|
||||||
sync_handler
|
if let Some(sync_handler) = screen_handler.sync_handler.as_ref() {
|
||||||
.update_property(screen_handler, property as i32, value)
|
sync_handler
|
||||||
.await;
|
.update_property(screen_handler, property as i32, value)
|
||||||
}
|
.await;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,11 +84,12 @@ impl FurnaceScreenHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ScreenHandler for FurnaceScreenHandler {
|
impl ScreenHandler for FurnaceScreenHandler {
|
||||||
async fn on_closed(&mut self, player: &dyn InventoryPlayer) {
|
fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
|
||||||
self.default_on_closed(player).await;
|
Box::pin(async move {
|
||||||
//TODO: self.inventory.on_closed(player).await;
|
self.default_on_closed(player).await;
|
||||||
|
// TODO: self.inventory.on_closed(player).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
@ -103,40 +104,55 @@ impl ScreenHandler for FurnaceScreenHandler {
|
|||||||
&mut self.behaviour
|
&mut self.behaviour
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack {
|
fn quick_move<'a>(
|
||||||
const FUEL_SLOT: i32 = 1;
|
&'a mut self,
|
||||||
|
_player: &'a dyn InventoryPlayer,
|
||||||
|
slot_index: i32,
|
||||||
|
) -> ItemStackFuture<'a> {
|
||||||
|
Box::pin(async move {
|
||||||
|
const FUEL_SLOT: i32 = 1; // Note: Slots 0, 1, 2 are Furnace slots.
|
||||||
|
|
||||||
let mut stack_left = ItemStack::EMPTY.clone();
|
let mut stack_left = ItemStack::EMPTY.clone();
|
||||||
let slot = self.get_behaviour().slots[slot_index as usize].clone();
|
|
||||||
|
|
||||||
if !slot.has_stack().await {
|
let slot = self.get_behaviour().slots[slot_index as usize].clone();
|
||||||
return stack_left;
|
|
||||||
}
|
|
||||||
|
|
||||||
let slot_stack = slot.get_stack().await;
|
if !slot.has_stack().await {
|
||||||
let mut stack = slot_stack.lock().await;
|
return stack_left;
|
||||||
stack_left = stack.clone();
|
}
|
||||||
|
|
||||||
let success = if slot_index < 3 {
|
let slot_stack_lock = slot.get_stack().await;
|
||||||
self.insert_item(&mut stack, 3, self.get_behaviour().slots.len() as i32, true)
|
|
||||||
.await
|
|
||||||
} else if is_fuel(stack.item.id) {
|
|
||||||
self.insert_item(&mut stack, FUEL_SLOT, 3, false).await
|
|
||||||
} else {
|
|
||||||
self.insert_item(&mut stack, 0, 3, false).await
|
|
||||||
};
|
|
||||||
|
|
||||||
if !success {
|
// Acquire the lock to read/clone the stack
|
||||||
return ItemStack::EMPTY.clone();
|
let mut stack = slot_stack_lock.lock().await;
|
||||||
}
|
stack_left = stack.clone();
|
||||||
|
|
||||||
if stack.is_empty() {
|
let success = if slot_index < 3 {
|
||||||
drop(stack);
|
// If clicked slot is one of the Furnace slots (0, 1, 2):
|
||||||
slot.set_stack(ItemStack::EMPTY.clone()).await;
|
// Try to move to player inventory (slots 3 onwards, starting from the end)
|
||||||
} else {
|
self.insert_item(&mut stack, 3, self.get_behaviour().slots.len() as i32, true)
|
||||||
slot.mark_dirty().await;
|
.await
|
||||||
}
|
} else if is_fuel(stack.item.id) {
|
||||||
|
// If clicked slot is in the player inventory (3+) and contains fuel:
|
||||||
|
// Try to move to the Furnace's Fuel slot (slot 1)
|
||||||
|
self.insert_item(&mut stack, FUEL_SLOT, 3, false).await
|
||||||
|
} else {
|
||||||
|
// If clicked slot is in the player inventory (3+) and NOT fuel (must be a smeltable item):
|
||||||
|
// Try to move to the Furnace's Input/Smelting slot (slot 0)
|
||||||
|
self.insert_item(&mut stack, 0, 3, false).await
|
||||||
|
};
|
||||||
|
|
||||||
stack_left
|
if !success {
|
||||||
|
return ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
if stack.is_empty() {
|
||||||
|
drop(stack); // Release lock before awaiting
|
||||||
|
slot.set_stack(ItemStack::EMPTY.clone()).await;
|
||||||
|
} else {
|
||||||
|
slot.mark_dirty().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_left
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
use std::{any::Any, sync::Arc};
|
use std::{any::Any, sync::Arc};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::screen::WindowType;
|
use pumpkin_data::screen::WindowType;
|
||||||
use pumpkin_world::{inventory::Inventory, item::ItemStack};
|
use pumpkin_world::{inventory::Inventory, item::ItemStack};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
player::player_inventory::PlayerInventory,
|
player::player_inventory::PlayerInventory,
|
||||||
screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour},
|
screen_handler::{
|
||||||
|
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour,
|
||||||
|
ScreenHandlerFuture,
|
||||||
|
},
|
||||||
slot::NormalSlot,
|
slot::NormalSlot,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -119,11 +121,12 @@ impl GenericContainerScreenHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ScreenHandler for GenericContainerScreenHandler {
|
impl ScreenHandler for GenericContainerScreenHandler {
|
||||||
async fn on_closed(&mut self, player: &dyn InventoryPlayer) {
|
fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
|
||||||
self.default_on_closed(player).await;
|
Box::pin(async move {
|
||||||
self.inventory.on_close().await;
|
self.default_on_closed(player).await;
|
||||||
|
self.inventory.on_close().await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
@ -138,45 +141,58 @@ impl ScreenHandler for GenericContainerScreenHandler {
|
|||||||
&mut self.behaviour
|
&mut self.behaviour
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack {
|
fn quick_move<'a>(
|
||||||
let mut stack_left = ItemStack::EMPTY.clone();
|
&'a mut self,
|
||||||
let slot = self.get_behaviour().slots[slot_index as usize].clone();
|
_player: &'a dyn InventoryPlayer,
|
||||||
|
slot_index: i32,
|
||||||
|
) -> ItemStackFuture<'a> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut stack_left = ItemStack::EMPTY.clone();
|
||||||
|
// Assuming bounds check passed for slot_index by caller or within quick_move spec
|
||||||
|
let slot = self.get_behaviour().slots[slot_index as usize].clone();
|
||||||
|
|
||||||
if slot.has_stack().await {
|
if slot.has_stack().await {
|
||||||
let slot_stack = slot.get_stack().await;
|
let slot_stack_lock = slot.get_stack().await;
|
||||||
stack_left = slot_stack.lock().await.clone();
|
let slot_stack_guard = slot_stack_lock.lock().await;
|
||||||
|
stack_left = slot_stack_guard.clone();
|
||||||
|
// Release the guard before calling insert_item which needs its own lock
|
||||||
|
drop(slot_stack_guard);
|
||||||
|
|
||||||
if slot_index < (self.rows * 9) as i32 {
|
// Re-acquire lock for insert_item (which expects &mut ItemStack)
|
||||||
if !self
|
let mut slot_stack_mut = slot_stack_lock.lock().await;
|
||||||
.insert_item(
|
|
||||||
&mut *slot_stack.lock().await,
|
if slot_index < (self.rows * 9) as i32 {
|
||||||
(self.rows * 9).into(),
|
// Move from inventory to player area (end)
|
||||||
self.get_behaviour().slots.len() as i32,
|
if !self
|
||||||
true,
|
.insert_item(
|
||||||
)
|
&mut slot_stack_mut,
|
||||||
|
(self.rows * 9).into(),
|
||||||
|
self.get_behaviour().slots.len() as i32,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
return ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
} else if !self
|
||||||
|
.insert_item(&mut slot_stack_mut, 0, (self.rows * 9).into(), false)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
// Move from player area to inventory (start)
|
||||||
return ItemStack::EMPTY.clone();
|
return ItemStack::EMPTY.clone();
|
||||||
}
|
}
|
||||||
} else if !self
|
|
||||||
.insert_item(
|
// Check the resulting state of the slot stack after insert_item
|
||||||
&mut *slot_stack.lock().await,
|
if slot_stack_mut.is_empty() {
|
||||||
0,
|
drop(slot_stack_mut); // Release lock
|
||||||
(self.rows * 9).into(),
|
slot.set_stack(ItemStack::EMPTY.clone()).await;
|
||||||
false,
|
} else {
|
||||||
)
|
drop(slot_stack_mut); // Release lock
|
||||||
.await
|
slot.mark_dirty().await;
|
||||||
{
|
}
|
||||||
return ItemStack::EMPTY.clone();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if stack_left.is_empty() {
|
stack_left
|
||||||
slot.set_stack(ItemStack::EMPTY.clone()).await;
|
})
|
||||||
} else {
|
|
||||||
slot.mark_dirty().await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return stack_left;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::{any::Any, array::from_fn, sync::Arc};
|
use std::{any::Any, array::from_fn, pin::Pin, sync::Arc};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use pumpkin_world::{
|
use pumpkin_world::{
|
||||||
@ -100,11 +100,12 @@ impl Inventory for EnderChestInventory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Clearable for EnderChestInventory {
|
impl Clearable for EnderChestInventory {
|
||||||
async fn clear(&self) {
|
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
|
||||||
for slot in self.items.iter() {
|
Box::pin(async move {
|
||||||
*slot.lock().await = ItemStack::EMPTY.clone();
|
for item in self.items.iter() {
|
||||||
}
|
*item.lock().await = ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ use pumpkin_world::item::ItemStack;
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::array::from_fn;
|
use std::array::from_fn;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicU8, Ordering};
|
use std::sync::atomic::{AtomicU8, Ordering};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
@ -307,14 +308,15 @@ impl PlayerInventory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Clearable for PlayerInventory {
|
impl Clearable for PlayerInventory {
|
||||||
async fn clear(&self) {
|
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
|
||||||
for item in self.main_inventory.iter() {
|
Box::pin(async move {
|
||||||
*item.lock().await = ItemStack::EMPTY.clone();
|
for item in self.main_inventory.iter() {
|
||||||
}
|
*item.lock().await = ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
|
||||||
self.entity_equipment.lock().await.clear();
|
self.entity_equipment.lock().await.clear();
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,10 @@ use super::player_inventory::PlayerInventory;
|
|||||||
use crate::crafting::crafting_inventory::CraftingInventory;
|
use crate::crafting::crafting_inventory::CraftingInventory;
|
||||||
use crate::crafting::crafting_screen_handler::CraftingScreenHandler;
|
use crate::crafting::crafting_screen_handler::CraftingScreenHandler;
|
||||||
use crate::crafting::recipes::{RecipeFinderScreenHandler, RecipeInputInventory};
|
use crate::crafting::recipes::{RecipeFinderScreenHandler, RecipeInputInventory};
|
||||||
use crate::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour};
|
use crate::screen_handler::{
|
||||||
|
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture,
|
||||||
|
};
|
||||||
use crate::slot::{ArmorSlot, NormalSlot, Slot};
|
use crate::slot::{ArmorSlot, NormalSlot, Slot};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::data_component_impl::{EquipmentSlot, EquipmentType, EquippableImpl};
|
use pumpkin_data::data_component_impl::{EquipmentSlot, EquipmentType, EquippableImpl};
|
||||||
use pumpkin_data::screen::WindowType;
|
use pumpkin_data::screen::WindowType;
|
||||||
use pumpkin_world::inventory::Inventory;
|
use pumpkin_world::inventory::Inventory;
|
||||||
@ -75,13 +76,14 @@ impl PlayerScreenHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ScreenHandler for PlayerScreenHandler {
|
impl ScreenHandler for PlayerScreenHandler {
|
||||||
async fn on_closed(&mut self, player: &dyn InventoryPlayer) {
|
fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
|
||||||
self.default_on_closed(player).await;
|
Box::pin(async move {
|
||||||
//TODO: this.craftingResultInventory.clear();
|
self.default_on_closed(player).await;
|
||||||
self.drop_inventory(player, self.crafting_inventory.clone())
|
//TODO: this.craftingResultInventory.clear();
|
||||||
.await;
|
self.drop_inventory(player, self.crafting_inventory.clone())
|
||||||
|
.await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
@ -99,114 +101,118 @@ impl ScreenHandler for PlayerScreenHandler {
|
|||||||
/// Do quick move (Shift + Click) for the given slot index.
|
/// Do quick move (Shift + Click) for the given slot index.
|
||||||
///
|
///
|
||||||
/// Returns the moved stack if successful, or `ItemStack::EMPTY` if nothing changed.
|
/// Returns the moved stack if successful, or `ItemStack::EMPTY` if nothing changed.
|
||||||
async fn quick_move(&mut self, player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack {
|
fn quick_move<'a>(
|
||||||
let slot = self.get_behaviour().slots[slot_index as usize].clone();
|
&'a mut self,
|
||||||
|
player: &'a dyn InventoryPlayer,
|
||||||
|
slot_index: i32,
|
||||||
|
) -> ItemStackFuture<'a> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let slot = self.get_behaviour().slots[slot_index as usize].clone();
|
||||||
|
|
||||||
// TODO: Equippable component
|
// TODO: Equippable component
|
||||||
|
|
||||||
if slot.has_stack().await {
|
if slot.has_stack().await {
|
||||||
let slot_stack = slot.get_stack().await;
|
let slot_stack_lock = slot.get_stack().await;
|
||||||
let mut slot_stack = slot_stack.lock().await;
|
let mut slot_stack = slot_stack_lock.lock().await;
|
||||||
let stack_prev = slot_stack.clone();
|
let stack_prev = slot_stack.clone();
|
||||||
|
|
||||||
let equipment_slot = slot_stack
|
let equipment_slot = slot_stack
|
||||||
.get_data_component::<EquippableImpl>()
|
.get_data_component::<EquippableImpl>()
|
||||||
.map_or(&EquipmentSlot::MAIN_HAND, |equippable| equippable.slot);
|
.map_or(&EquipmentSlot::MAIN_HAND, |equippable| equippable.slot);
|
||||||
|
|
||||||
#[allow(clippy::if_same_then_else)]
|
// Quick move logic
|
||||||
if slot_index == 0 {
|
#[allow(clippy::if_same_then_else)]
|
||||||
// From crafting result slot
|
let success = if slot_index == 0 {
|
||||||
if !self.insert_item(&mut slot_stack, 9, 45, true).await {
|
// From crafting result slot (0) -> Player Inventory (9-45, from end)
|
||||||
return ItemStack::EMPTY.clone();
|
self.insert_item(&mut slot_stack, 9, 45, true).await
|
||||||
}
|
} else if (1..5).contains(&slot_index) {
|
||||||
} else if (1..5).contains(&slot_index) {
|
// From craft ingredient slots (1-4) -> Player Inventory (9-45, from start)
|
||||||
// From craft ingredient slots
|
self.insert_item(&mut slot_stack, 9, 45, false).await
|
||||||
if !self.insert_item(&mut slot_stack, 9, 45, false).await {
|
} else if (5..9).contains(&slot_index) {
|
||||||
return ItemStack::EMPTY.clone();
|
// From armour slots (5-8) -> Player Inventory (9-45, from start)
|
||||||
}
|
let result = self.insert_item(&mut slot_stack, 9, 45, false).await;
|
||||||
} else if (5..9).contains(&slot_index) {
|
|
||||||
// From armour slots
|
|
||||||
if !self.insert_item(&mut slot_stack, 9, 45, false).await {
|
|
||||||
return ItemStack::EMPTY.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
player
|
if result {
|
||||||
.enqueue_equipment_change(equipment_slot, ItemStack::EMPTY)
|
player
|
||||||
.await;
|
.enqueue_equipment_change(equipment_slot, ItemStack::EMPTY)
|
||||||
} else if equipment_slot.slot_type() == EquipmentType::HumanoidArmor
|
.await;
|
||||||
&& self
|
}
|
||||||
.get_slot((8 - equipment_slot.get_entity_slot_id()) as usize)
|
result
|
||||||
.await
|
} else if equipment_slot.slot_type() == EquipmentType::HumanoidArmor
|
||||||
.get_cloned_stack()
|
&& self
|
||||||
.await
|
.get_slot((8 - equipment_slot.get_entity_slot_id()) as usize)
|
||||||
.is_empty()
|
.await
|
||||||
{
|
.get_cloned_stack()
|
||||||
// Into armour slots
|
.await
|
||||||
let index = 8 - equipment_slot.get_entity_slot_id();
|
.is_empty()
|
||||||
if !self
|
|
||||||
.insert_item(&mut slot_stack, index, index + 1, false)
|
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
return ItemStack::EMPTY.clone();
|
// Into empty armour slots (5-8)
|
||||||
}
|
let index = 8 - equipment_slot.get_entity_slot_id();
|
||||||
|
let result = self
|
||||||
|
.insert_item(&mut slot_stack, index, index + 1, false)
|
||||||
|
.await;
|
||||||
|
|
||||||
player
|
if result {
|
||||||
.enqueue_equipment_change(equipment_slot, &stack_prev)
|
player
|
||||||
.await;
|
.enqueue_equipment_change(equipment_slot, &stack_prev)
|
||||||
} else if matches!(equipment_slot, EquipmentSlot::OffHand(_))
|
.await;
|
||||||
&& slot_index != 45
|
}
|
||||||
&& self.get_slot(45).await.get_cloned_stack().await.is_empty()
|
result
|
||||||
{
|
} else if matches!(equipment_slot, EquipmentSlot::OffHand(_))
|
||||||
// Into offhand slot
|
&& slot_index != 45
|
||||||
let index = 45;
|
&& self.get_slot(45).await.get_cloned_stack().await.is_empty()
|
||||||
if !self
|
|
||||||
.insert_item(&mut slot_stack, index, index + 1, false)
|
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
|
// Into empty offhand slot (45)
|
||||||
|
let index = 45;
|
||||||
|
self.insert_item(&mut slot_stack, index, index + 1, false)
|
||||||
|
.await
|
||||||
|
} else if (9..36).contains(&slot_index) {
|
||||||
|
// From main inventory (9-35) -> Hotbar (36-44)
|
||||||
|
self.insert_item(&mut slot_stack, 36, 45, false).await
|
||||||
|
} else if (36..45).contains(&slot_index) {
|
||||||
|
// From hotbar (36-44) -> Main inventory (9-35)
|
||||||
|
self.insert_item(&mut slot_stack, 9, 36, false).await
|
||||||
|
} else {
|
||||||
|
// Fallback to moving into the player inventory area
|
||||||
|
self.insert_item(&mut slot_stack, 9, 45, false).await
|
||||||
|
};
|
||||||
|
|
||||||
|
if !success {
|
||||||
return ItemStack::EMPTY.clone();
|
return ItemStack::EMPTY.clone();
|
||||||
}
|
}
|
||||||
} else if (9..36).contains(&slot_index) {
|
|
||||||
if !self.insert_item(&mut slot_stack, 36, 45, false).await {
|
let stack = slot_stack.clone();
|
||||||
|
drop(slot_stack); // release the lock before calling other methods
|
||||||
|
|
||||||
|
if stack.is_empty() {
|
||||||
|
slot.set_stack_prev(ItemStack::EMPTY.clone(), stack_prev.clone())
|
||||||
|
.await;
|
||||||
|
} else {
|
||||||
|
slot.mark_dirty().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if stack.item_count == stack_prev.item_count {
|
||||||
return ItemStack::EMPTY.clone();
|
return ItemStack::EMPTY.clone();
|
||||||
}
|
}
|
||||||
} else if (36..45).contains(&slot_index) {
|
|
||||||
if !self.insert_item(&mut slot_stack, 9, 36, false).await {
|
slot.on_take_item(player, &stack).await;
|
||||||
return ItemStack::EMPTY.clone();
|
|
||||||
|
if slot_index == 0 {
|
||||||
|
// From crafting result slot (0)
|
||||||
|
// Notify the result slot to refill
|
||||||
|
slot.on_quick_move_crafted(stack.clone(), stack_prev.clone())
|
||||||
|
.await;
|
||||||
|
// For crafting result slot, drop any remaining items
|
||||||
|
if !stack.is_empty() {
|
||||||
|
player.drop_item(stack, false).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if !self.insert_item(&mut slot_stack, 9, 45, false).await {
|
|
||||||
return ItemStack::EMPTY.clone();
|
return stack_prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
let stack = slot_stack.clone();
|
// Nothing changed
|
||||||
drop(slot_stack); // release the lock before calling other methods
|
ItemStack::EMPTY.clone()
|
||||||
if stack.is_empty() {
|
})
|
||||||
slot.set_stack_prev(ItemStack::EMPTY.clone(), stack_prev.clone())
|
|
||||||
.await;
|
|
||||||
} else {
|
|
||||||
slot.mark_dirty().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
if stack.item_count == stack_prev.item_count {
|
|
||||||
return ItemStack::EMPTY.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
slot.on_take_item(player, &stack).await;
|
|
||||||
|
|
||||||
if slot_index == 0 {
|
|
||||||
// From crafting result slot
|
|
||||||
// Notify the result slot to refill
|
|
||||||
slot.on_quick_move_crafted(stack.clone(), stack_prev.clone())
|
|
||||||
.await;
|
|
||||||
// For crafting result slot, drop any remaining items
|
|
||||||
if !stack.is_empty() {
|
|
||||||
player.drop_item(stack, false).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return stack_prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nothing changed
|
|
||||||
ItemStack::EMPTY.clone()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -2,11 +2,13 @@ use async_trait::async_trait;
|
|||||||
use pumpkin_data::block_properties::{BarrelLikeProperties, BlockProperties};
|
use pumpkin_data::block_properties::{BarrelLikeProperties, BlockProperties};
|
||||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||||
use pumpkin_data::{Block, FacingExt};
|
use pumpkin_data::{Block, FacingExt};
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
use pumpkin_util::random::xoroshiro128::Xoroshiro;
|
use pumpkin_util::random::xoroshiro128::Xoroshiro;
|
||||||
use pumpkin_util::random::{RandomImpl, get_seed};
|
use pumpkin_util::random::{RandomImpl, get_seed};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::pin::Pin;
|
||||||
use std::{
|
use std::{
|
||||||
array::from_fn,
|
array::from_fn,
|
||||||
sync::{
|
sync::{
|
||||||
@ -16,7 +18,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker};
|
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture};
|
||||||
use crate::world::{BlockFlags, SimpleWorld};
|
use crate::world::{BlockFlags, SimpleWorld};
|
||||||
use crate::{
|
use crate::{
|
||||||
inventory::{
|
inventory::{
|
||||||
@ -37,7 +39,6 @@ pub struct BarrelBlockEntity {
|
|||||||
viewers: ViewerCountTracker,
|
viewers: ViewerCountTracker,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for BarrelBlockEntity {
|
impl BlockEntity for BarrelBlockEntity {
|
||||||
fn resource_location(&self) -> &'static str {
|
fn resource_location(&self) -> &'static str {
|
||||||
Self::ID
|
Self::ID
|
||||||
@ -63,16 +64,26 @@ impl BlockEntity for BarrelBlockEntity {
|
|||||||
barrel
|
barrel
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
fn write_nbt<'a>(
|
||||||
self.write_data(nbt, &self.items, true).await;
|
&'a self,
|
||||||
|
nbt: &'a mut NbtCompound,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.write_data(nbt, &self.items, true).await;
|
||||||
|
})
|
||||||
// Safety precaution
|
// Safety precaution
|
||||||
//self.clear().await;
|
//self.clear().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
|
fn tick<'a>(
|
||||||
self.viewers
|
&'a self,
|
||||||
.update_viewer_count::<BarrelBlockEntity>(self, world, &self.position)
|
world: Arc<dyn SimpleWorld>,
|
||||||
.await;
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.viewers
|
||||||
|
.update_viewer_count::<BarrelBlockEntity>(self, world, &self.position)
|
||||||
|
.await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
|
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
|
||||||
@ -88,16 +99,27 @@ impl BlockEntity for BarrelBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ViewerCountListener for BarrelBlockEntity {
|
impl ViewerCountListener for BarrelBlockEntity {
|
||||||
async fn on_container_open(&self, world: &Arc<dyn SimpleWorld>, _position: &BlockPos) {
|
fn on_container_open<'a>(
|
||||||
self.play_sound(world, Sound::BlockBarrelOpen).await;
|
&'a self,
|
||||||
self.set_open(world, true).await;
|
world: &'a Arc<dyn SimpleWorld>,
|
||||||
|
_position: &'a BlockPos,
|
||||||
|
) -> ViewerFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.play_sound(world, Sound::BlockBarrelOpen).await;
|
||||||
|
self.set_open(world, true).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_container_close(&self, world: &Arc<dyn SimpleWorld>, _position: &BlockPos) {
|
fn on_container_close<'a>(
|
||||||
self.play_sound(world, Sound::BlockBarrelClose).await;
|
&'a self,
|
||||||
self.set_open(world, false).await;
|
world: &'a Arc<dyn SimpleWorld>,
|
||||||
|
_position: &'a BlockPos,
|
||||||
|
) -> ViewerFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.play_sound(world, Sound::BlockBarrelClose).await;
|
||||||
|
self.set_open(world, false).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,11 +227,12 @@ impl Inventory for BarrelBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Clearable for BarrelBlockEntity {
|
impl Clearable for BarrelBlockEntity {
|
||||||
async fn clear(&self) {
|
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
|
||||||
for slot in self.items.iter() {
|
Box::pin(async move {
|
||||||
*slot.lock().await = ItemStack::EMPTY.clone();
|
for slot in self.items.iter() {
|
||||||
}
|
*slot.lock().await = ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
use async_trait::async_trait;
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
|
|
||||||
use super::BlockEntity;
|
use super::BlockEntity;
|
||||||
@ -7,7 +9,6 @@ pub struct BedBlockEntity {
|
|||||||
pub position: BlockPos,
|
pub position: BlockPos,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for BedBlockEntity {
|
impl BlockEntity for BedBlockEntity {
|
||||||
fn resource_location(&self) -> &'static str {
|
fn resource_location(&self) -> &'static str {
|
||||||
Self::ID
|
Self::ID
|
||||||
@ -24,7 +25,12 @@ impl BlockEntity for BedBlockEntity {
|
|||||||
Self { position }
|
Self { position }
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_nbt(&self, _nbt: &mut pumpkin_nbt::compound::NbtCompound) {}
|
fn write_nbt<'a>(
|
||||||
|
&'a self,
|
||||||
|
_nbt: &'a mut NbtCompound,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {})
|
||||||
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
array::from_fn,
|
array::from_fn,
|
||||||
|
pin::Pin,
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
Arc,
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
@ -13,6 +14,7 @@ use pumpkin_data::{
|
|||||||
block_properties::{BlockProperties, ChestLikeProperties, ChestType},
|
block_properties::{BlockProperties, ChestLikeProperties, ChestType},
|
||||||
sound::{Sound, SoundCategory},
|
sound::{Sound, SoundCategory},
|
||||||
};
|
};
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::{position::BlockPos, vector3::Vector3},
|
math::{position::BlockPos, vector3::Vector3},
|
||||||
random::{RandomImpl, get_seed, xoroshiro128::Xoroshiro},
|
random::{RandomImpl, get_seed, xoroshiro128::Xoroshiro},
|
||||||
@ -20,7 +22,7 @@ use pumpkin_util::{
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
block::viewer::{ViewerCountListener, ViewerCountTracker},
|
block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture},
|
||||||
inventory::{Clearable, Inventory, split_stack},
|
inventory::{Clearable, Inventory, split_stack},
|
||||||
item::ItemStack,
|
item::ItemStack,
|
||||||
world::SimpleWorld,
|
world::SimpleWorld,
|
||||||
@ -38,7 +40,6 @@ pub struct ChestBlockEntity {
|
|||||||
viewers: ViewerCountTracker,
|
viewers: ViewerCountTracker,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for ChestBlockEntity {
|
impl BlockEntity for ChestBlockEntity {
|
||||||
fn resource_location(&self) -> &'static str {
|
fn resource_location(&self) -> &'static str {
|
||||||
Self::ID
|
Self::ID
|
||||||
@ -64,16 +65,26 @@ impl BlockEntity for ChestBlockEntity {
|
|||||||
chest
|
chest
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
fn write_nbt<'a>(
|
||||||
self.write_data(nbt, &self.items, true).await;
|
&'a self,
|
||||||
|
nbt: &'a mut NbtCompound,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.write_data(nbt, &self.items, true).await;
|
||||||
|
})
|
||||||
// Safety precaution
|
// Safety precaution
|
||||||
//self.clear().await;
|
//self.clear().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
|
fn tick<'a>(
|
||||||
self.viewers
|
&'a self,
|
||||||
.update_viewer_count::<ChestBlockEntity>(self, world, &self.position)
|
world: Arc<dyn SimpleWorld>,
|
||||||
.await;
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.viewers
|
||||||
|
.update_viewer_count::<ChestBlockEntity>(self, world, &self.position)
|
||||||
|
.await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
|
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
|
||||||
@ -89,29 +100,41 @@ impl BlockEntity for ChestBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ViewerCountListener for ChestBlockEntity {
|
impl ViewerCountListener for ChestBlockEntity {
|
||||||
async fn on_container_open(&self, world: &Arc<dyn SimpleWorld>, _position: &BlockPos) {
|
fn on_container_open<'a>(
|
||||||
self.play_sound(world, Sound::BlockChestOpen).await;
|
&'a self,
|
||||||
|
world: &'a Arc<dyn SimpleWorld>,
|
||||||
|
_position: &'a BlockPos,
|
||||||
|
) -> ViewerFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.play_sound(world, Sound::BlockEnderChestOpen).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_container_close(&self, world: &Arc<dyn SimpleWorld>, _position: &BlockPos) {
|
fn on_container_close<'a>(
|
||||||
self.play_sound(world, Sound::BlockChestClose).await;
|
&'a self,
|
||||||
|
world: &'a Arc<dyn SimpleWorld>,
|
||||||
|
_position: &'a BlockPos,
|
||||||
|
) -> ViewerFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.play_sound(world, Sound::BlockEnderChestClose).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_viewer_count_update(
|
fn on_viewer_count_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
world: &Arc<dyn SimpleWorld>,
|
world: &'a Arc<dyn SimpleWorld>,
|
||||||
position: &BlockPos,
|
position: &'a BlockPos,
|
||||||
_old: u16,
|
_old: u16,
|
||||||
new: u16,
|
new: u16,
|
||||||
) {
|
) -> ViewerFuture<'a, ()> {
|
||||||
world
|
Box::pin(async move {
|
||||||
.add_synced_block_event(*position, Self::LID_ANIMATION_EVENT_TYPE, new as u8)
|
world
|
||||||
.await
|
.add_synced_block_event(*position, Self::LID_ANIMATION_EVENT_TYPE, new as u8)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChestBlockEntity {
|
impl ChestBlockEntity {
|
||||||
pub const INVENTORY_SIZE: usize = 27;
|
pub const INVENTORY_SIZE: usize = 27;
|
||||||
pub const LID_ANIMATION_EVENT_TYPE: u8 = 1;
|
pub const LID_ANIMATION_EVENT_TYPE: u8 = 1;
|
||||||
@ -212,11 +235,12 @@ impl Inventory for ChestBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Clearable for ChestBlockEntity {
|
impl Clearable for ChestBlockEntity {
|
||||||
async fn clear(&self) {
|
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
|
||||||
for slot in self.items.iter() {
|
Box::pin(async move {
|
||||||
*slot.lock().await = ItemStack::EMPTY.clone();
|
for slot in self.items.iter() {
|
||||||
}
|
*slot.lock().await = ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use pumpkin_data::block_properties::{BlockProperties, ChiseledBookshelfLikePrope
|
|||||||
use pumpkin_nbt::compound::NbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::pin::Pin;
|
||||||
use std::{
|
use std::{
|
||||||
array::from_fn,
|
array::from_fn,
|
||||||
sync::{
|
sync::{
|
||||||
@ -31,7 +32,6 @@ pub struct ChiseledBookshelfBlockEntity {
|
|||||||
|
|
||||||
const LAST_INTERACTED_SLOT: &str = "last_interacted_slot";
|
const LAST_INTERACTED_SLOT: &str = "last_interacted_slot";
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for ChiseledBookshelfBlockEntity {
|
impl BlockEntity for ChiseledBookshelfBlockEntity {
|
||||||
fn resource_location(&self) -> &'static str {
|
fn resource_location(&self) -> &'static str {
|
||||||
Self::ID
|
Self::ID
|
||||||
@ -59,12 +59,17 @@ impl BlockEntity for ChiseledBookshelfBlockEntity {
|
|||||||
chiseled_bookshelf
|
chiseled_bookshelf
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_nbt(&self, nbt: &mut NbtCompound) {
|
fn write_nbt<'a>(
|
||||||
self.write_data(nbt, &self.items, true).await;
|
&'a self,
|
||||||
nbt.put_int(
|
nbt: &'a mut NbtCompound,
|
||||||
LAST_INTERACTED_SLOT,
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
self.last_interacted_slot.load(Ordering::Relaxed).into(),
|
Box::pin(async move {
|
||||||
);
|
self.write_data(nbt, &self.items, true).await;
|
||||||
|
nbt.put_int(
|
||||||
|
LAST_INTERACTED_SLOT,
|
||||||
|
self.last_interacted_slot.load(Ordering::Relaxed).into(),
|
||||||
|
);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
|
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
|
||||||
@ -169,11 +174,12 @@ impl Inventory for ChiseledBookshelfBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Clearable for ChiseledBookshelfBlockEntity {
|
impl Clearable for ChiseledBookshelfBlockEntity {
|
||||||
async fn clear(&self) {
|
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
|
||||||
for slot in self.items.iter() {
|
Box::pin(async move {
|
||||||
*slot.lock().await = ItemStack::EMPTY.clone();
|
for slot in self.items.iter() {
|
||||||
}
|
*slot.lock().await = ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::{
|
||||||
|
pin::Pin,
|
||||||
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
|
|
||||||
use super::BlockEntity;
|
use super::BlockEntity;
|
||||||
@ -26,7 +29,6 @@ impl CommandBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for CommandBlockEntity {
|
impl BlockEntity for CommandBlockEntity {
|
||||||
fn resource_location(&self) -> &'static str {
|
fn resource_location(&self) -> &'static str {
|
||||||
Self::ID
|
Self::ID
|
||||||
@ -42,7 +44,12 @@ impl BlockEntity for CommandBlockEntity {
|
|||||||
Self::new(position)
|
Self::new(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_nbt(&self, _nbt: &mut pumpkin_nbt::compound::NbtCompound) {}
|
fn write_nbt<'a>(
|
||||||
|
&'a self,
|
||||||
|
_nbt: &'a mut NbtCompound,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {})
|
||||||
|
}
|
||||||
|
|
||||||
fn is_dirty(&self) -> bool {
|
fn is_dirty(&self) -> bool {
|
||||||
self.dirty.load(Ordering::Relaxed)
|
self.dirty.load(Ordering::Relaxed)
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
use std::sync::atomic::{AtomicU8, Ordering};
|
use std::{
|
||||||
|
pin::Pin,
|
||||||
|
sync::atomic::{AtomicU8, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_nbt::compound::NbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
|
|
||||||
@ -23,7 +25,6 @@ impl ComparatorBlockEntity {
|
|||||||
|
|
||||||
const OUTPUT_SIGNAL: &str = "OutputSignal";
|
const OUTPUT_SIGNAL: &str = "OutputSignal";
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for ComparatorBlockEntity {
|
impl BlockEntity for ComparatorBlockEntity {
|
||||||
fn resource_location(&self) -> &'static str {
|
fn resource_location(&self) -> &'static str {
|
||||||
Self::ID
|
Self::ID
|
||||||
@ -44,11 +45,16 @@ impl BlockEntity for ComparatorBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_nbt(&self, nbt: &mut NbtCompound) {
|
fn write_nbt<'a>(
|
||||||
nbt.put_int(
|
&'a self,
|
||||||
OUTPUT_SIGNAL,
|
nbt: &'a mut NbtCompound,
|
||||||
self.output_signal.load(Ordering::Relaxed) as i32,
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
);
|
Box::pin(async move {
|
||||||
|
nbt.put_int(
|
||||||
|
OUTPUT_SIGNAL,
|
||||||
|
self.output_signal.load(Ordering::Relaxed) as i32,
|
||||||
|
);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chunk_data_nbt(&self) -> Option<NbtCompound> {
|
fn chunk_data_nbt(&self) -> Option<NbtCompound> {
|
||||||
|
|||||||
@ -2,10 +2,12 @@ use crate::block::entities::BlockEntity;
|
|||||||
use crate::inventory::{Clearable, Inventory, split_stack};
|
use crate::inventory::{Clearable, Inventory, split_stack};
|
||||||
use crate::item::ItemStack;
|
use crate::item::ItemStack;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use rand::{Rng, rng};
|
use rand::{Rng, rng};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::array::from_fn;
|
use std::array::from_fn;
|
||||||
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use tokio::sync::{Mutex, MutexGuard};
|
use tokio::sync::{Mutex, MutexGuard};
|
||||||
@ -17,10 +19,14 @@ pub struct DropperBlockEntity {
|
|||||||
pub dirty: AtomicBool,
|
pub dirty: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for DropperBlockEntity {
|
impl BlockEntity for DropperBlockEntity {
|
||||||
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
fn write_nbt<'a>(
|
||||||
self.write_data(nbt, &self.items, true).await;
|
&'a self,
|
||||||
|
nbt: &'a mut NbtCompound,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.write_data(nbt, &self.items, true).await;
|
||||||
|
})
|
||||||
// Safety precaution
|
// Safety precaution
|
||||||
//self.clear().await;
|
//self.clear().await;
|
||||||
}
|
}
|
||||||
@ -133,11 +139,12 @@ impl Inventory for DropperBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Clearable for DropperBlockEntity {
|
impl Clearable for DropperBlockEntity {
|
||||||
async fn clear(&self) {
|
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
|
||||||
for slot in self.items.iter() {
|
Box::pin(async move {
|
||||||
*slot.lock().await = ItemStack::EMPTY.clone();
|
for slot in self.items.iter() {
|
||||||
}
|
*slot.lock().await = ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
use async_trait::async_trait;
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
|
|
||||||
use super::BlockEntity;
|
use super::BlockEntity;
|
||||||
@ -14,7 +16,6 @@ impl EndPortalBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for EndPortalBlockEntity {
|
impl BlockEntity for EndPortalBlockEntity {
|
||||||
fn resource_location(&self) -> &'static str {
|
fn resource_location(&self) -> &'static str {
|
||||||
Self::ID
|
Self::ID
|
||||||
@ -31,7 +32,12 @@ impl BlockEntity for EndPortalBlockEntity {
|
|||||||
Self { position }
|
Self { position }
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_nbt(&self, _nbt: &mut pumpkin_nbt::compound::NbtCompound) {}
|
fn write_nbt<'a>(
|
||||||
|
&'a self,
|
||||||
|
_nbt: &'a mut NbtCompound,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {})
|
||||||
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||||
use pumpkin_nbt::compound::NbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_util::random::xoroshiro128::Xoroshiro;
|
use pumpkin_util::random::xoroshiro128::Xoroshiro;
|
||||||
use pumpkin_util::random::{RandomImpl, get_seed};
|
use pumpkin_util::random::{RandomImpl, get_seed};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker};
|
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture};
|
||||||
use crate::world::SimpleWorld;
|
use crate::world::SimpleWorld;
|
||||||
|
|
||||||
use super::BlockEntity;
|
use super::BlockEntity;
|
||||||
@ -20,7 +20,6 @@ pub struct EnderChestBlockEntity {
|
|||||||
viewers: Arc<ViewerCountTracker>,
|
viewers: Arc<ViewerCountTracker>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for EnderChestBlockEntity {
|
impl BlockEntity for EnderChestBlockEntity {
|
||||||
fn resource_location(&self) -> &'static str {
|
fn resource_location(&self) -> &'static str {
|
||||||
Self::ID
|
Self::ID
|
||||||
@ -40,12 +39,22 @@ impl BlockEntity for EnderChestBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_nbt(&self, _nbt: &mut NbtCompound) {}
|
fn write_nbt<'a>(
|
||||||
|
&'a self,
|
||||||
|
_nbt: &'a mut NbtCompound,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {})
|
||||||
|
}
|
||||||
|
|
||||||
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
|
fn tick<'a>(
|
||||||
self.viewers
|
&'a self,
|
||||||
.update_viewer_count::<EnderChestBlockEntity>(self, world, &self.position)
|
world: Arc<dyn SimpleWorld>,
|
||||||
.await;
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.viewers
|
||||||
|
.update_viewer_count::<EnderChestBlockEntity>(self, world, &self.position)
|
||||||
|
.await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
@ -53,26 +62,39 @@ impl BlockEntity for EnderChestBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ViewerCountListener for EnderChestBlockEntity {
|
impl ViewerCountListener for EnderChestBlockEntity {
|
||||||
async fn on_container_open(&self, world: &Arc<dyn SimpleWorld>, _position: &BlockPos) {
|
fn on_container_open<'a>(
|
||||||
self.play_sound(world, Sound::BlockEnderChestOpen).await;
|
&'a self,
|
||||||
|
world: &'a Arc<dyn SimpleWorld>,
|
||||||
|
_position: &'a BlockPos,
|
||||||
|
) -> ViewerFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.play_sound(world, Sound::BlockEnderChestOpen).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_container_close(&self, world: &Arc<dyn SimpleWorld>, _position: &BlockPos) {
|
fn on_container_close<'a>(
|
||||||
self.play_sound(world, Sound::BlockEnderChestClose).await;
|
&'a self,
|
||||||
|
world: &'a Arc<dyn SimpleWorld>,
|
||||||
|
_position: &'a BlockPos,
|
||||||
|
) -> ViewerFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.play_sound(world, Sound::BlockEnderChestClose).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_viewer_count_update(
|
fn on_viewer_count_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
world: &Arc<dyn SimpleWorld>,
|
world: &'a Arc<dyn SimpleWorld>,
|
||||||
position: &BlockPos,
|
position: &'a BlockPos,
|
||||||
_old: u16,
|
_old: u16,
|
||||||
new: u16,
|
new: u16,
|
||||||
) {
|
) -> ViewerFuture<'a, ()> {
|
||||||
world
|
Box::pin(async move {
|
||||||
.add_synced_block_event(*position, Self::LID_ANIMATION_EVENT_TYPE, new as u8)
|
world
|
||||||
.await
|
.add_synced_block_event(*position, Self::LID_ANIMATION_EVENT_TYPE, new as u8)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
array::from_fn,
|
array::from_fn,
|
||||||
|
pin::Pin,
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
Arc,
|
||||||
atomic::{AtomicBool, AtomicU16, Ordering},
|
atomic::{AtomicBool, AtomicU16, Ordering},
|
||||||
@ -14,6 +15,7 @@ use pumpkin_data::{
|
|||||||
recipe_remainder::get_recipe_remainder_id,
|
recipe_remainder::get_recipe_remainder_id,
|
||||||
recipes::{CookingRecipe, CookingRecipeType, RECIPES_COOKING},
|
recipes::{CookingRecipe, CookingRecipeType, RECIPES_COOKING},
|
||||||
};
|
};
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
@ -156,112 +158,117 @@ impl FurnaceBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for FurnaceBlockEntity {
|
impl BlockEntity for FurnaceBlockEntity {
|
||||||
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
|
fn tick<'a>(
|
||||||
let is_burning = self.is_burning();
|
&'a self,
|
||||||
let mut is_dirty = false;
|
world: Arc<dyn SimpleWorld>,
|
||||||
if self.is_burning() {
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
self.lit_time_remaining.fetch_sub(1, Ordering::Relaxed);
|
Box::pin(async move {
|
||||||
}
|
let is_burning = self.is_burning();
|
||||||
|
let mut is_dirty = false;
|
||||||
|
if self.is_burning() {
|
||||||
|
self.lit_time_remaining.fetch_sub(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
let top_items = self.items[0].lock().await;
|
let top_items = self.items[0].lock().await;
|
||||||
let is_top_items_empty = top_items.is_empty();
|
let is_top_items_empty = top_items.is_empty();
|
||||||
|
|
||||||
let furnace_recipe = Self::get_furnace_cooking_recipe(top_items.item);
|
let furnace_recipe = Self::get_furnace_cooking_recipe(top_items.item);
|
||||||
drop(top_items);
|
drop(top_items);
|
||||||
|
|
||||||
let can_accept_output = self
|
let can_accept_output = self
|
||||||
.can_accept_recipe_output(furnace_recipe, self.get_max_count_per_stack())
|
.can_accept_recipe_output(furnace_recipe, self.get_max_count_per_stack())
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let bottom_items_is_empty = self.items[1].lock().await.is_empty();
|
let bottom_items_is_empty = self.items[1].lock().await.is_empty();
|
||||||
if self.is_burning() || !bottom_items_is_empty && !is_top_items_empty {
|
if self.is_burning() || !bottom_items_is_empty && !is_top_items_empty {
|
||||||
if !self.is_burning() && can_accept_output {
|
if !self.is_burning() && can_accept_output {
|
||||||
let mut bottom_items = self.items[1].lock().await;
|
let mut bottom_items = self.items[1].lock().await;
|
||||||
|
|
||||||
let fuel_ticks = get_item_burn_ticks(bottom_items.item.id).unwrap_or(0);
|
let fuel_ticks = get_item_burn_ticks(bottom_items.item.id).unwrap_or(0);
|
||||||
self.lit_time_remaining.store(fuel_ticks, Ordering::Relaxed);
|
self.lit_time_remaining.store(fuel_ticks, Ordering::Relaxed);
|
||||||
self.lit_total_time.store(fuel_ticks, Ordering::Relaxed);
|
self.lit_total_time.store(fuel_ticks, Ordering::Relaxed);
|
||||||
|
|
||||||
if self.is_burning() {
|
if self.is_burning() {
|
||||||
is_dirty = true;
|
is_dirty = true;
|
||||||
if !bottom_items.is_empty() {
|
if !bottom_items.is_empty() {
|
||||||
bottom_items.decrement(1);
|
bottom_items.decrement(1);
|
||||||
if let Some(remainder_id) = get_recipe_remainder_id(bottom_items.item.id)
|
if let Some(remainder_id) =
|
||||||
&& bottom_items.is_empty()
|
get_recipe_remainder_id(bottom_items.item.id)
|
||||||
&& let Some(remainder_item) = Item::from_id(remainder_id)
|
&& bottom_items.is_empty()
|
||||||
{
|
&& let Some(remainder_item) = Item::from_id(remainder_id)
|
||||||
drop(bottom_items);
|
{
|
||||||
self.set_stack(1, ItemStack::new(1, remainder_item)).await;
|
drop(bottom_items);
|
||||||
|
self.set_stack(1, ItemStack::new(1, remainder_item)).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if self.is_burning() && can_accept_output {
|
if self.is_burning() && can_accept_output {
|
||||||
self.cooking_time_spent.fetch_add(1, Ordering::Relaxed);
|
self.cooking_time_spent.fetch_add(1, Ordering::Relaxed);
|
||||||
|
|
||||||
if self.cooking_time_spent.load(Ordering::Relaxed)
|
if self.cooking_time_spent.load(Ordering::Relaxed)
|
||||||
== self.cooking_total_time.load(Ordering::Relaxed)
|
== self.cooking_total_time.load(Ordering::Relaxed)
|
||||||
{
|
{
|
||||||
self.cooking_time_spent.store(0, Ordering::Relaxed);
|
self.cooking_time_spent.store(0, Ordering::Relaxed);
|
||||||
if let Some(cooking_recipe) = furnace_recipe {
|
if let Some(cooking_recipe) = furnace_recipe {
|
||||||
let cooking_total_time = cooking_recipe.cookingtime;
|
let cooking_total_time = cooking_recipe.cookingtime;
|
||||||
self.cooking_total_time
|
self.cooking_total_time
|
||||||
.store(cooking_total_time as u16, Ordering::Relaxed);
|
.store(cooking_total_time as u16, Ordering::Relaxed);
|
||||||
|
|
||||||
self.craft_recipe(Some(cooking_recipe)).await;
|
self.craft_recipe(Some(cooking_recipe)).await;
|
||||||
is_dirty = true;
|
is_dirty = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
self.cooking_time_spent.store(0, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
} else {
|
} else if !self.is_burning() && self.cooking_time_spent.load(Ordering::Relaxed) > 0 {
|
||||||
self.cooking_time_spent.store(0, Ordering::Relaxed);
|
self.cooking_time_spent
|
||||||
|
.fetch_update(Ordering::Acquire, Ordering::Acquire, |v| {
|
||||||
|
Some(
|
||||||
|
v.saturating_sub(2)
|
||||||
|
.min(self.cooking_total_time.load(Ordering::Acquire)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
} else if !self.is_burning() && self.cooking_time_spent.load(Ordering::Relaxed) > 0 {
|
|
||||||
self.cooking_time_spent
|
|
||||||
.fetch_update(Ordering::Acquire, Ordering::Acquire, |v| {
|
|
||||||
Some(
|
|
||||||
v.saturating_sub(2)
|
|
||||||
.min(self.cooking_total_time.load(Ordering::Acquire)),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_burning != self.is_burning() {
|
if is_burning != self.is_burning() {
|
||||||
is_dirty = true;
|
is_dirty = true;
|
||||||
let world = world.clone();
|
let world = world.clone();
|
||||||
|
|
||||||
let (furnace_block, furnace_block_state) =
|
let (furnace_block, furnace_block_state) =
|
||||||
world.get_block_and_state(&self.position).await;
|
world.get_block_and_state(&self.position).await;
|
||||||
let mut props =
|
let mut props =
|
||||||
FurnaceLikeProperties::from_state_id(furnace_block_state.id, furnace_block);
|
FurnaceLikeProperties::from_state_id(furnace_block_state.id, furnace_block);
|
||||||
|
|
||||||
if self.is_burning() {
|
if self.is_burning() {
|
||||||
props.lit = true;
|
props.lit = true;
|
||||||
world
|
world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
&self.position,
|
&self.position,
|
||||||
props.to_state_id(furnace_block),
|
props.to_state_id(furnace_block),
|
||||||
BlockFlags::NOTIFY_ALL,
|
BlockFlags::NOTIFY_ALL,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
} else {
|
} else {
|
||||||
props.lit = false;
|
props.lit = false;
|
||||||
world
|
world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
&self.position,
|
&self.position,
|
||||||
props.to_state_id(furnace_block),
|
props.to_state_id(furnace_block),
|
||||||
BlockFlags::NOTIFY_ALL,
|
BlockFlags::NOTIFY_ALL,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if is_dirty {
|
if is_dirty {
|
||||||
self.is_dirty();
|
self.is_dirty();
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resource_location(&self) -> &'static str {
|
fn resource_location(&self) -> &'static str {
|
||||||
@ -307,24 +314,29 @@ impl BlockEntity for FurnaceBlockEntity {
|
|||||||
furnace
|
furnace
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
fn write_nbt<'a>(
|
||||||
nbt.put_short(
|
&'a self,
|
||||||
"cooking_total_time",
|
nbt: &'a mut NbtCompound,
|
||||||
self.cooking_total_time.load(Ordering::Relaxed) as i16,
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
);
|
Box::pin(async move {
|
||||||
nbt.put_short(
|
nbt.put_short(
|
||||||
"cooking_time_spent",
|
"cooking_total_time",
|
||||||
self.cooking_time_spent.load(Ordering::Relaxed) as i16,
|
self.cooking_total_time.load(Ordering::Relaxed) as i16,
|
||||||
);
|
);
|
||||||
nbt.put_short(
|
nbt.put_short(
|
||||||
"lit_total_time",
|
"cooking_time_spent",
|
||||||
self.lit_total_time.load(Ordering::Relaxed) as i16,
|
self.cooking_time_spent.load(Ordering::Relaxed) as i16,
|
||||||
);
|
);
|
||||||
nbt.put_short(
|
nbt.put_short(
|
||||||
"lit_time_remaining",
|
"lit_total_time",
|
||||||
self.lit_time_remaining.load(Ordering::Relaxed) as i16,
|
self.lit_total_time.load(Ordering::Relaxed) as i16,
|
||||||
);
|
);
|
||||||
self.write_data(nbt, &self.items, true).await;
|
nbt.put_short(
|
||||||
|
"lit_time_remaining",
|
||||||
|
self.lit_time_remaining.load(Ordering::Relaxed) as i16,
|
||||||
|
);
|
||||||
|
self.write_data(nbt, &self.items, true).await;
|
||||||
|
})
|
||||||
// Safety precaution
|
// Safety precaution
|
||||||
// self.clear().await;
|
// self.clear().await;
|
||||||
}
|
}
|
||||||
@ -425,12 +437,13 @@ impl Inventory for FurnaceBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Clearable for FurnaceBlockEntity {
|
impl Clearable for FurnaceBlockEntity {
|
||||||
async fn clear(&self) {
|
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
|
||||||
for slot in self.items.iter() {
|
Box::pin(async move {
|
||||||
*slot.lock().await = ItemStack::EMPTY.clone();
|
for slot in self.items.iter() {
|
||||||
}
|
*slot.lock().await = ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,11 +7,13 @@ use async_trait::async_trait;
|
|||||||
use pumpkin_data::block_properties::{BlockProperties, HopperFacing, HopperLikeProperties};
|
use pumpkin_data::block_properties::{BlockProperties, HopperFacing, HopperLikeProperties};
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
use pumpkin_data::{Block, tag};
|
use pumpkin_data::{Block, tag};
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_nbt::tag::NbtTag;
|
use pumpkin_nbt::tag::NbtTag;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::array::from_fn;
|
use std::array::from_fn;
|
||||||
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64};
|
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
@ -37,17 +39,21 @@ pub fn to_offset(facing: &HopperFacing) -> Vector3<i32> {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for HopperBlockEntity {
|
impl BlockEntity for HopperBlockEntity {
|
||||||
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
fn write_nbt<'a>(
|
||||||
self.write_data(nbt, &self.items, true).await;
|
&'a self,
|
||||||
nbt.put(
|
nbt: &'a mut NbtCompound,
|
||||||
"TransferCooldown",
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
NbtTag::Int(
|
Box::pin(async move {
|
||||||
self.cooldown_time
|
self.write_data(nbt, &self.items, true).await;
|
||||||
.load(std::sync::atomic::Ordering::Relaxed),
|
nbt.put(
|
||||||
),
|
"TransferCooldown",
|
||||||
);
|
NbtTag::Int(
|
||||||
|
self.cooldown_time
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
})
|
||||||
// Safety precaution
|
// Safety precaution
|
||||||
//self.clear().await;
|
//self.clear().await;
|
||||||
}
|
}
|
||||||
@ -70,24 +76,29 @@ impl BlockEntity for HopperBlockEntity {
|
|||||||
hopper
|
hopper
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
|
fn tick<'a>(
|
||||||
self.ticked_game_time.store(
|
&'a self,
|
||||||
world.get_world_age().await,
|
world: Arc<dyn SimpleWorld>,
|
||||||
std::sync::atomic::Ordering::Relaxed,
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
);
|
Box::pin(async move {
|
||||||
if self
|
self.ticked_game_time.store(
|
||||||
.cooldown_time
|
world.get_world_age().await,
|
||||||
.fetch_sub(1, std::sync::atomic::Ordering::Relaxed)
|
std::sync::atomic::Ordering::Relaxed,
|
||||||
<= 0
|
|
||||||
{
|
|
||||||
self.cooldown_time
|
|
||||||
.store(0, std::sync::atomic::Ordering::Relaxed);
|
|
||||||
let state = HopperLikeProperties::from_state_id(
|
|
||||||
world.get_block_state(&self.position).await.id,
|
|
||||||
&Block::HOPPER,
|
|
||||||
);
|
);
|
||||||
self.try_move_items(&state, &world).await;
|
if self
|
||||||
}
|
.cooldown_time
|
||||||
|
.fetch_sub(1, std::sync::atomic::Ordering::Relaxed)
|
||||||
|
<= 0
|
||||||
|
{
|
||||||
|
self.cooldown_time
|
||||||
|
.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
let state = HopperLikeProperties::from_state_id(
|
||||||
|
world.get_block_state(&self.position).await.id,
|
||||||
|
&Block::HOPPER,
|
||||||
|
);
|
||||||
|
self.try_move_items(&state, &world).await;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resource_location(&self) -> &'static str {
|
fn resource_location(&self) -> &'static str {
|
||||||
@ -327,11 +338,12 @@ impl Inventory for HopperBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Clearable for HopperBlockEntity {
|
impl Clearable for HopperBlockEntity {
|
||||||
async fn clear(&self) {
|
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
|
||||||
for slot in self.items.iter() {
|
Box::pin(async move {
|
||||||
*slot.lock().await = ItemStack::EMPTY.clone();
|
for slot in self.items.iter() {
|
||||||
}
|
*slot.lock().await = ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
use std::sync::{
|
use std::{
|
||||||
Arc,
|
pin::Pin,
|
||||||
atomic::{AtomicI32, Ordering},
|
sync::{
|
||||||
|
Arc,
|
||||||
|
atomic::{AtomicI32, Ordering},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use crossbeam::atomic::AtomicCell;
|
use crossbeam::atomic::AtomicCell;
|
||||||
use pumpkin_data::{entity::EntityType, world::WorldEvent};
|
use pumpkin_data::{entity::EntityType, world::WorldEvent};
|
||||||
use pumpkin_nbt::compound::NbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
@ -67,7 +69,6 @@ impl MobSpawnerBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for MobSpawnerBlockEntity {
|
impl BlockEntity for MobSpawnerBlockEntity {
|
||||||
fn resource_location(&self) -> &'static str {
|
fn resource_location(&self) -> &'static str {
|
||||||
Self::ID
|
Self::ID
|
||||||
@ -77,53 +78,58 @@ impl BlockEntity for MobSpawnerBlockEntity {
|
|||||||
self.position
|
self.position
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
|
fn tick<'a>(
|
||||||
if let Some(entity_type) = &self.entity_type.load() {
|
&'a self,
|
||||||
if self.delay.load(Ordering::Relaxed) == -1 {
|
world: Arc<dyn SimpleWorld>,
|
||||||
self.update_spawns(&world).await;
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
} else {
|
Box::pin(async move {
|
||||||
self.delay.fetch_sub(1, Ordering::Relaxed);
|
if let Some(entity_type) = &self.entity_type.load() {
|
||||||
return;
|
if self.delay.load(Ordering::Relaxed) == -1 {
|
||||||
}
|
self.update_spawns(&world).await;
|
||||||
let spawn_range = self.spawn_range;
|
} else {
|
||||||
let mut update_spawns = false;
|
self.delay.fetch_sub(1, Ordering::Relaxed);
|
||||||
for _ in 0..self.spawn_count {
|
return;
|
||||||
let pos = self.position.0;
|
}
|
||||||
|
let spawn_range = self.spawn_range;
|
||||||
let spawn_pos = Vector3::new(
|
let mut update_spawns = false;
|
||||||
pos.x as f64
|
for _ in 0..self.spawn_count {
|
||||||
+ (rand::random::<f64>() + rand::random::<f64>()) * spawn_range as f64
|
let pos = self.position.0;
|
||||||
+ 0.5,
|
|
||||||
(pos.y + rand::random_range(0..3) - 1) as f64,
|
let spawn_pos = Vector3::new(
|
||||||
pos.z as f64
|
pos.x as f64
|
||||||
+ (rand::random::<f64>() + rand::random::<f64>()) * spawn_range as f64
|
+ (rand::random::<f64>() + rand::random::<f64>()) * spawn_range as f64
|
||||||
+ 0.5,
|
+ 0.5,
|
||||||
);
|
(pos.y + rand::random_range(0..3) - 1) as f64,
|
||||||
// TODO: we should use getSpawnBox, but this is only modified for slimes and magma slimes
|
pos.z as f64
|
||||||
if !world
|
+ (rand::random::<f64>() + rand::random::<f64>()) * spawn_range as f64
|
||||||
.is_space_empty(BoundingBox::new_from_pos(
|
+ 0.5,
|
||||||
spawn_pos.x,
|
);
|
||||||
spawn_pos.y,
|
// TODO: we should use getSpawnBox, but this is only modified for slimes and magma slimes
|
||||||
spawn_pos.z,
|
if !world
|
||||||
&EntityDimensions {
|
.is_space_empty(BoundingBox::new_from_pos(
|
||||||
width: entity_type.dimension[0],
|
spawn_pos.x,
|
||||||
height: entity_type.dimension[1],
|
spawn_pos.y,
|
||||||
},
|
spawn_pos.z,
|
||||||
))
|
&EntityDimensions {
|
||||||
.await
|
width: entity_type.dimension[0],
|
||||||
{
|
height: entity_type.dimension[1],
|
||||||
continue;
|
},
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
world.clone().spawn_from_type(entity_type, spawn_pos).await;
|
||||||
|
world
|
||||||
|
.sync_world_event(WorldEvent::SpawnerSpawnsMob, self.position, 0)
|
||||||
|
.await;
|
||||||
|
update_spawns = true;
|
||||||
|
}
|
||||||
|
if update_spawns {
|
||||||
|
self.update_spawns(&world).await;
|
||||||
}
|
}
|
||||||
world.clone().spawn_from_type(entity_type, spawn_pos).await;
|
|
||||||
world
|
|
||||||
.sync_world_event(WorldEvent::SpawnerSpawnsMob, self.position, 0)
|
|
||||||
.await;
|
|
||||||
update_spawns = true;
|
|
||||||
}
|
}
|
||||||
if update_spawns {
|
})
|
||||||
self.update_spawns(&world).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||||
@ -156,13 +162,18 @@ impl BlockEntity for MobSpawnerBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
fn write_nbt<'a>(
|
||||||
if let Some(entity_type) = self.entity_type.load() {
|
&'a self,
|
||||||
let mut entity_nbt = NbtCompound::new();
|
nbt: &'a mut NbtCompound,
|
||||||
entity_nbt.put_string("id", format!("minecraft:{}", entity_type.resource_name));
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
if let Some(entity_type) = self.entity_type.load() {
|
||||||
|
let mut entity_nbt = NbtCompound::new();
|
||||||
|
entity_nbt.put_string("id", format!("minecraft:{}", entity_type.resource_name));
|
||||||
|
|
||||||
nbt.put_component("entity", entity_nbt);
|
nbt.put_component("entity", entity_nbt);
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chunk_data_nbt(&self) -> Option<NbtCompound> {
|
fn chunk_data_nbt(&self) -> Option<NbtCompound> {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
use std::pin::Pin;
|
||||||
use std::{any::Any, sync::Arc};
|
use std::{any::Any, sync::Arc};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use barrel::BarrelBlockEntity;
|
use barrel::BarrelBlockEntity;
|
||||||
use bed::BedBlockEntity;
|
use bed::BedBlockEntity;
|
||||||
use chest::ChestBlockEntity;
|
use chest::ChestBlockEntity;
|
||||||
@ -39,22 +39,34 @@ pub mod shulker_box;
|
|||||||
pub mod sign;
|
pub mod sign;
|
||||||
|
|
||||||
//TODO: We need a mark_dirty for chests
|
//TODO: We need a mark_dirty for chests
|
||||||
#[async_trait]
|
|
||||||
pub trait BlockEntity: Send + Sync {
|
pub trait BlockEntity: Send + Sync {
|
||||||
async fn write_nbt(&self, nbt: &mut NbtCompound);
|
fn write_nbt<'a>(
|
||||||
|
&'a self,
|
||||||
|
nbt: &'a mut NbtCompound,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
|
||||||
fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self
|
fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
async fn tick(&self, _world: Arc<dyn SimpleWorld>) {}
|
fn tick<'a>(
|
||||||
|
&'a self,
|
||||||
|
_world: Arc<dyn SimpleWorld>,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {})
|
||||||
|
}
|
||||||
fn resource_location(&self) -> &'static str;
|
fn resource_location(&self) -> &'static str;
|
||||||
fn get_position(&self) -> BlockPos;
|
fn get_position(&self) -> BlockPos;
|
||||||
async fn write_internal(&self, nbt: &mut NbtCompound) {
|
fn write_internal<'a>(
|
||||||
nbt.put_string("id", self.resource_location().to_string());
|
&'a self,
|
||||||
let position = self.get_position();
|
nbt: &'a mut NbtCompound,
|
||||||
nbt.put_int("x", position.0.x);
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
nbt.put_int("y", position.0.y);
|
Box::pin(async move {
|
||||||
nbt.put_int("z", position.0.z);
|
nbt.put_string("id", self.resource_location().to_string());
|
||||||
self.write_nbt(nbt).await;
|
let position = self.get_position();
|
||||||
|
nbt.put_int("x", position.0.x);
|
||||||
|
nbt.put_int("y", position.0.y);
|
||||||
|
nbt.put_int("z", position.0.z);
|
||||||
|
self.write_nbt(nbt).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
fn get_id(&self) -> u32 {
|
fn get_id(&self) -> u32 {
|
||||||
pumpkin_data::block_properties::BLOCK_ENTITY_TYPES
|
pumpkin_data::block_properties::BLOCK_ENTITY_TYPES
|
||||||
@ -71,10 +83,20 @@ pub trait BlockEntity: Send + Sync {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn set_block_state(&mut self, _block_state: BlockStateId) {}
|
fn set_block_state(&mut self, _block_state: BlockStateId) {}
|
||||||
async fn on_block_replaced(self: Arc<Self>, world: Arc<dyn SimpleWorld>, position: BlockPos) {
|
fn on_block_replaced<'a>(
|
||||||
if let Some(inventory) = self.get_inventory() {
|
self: Arc<Self>,
|
||||||
world.scatter_inventory(&position, &inventory).await;
|
world: Arc<dyn SimpleWorld>,
|
||||||
}
|
position: BlockPos,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>
|
||||||
|
where
|
||||||
|
Self: 'a,
|
||||||
|
{
|
||||||
|
Box::pin(async move {
|
||||||
|
if let Some(inventory) = self.get_inventory() {
|
||||||
|
// Assuming scatter_inventory is an async method on SimpleWorld
|
||||||
|
world.scatter_inventory(&position, &inventory).await;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
fn is_dirty(&self) -> bool {
|
fn is_dirty(&self) -> bool {
|
||||||
false
|
false
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use std::sync::Arc;
|
use std::{pin::Pin, sync::Arc};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use crossbeam::atomic::AtomicCell;
|
use crossbeam::atomic::AtomicCell;
|
||||||
use pumpkin_data::{Block, BlockDirection, BlockState};
|
use pumpkin_data::{Block, BlockDirection, BlockState};
|
||||||
use pumpkin_nbt::compound::NbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
@ -50,7 +49,6 @@ const LAST_PROGRESS: &str = "progress";
|
|||||||
const EXTENDING: &str = "extending";
|
const EXTENDING: &str = "extending";
|
||||||
const SOURCE: &str = "source";
|
const SOURCE: &str = "source";
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for PistonBlockEntity {
|
impl BlockEntity for PistonBlockEntity {
|
||||||
fn resource_location(&self) -> &'static str {
|
fn resource_location(&self) -> &'static str {
|
||||||
Self::ID
|
Self::ID
|
||||||
@ -60,42 +58,47 @@ impl BlockEntity for PistonBlockEntity {
|
|||||||
self.position
|
self.position
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
|
fn tick<'a>(
|
||||||
let current_progress = self.current_progress.load();
|
&'a self,
|
||||||
self.last_progress.store(current_progress);
|
world: Arc<dyn SimpleWorld>,
|
||||||
if current_progress >= 1.0 {
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
let pos = self.position;
|
Box::pin(async move {
|
||||||
world.remove_block_entity(&pos).await;
|
let current_progress = self.current_progress.load();
|
||||||
if world.get_block(&pos).await == &Block::MOVING_PISTON {
|
self.last_progress.store(current_progress);
|
||||||
if self.pushed_block_state.is_air() {
|
if current_progress >= 1.0 {
|
||||||
world
|
let pos = self.position;
|
||||||
.clone()
|
world.remove_block_entity(&pos).await;
|
||||||
.set_block_state(
|
if world.get_block(&pos).await == &Block::MOVING_PISTON {
|
||||||
&pos,
|
if self.pushed_block_state.is_air() {
|
||||||
self.pushed_block_state.id,
|
world
|
||||||
BlockFlags::FORCE_STATE | BlockFlags::MOVED,
|
.clone()
|
||||||
)
|
.set_block_state(
|
||||||
.await;
|
&pos,
|
||||||
} else {
|
self.pushed_block_state.id,
|
||||||
world
|
BlockFlags::FORCE_STATE | BlockFlags::MOVED,
|
||||||
.clone()
|
)
|
||||||
.set_block_state(
|
.await;
|
||||||
&pos,
|
} else {
|
||||||
self.pushed_block_state.id,
|
world
|
||||||
BlockFlags::NOTIFY_ALL | BlockFlags::MOVED,
|
.clone()
|
||||||
)
|
.set_block_state(
|
||||||
.await;
|
&pos,
|
||||||
world
|
self.pushed_block_state.id,
|
||||||
.clone()
|
BlockFlags::NOTIFY_ALL | BlockFlags::MOVED,
|
||||||
.update_neighbor(&pos, Block::from_state_id(self.pushed_block_state.id))
|
)
|
||||||
.await;
|
.await;
|
||||||
|
world
|
||||||
|
.clone()
|
||||||
|
.update_neighbor(&pos, Block::from_state_id(self.pushed_block_state.id))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
self.current_progress.store(current_progress + 0.5);
|
||||||
self.current_progress.store(current_progress + 0.5);
|
if current_progress + 0.5 >= 1.0 {
|
||||||
if current_progress + 0.5 >= 1.0 {
|
self.current_progress.store(1.0);
|
||||||
self.current_progress.store(1.0);
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||||
@ -119,12 +122,17 @@ impl BlockEntity for PistonBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
fn write_nbt<'a>(
|
||||||
// TODO: pushed_block_state
|
&'a self,
|
||||||
nbt.put_byte(FACING, self.facing.to_index() as i8);
|
nbt: &'a mut NbtCompound,
|
||||||
nbt.put_float(LAST_PROGRESS, self.last_progress.load());
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
nbt.put_bool(EXTENDING, self.extending);
|
Box::pin(async move {
|
||||||
nbt.put_bool(SOURCE, self.source);
|
// TODO: pushed_block_state
|
||||||
|
nbt.put_byte(FACING, self.facing.to_index() as i8);
|
||||||
|
nbt.put_float(LAST_PROGRESS, self.last_progress.load());
|
||||||
|
nbt.put_bool(EXTENDING, self.extending);
|
||||||
|
nbt.put_bool(SOURCE, self.source);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chunk_data_nbt(&self) -> Option<NbtCompound> {
|
fn chunk_data_nbt(&self) -> Option<NbtCompound> {
|
||||||
|
|||||||
@ -1,16 +1,18 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_util::random::xoroshiro128::Xoroshiro;
|
use pumpkin_util::random::xoroshiro128::Xoroshiro;
|
||||||
use pumpkin_util::random::{RandomImpl, get_seed};
|
use pumpkin_util::random::{RandomImpl, get_seed};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::pin::Pin;
|
||||||
use std::{
|
use std::{
|
||||||
array::from_fn,
|
array::from_fn,
|
||||||
sync::{Arc, atomic::AtomicBool},
|
sync::{Arc, atomic::AtomicBool},
|
||||||
};
|
};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker};
|
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture};
|
||||||
use crate::world::SimpleWorld;
|
use crate::world::SimpleWorld;
|
||||||
use crate::{
|
use crate::{
|
||||||
inventory::{
|
inventory::{
|
||||||
@ -31,7 +33,6 @@ pub struct ShulkerBoxBlockEntity {
|
|||||||
viewers: ViewerCountTracker,
|
viewers: ViewerCountTracker,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for ShulkerBoxBlockEntity {
|
impl BlockEntity for ShulkerBoxBlockEntity {
|
||||||
fn resource_location(&self) -> &'static str {
|
fn resource_location(&self) -> &'static str {
|
||||||
Self::ID
|
Self::ID
|
||||||
@ -57,20 +58,39 @@ impl BlockEntity for ShulkerBoxBlockEntity {
|
|||||||
shulker_box
|
shulker_box
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
fn write_nbt<'a>(
|
||||||
self.write_data(nbt, &self.items, true).await;
|
&'a self,
|
||||||
|
nbt: &'a mut NbtCompound,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.write_data(nbt, &self.items, true).await;
|
||||||
|
})
|
||||||
// Safety precaution
|
// Safety precaution
|
||||||
//self.clear().await;
|
//self.clear().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
|
fn tick<'a>(
|
||||||
self.viewers
|
&'a self,
|
||||||
.update_viewer_count::<ShulkerBoxBlockEntity>(self, world, &self.position)
|
world: Arc<dyn SimpleWorld>,
|
||||||
.await;
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.viewers
|
||||||
|
.update_viewer_count::<ShulkerBoxBlockEntity>(self, world, &self.position)
|
||||||
|
.await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_block_replaced(self: Arc<Self>, _world: Arc<dyn SimpleWorld>, _position: BlockPos) {
|
fn on_block_replaced<'a>(
|
||||||
// Do nothing
|
self: Arc<Self>,
|
||||||
|
_world: Arc<dyn SimpleWorld>,
|
||||||
|
_position: BlockPos,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>
|
||||||
|
where
|
||||||
|
Self: 'a,
|
||||||
|
{
|
||||||
|
Box::pin(async move {
|
||||||
|
// Do nothing
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
|
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
|
||||||
@ -86,30 +106,43 @@ impl BlockEntity for ShulkerBoxBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ViewerCountListener for ShulkerBoxBlockEntity {
|
impl ViewerCountListener for ShulkerBoxBlockEntity {
|
||||||
async fn on_container_open(&self, world: &Arc<dyn SimpleWorld>, position: &BlockPos) {
|
fn on_container_open<'a>(
|
||||||
self.play_sound(world, position, Sound::BlockShulkerBoxOpen)
|
&'a self,
|
||||||
.await;
|
world: &'a Arc<dyn SimpleWorld>,
|
||||||
// TODO: this.world.emitGameEvent(player, GameEvent.CONTAINER_OPEN, this.pos);
|
position: &'a BlockPos,
|
||||||
|
) -> ViewerFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.play_sound(world, position, Sound::BlockShulkerBoxOpen)
|
||||||
|
.await;
|
||||||
|
// TODO: this.world.emitGameEvent(player, GameEvent.CONTAINER_OPEN, this.pos);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_container_close(&self, world: &Arc<dyn SimpleWorld>, position: &BlockPos) {
|
fn on_container_close<'a>(
|
||||||
self.play_sound(world, position, Sound::BlockShulkerBoxClose)
|
&'a self,
|
||||||
.await;
|
world: &'a Arc<dyn SimpleWorld>,
|
||||||
// TODO: this.world.emitGameEvent(player, GameEvent.CONTAINER_CLOSE, this.pos);
|
position: &'a BlockPos,
|
||||||
|
) -> ViewerFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.play_sound(world, position, Sound::BlockShulkerBoxClose)
|
||||||
|
.await;
|
||||||
|
// TODO: this.world.emitGameEvent(player, GameEvent.CONTAINER_CLOSE, this.pos);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_viewer_count_update(
|
fn on_viewer_count_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
world: &Arc<dyn SimpleWorld>,
|
world: &'a Arc<dyn SimpleWorld>,
|
||||||
position: &BlockPos,
|
position: &'a BlockPos,
|
||||||
_old: u16,
|
_old: u16,
|
||||||
new: u16,
|
new: u16,
|
||||||
) {
|
) -> ViewerFuture<'a, ()> {
|
||||||
world
|
Box::pin(async move {
|
||||||
.add_synced_block_event(*position, Self::OPEN_ANIMATION_EVENT_TYPE, new as u8)
|
world
|
||||||
.await
|
.add_synced_block_event(*position, Self::OPEN_ANIMATION_EVENT_TYPE, new as u8)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,11 +227,12 @@ impl Inventory for ShulkerBoxBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Clearable for ShulkerBoxBlockEntity {
|
impl Clearable for ShulkerBoxBlockEntity {
|
||||||
async fn clear(&self) {
|
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
|
||||||
for slot in self.items.iter() {
|
Box::pin(async move {
|
||||||
*slot.lock().await = ItemStack::EMPTY.clone();
|
for slot in self.items.iter() {
|
||||||
}
|
*slot.lock().await = ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
use std::sync::{
|
use std::{
|
||||||
Arc,
|
pin::Pin,
|
||||||
atomic::{AtomicBool, AtomicI8, Ordering},
|
sync::{
|
||||||
|
Arc,
|
||||||
|
atomic::{AtomicBool, AtomicI8, Ordering},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::BlockEntity;
|
use super::BlockEntity;
|
||||||
use async_trait::async_trait;
|
|
||||||
use num_derive::FromPrimitive;
|
use num_derive::FromPrimitive;
|
||||||
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
|
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
@ -208,7 +210,6 @@ impl Text {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockEntity for SignBlockEntity {
|
impl BlockEntity for SignBlockEntity {
|
||||||
fn resource_location(&self) -> &'static str {
|
fn resource_location(&self) -> &'static str {
|
||||||
Self::ID
|
Self::ID
|
||||||
@ -234,10 +235,15 @@ impl BlockEntity for SignBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
fn write_nbt<'a>(
|
||||||
nbt.put("front_text", self.front_text.clone());
|
&'a self,
|
||||||
nbt.put("back_text", self.back_text.clone());
|
nbt: &'a mut NbtCompound,
|
||||||
nbt.put_bool("is_waxed", self.is_waxed.load(Ordering::Relaxed));
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
nbt.put("front_text", self.front_text.clone());
|
||||||
|
nbt.put("back_text", self.back_text.clone());
|
||||||
|
nbt.put_bool("is_waxed", self.is_waxed.load(Ordering::Relaxed));
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chunk_data_nbt(&self) -> Option<NbtCompound> {
|
fn chunk_data_nbt(&self) -> Option<NbtCompound> {
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
use std::sync::{
|
use std::{
|
||||||
Arc,
|
pin::Pin,
|
||||||
atomic::{AtomicU16, Ordering},
|
sync::{
|
||||||
|
Arc,
|
||||||
|
atomic::{AtomicU16, Ordering},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
|
|
||||||
use crate::{block::entities::BlockEntity, world::SimpleWorld};
|
use crate::{block::entities::BlockEntity, world::SimpleWorld};
|
||||||
@ -70,16 +72,32 @@ impl ViewerCountTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
pub type ViewerFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
|
||||||
pub trait ViewerCountListener {
|
|
||||||
async fn on_container_open(&self, _world: &Arc<dyn SimpleWorld>, _position: &BlockPos) {}
|
pub trait ViewerCountListener: Send + Sync {
|
||||||
async fn on_container_close(&self, _world: &Arc<dyn SimpleWorld>, _position: &BlockPos) {}
|
fn on_container_open<'a>(
|
||||||
async fn on_viewer_count_update(
|
&'a self,
|
||||||
&self,
|
_world: &'a Arc<dyn SimpleWorld>,
|
||||||
_world: &Arc<dyn SimpleWorld>,
|
_position: &'a BlockPos,
|
||||||
_position: &BlockPos,
|
) -> ViewerFuture<'a, ()> {
|
||||||
|
Box::pin(async {})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_container_close<'a>(
|
||||||
|
&'a self,
|
||||||
|
_world: &'a Arc<dyn SimpleWorld>,
|
||||||
|
_position: &'a BlockPos,
|
||||||
|
) -> ViewerFuture<'a, ()> {
|
||||||
|
Box::pin(async {})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_viewer_count_update<'a>(
|
||||||
|
&'a self,
|
||||||
|
_world: &'a Arc<dyn SimpleWorld>,
|
||||||
|
_position: &'a BlockPos,
|
||||||
_old: u16,
|
_old: u16,
|
||||||
_new: u16,
|
_new: u16,
|
||||||
) {
|
) -> ViewerFuture<'a, ()> {
|
||||||
|
Box::pin(async {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ use std::{
|
|||||||
io::{Read, SeekFrom, Write},
|
io::{Read, SeekFrom, Write},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
pin::Pin,
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
@ -517,9 +518,10 @@ impl<S: SingleChunkDataSerializer> Default for AnvilChunkFile<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait SingleChunkDataSerializer: Send + Sync + Sized + Dirtiable {
|
pub trait SingleChunkDataSerializer: Send + Sync + Sized + Dirtiable {
|
||||||
async fn to_bytes(&self) -> Result<Bytes, ChunkSerializingError>;
|
fn to_bytes(
|
||||||
|
&self,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Result<Bytes, ChunkSerializingError>> + Send + '_>>;
|
||||||
fn from_bytes(bytes: Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError>;
|
fn from_bytes(bytes: Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError>;
|
||||||
fn position(&self) -> &Vector2<i32>;
|
fn position(&self) -> &Vector2<i32>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use std::{collections::HashMap, io::Cursor, path::PathBuf};
|
use std::{collections::HashMap, io::Cursor, path::PathBuf, pin::Pin};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use pumpkin_data::{Block, chunk::ChunkStatus, fluid::Fluid};
|
use pumpkin_data::{Block, chunk::ChunkStatus, fluid::Fluid};
|
||||||
@ -30,7 +29,6 @@ use crate::block::BlockStateCodec;
|
|||||||
pub mod anvil;
|
pub mod anvil;
|
||||||
pub mod linear;
|
pub mod linear;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl SingleChunkDataSerializer for ChunkData {
|
impl SingleChunkDataSerializer for ChunkData {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_bytes(bytes: Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError> {
|
fn from_bytes(bytes: Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError> {
|
||||||
@ -38,8 +36,10 @@ impl SingleChunkDataSerializer for ChunkData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
async fn to_bytes(&self) -> Result<Bytes, ChunkSerializingError> {
|
fn to_bytes(
|
||||||
self.internal_to_bytes().await
|
&self,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Result<Bytes, ChunkSerializingError>> + Send + '_>> {
|
||||||
|
Box::pin(async move { self.internal_to_bytes().await })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -247,7 +247,6 @@ impl Dirtiable for ChunkEntityData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl SingleChunkDataSerializer for ChunkEntityData {
|
impl SingleChunkDataSerializer for ChunkEntityData {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_bytes(bytes: Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError> {
|
fn from_bytes(bytes: Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError> {
|
||||||
@ -255,8 +254,10 @@ impl SingleChunkDataSerializer for ChunkEntityData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
async fn to_bytes(&self) -> Result<Bytes, ChunkSerializingError> {
|
fn to_bytes(
|
||||||
self.internal_to_bytes()
|
&self,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Result<Bytes, ChunkSerializingError>> + Send + '_>> {
|
||||||
|
Box::pin(async move { self.internal_to_bytes() })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@ -11,6 +11,7 @@ use crate::chunk::io::LoadedData::Loaded;
|
|||||||
use crate::chunk::{ChunkData, ChunkHeightmapType, ChunkLight, ChunkSections, SubChunk};
|
use crate::chunk::{ChunkData, ChunkHeightmapType, ChunkLight, ChunkSections, SubChunk};
|
||||||
use crate::dimension::Dimension;
|
use crate::dimension::Dimension;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::generation::height_limit::HeightLimitView;
|
use crate::generation::height_limit::HeightLimitView;
|
||||||
|
|
||||||
@ -19,7 +20,6 @@ use crate::generation::settings::{GenerationSettings, gen_settings_from_dimensio
|
|||||||
use crate::level::{Level, SyncChunk};
|
use crate::level::{Level, SyncChunk};
|
||||||
use crate::world::{BlockAccessor, BlockRegistryExt};
|
use crate::world::{BlockAccessor, BlockRegistryExt};
|
||||||
use crate::{GlobalRandomConfig, ProtoChunk, ProtoNoiseRouters};
|
use crate::{GlobalRandomConfig, ProtoChunk, ProtoNoiseRouters};
|
||||||
use async_trait::async_trait;
|
|
||||||
use crossbeam::channel::{Receiver, Sender};
|
use crossbeam::channel::{Receiver, Sender};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
@ -849,22 +849,29 @@ impl HeightLimitView for Cache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockAccessor for Cache {
|
impl BlockAccessor for Cache {
|
||||||
async fn get_block(&self, position: &BlockPos) -> &'static Block {
|
fn get_block<'a>(
|
||||||
GenerationCache::get_block_state(self, &position.0).to_block()
|
&'a self,
|
||||||
|
position: &'a BlockPos,
|
||||||
|
) -> Pin<Box<dyn Future<Output = &'static Block> + Send + 'a>> {
|
||||||
|
Box::pin(async move { GenerationCache::get_block_state(self, &position.0).to_block() })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_block_state(&self, position: &BlockPos) -> &'static BlockState {
|
fn get_block_state<'a>(
|
||||||
GenerationCache::get_block_state(self, &position.0).to_state()
|
&'a self,
|
||||||
|
position: &'a BlockPos,
|
||||||
|
) -> Pin<Box<dyn Future<Output = &'static BlockState> + Send + 'a>> {
|
||||||
|
Box::pin(async move { GenerationCache::get_block_state(self, &position.0).to_state() })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_block_and_state(
|
fn get_block_and_state<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
position: &BlockPos,
|
position: &'a BlockPos,
|
||||||
) -> (&'static Block, &'static BlockState) {
|
) -> Pin<Box<dyn Future<Output = (&'static Block, &'static BlockState)> + Send + 'a>> {
|
||||||
let id = GenerationCache::get_block_state(self, &position.0);
|
Box::pin(async move {
|
||||||
(id.to_block(), id.to_state())
|
let id = GenerationCache::get_block_state(self, &position.0);
|
||||||
|
(id.to_block(), id.to_state())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::{Block, BlockDirection};
|
use pumpkin_data::{Block, BlockDirection};
|
||||||
use pumpkin_util::{HeightMap, include_json_static};
|
use pumpkin_util::{HeightMap, include_json_static};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@ -378,7 +377,6 @@ pub struct BlockFilterPlacementModifier {
|
|||||||
predicate: BlockPredicate,
|
predicate: BlockPredicate,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ConditionalPlacementModifier for BlockFilterPlacementModifier {
|
impl ConditionalPlacementModifier for BlockFilterPlacementModifier {
|
||||||
fn should_place<T: GenerationCache>(
|
fn should_place<T: GenerationCache>(
|
||||||
&self,
|
&self,
|
||||||
@ -399,7 +397,6 @@ pub struct SurfaceThresholdFilterPlacementModifier {
|
|||||||
max_inclusive: Option<i32>,
|
max_inclusive: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ConditionalPlacementModifier for SurfaceThresholdFilterPlacementModifier {
|
impl ConditionalPlacementModifier for SurfaceThresholdFilterPlacementModifier {
|
||||||
fn should_place<T: GenerationCache>(
|
fn should_place<T: GenerationCache>(
|
||||||
&self,
|
&self,
|
||||||
@ -421,7 +418,6 @@ pub struct RarityFilterPlacementModifier {
|
|||||||
chance: u32,
|
chance: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ConditionalPlacementModifier for RarityFilterPlacementModifier {
|
impl ConditionalPlacementModifier for RarityFilterPlacementModifier {
|
||||||
fn should_place<T: GenerationCache>(
|
fn should_place<T: GenerationCache>(
|
||||||
&self,
|
&self,
|
||||||
@ -465,7 +461,6 @@ pub struct SurfaceWaterDepthFilterPlacementModifier {
|
|||||||
max_water_depth: i32,
|
max_water_depth: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ConditionalPlacementModifier for SurfaceWaterDepthFilterPlacementModifier {
|
impl ConditionalPlacementModifier for SurfaceWaterDepthFilterPlacementModifier {
|
||||||
fn should_place<T: GenerationCache>(
|
fn should_place<T: GenerationCache>(
|
||||||
&self,
|
&self,
|
||||||
@ -484,7 +479,6 @@ impl ConditionalPlacementModifier for SurfaceWaterDepthFilterPlacementModifier {
|
|||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct BiomePlacementModifier;
|
pub struct BiomePlacementModifier;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ConditionalPlacementModifier for BiomePlacementModifier {
|
impl ConditionalPlacementModifier for BiomePlacementModifier {
|
||||||
fn should_place<T: GenerationCache>(
|
fn should_place<T: GenerationCache>(
|
||||||
&self,
|
&self,
|
||||||
@ -563,7 +557,6 @@ pub trait CountPlacementModifierBase {
|
|||||||
fn get_count(&self, random: &mut RandomGenerator, pos: BlockPos) -> i32;
|
fn get_count(&self, random: &mut RandomGenerator, pos: BlockPos) -> i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait ConditionalPlacementModifier {
|
pub trait ConditionalPlacementModifier {
|
||||||
fn get_positions<T: GenerationCache>(
|
fn get_positions<T: GenerationCache>(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::tag;
|
use pumpkin_data::tag;
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockState, block_properties::blocks_movement, chunk::Biome, tag::Taggable,
|
Block, BlockState, block_properties::blocks_movement, chunk::Biome, tag::Taggable,
|
||||||
@ -988,22 +988,29 @@ impl ProtoChunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockAccessor for ProtoChunk {
|
impl BlockAccessor for ProtoChunk {
|
||||||
async fn get_block(&self, position: &BlockPos) -> &'static pumpkin_data::Block {
|
fn get_block<'a>(
|
||||||
self.get_block_state(&position.0).to_block()
|
&'a self,
|
||||||
|
position: &'a BlockPos,
|
||||||
|
) -> Pin<Box<dyn Future<Output = &'static Block> + Send + 'a>> {
|
||||||
|
Box::pin(async move { self.get_block_state(&position.0).to_block() })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_block_state(&self, position: &BlockPos) -> &'static pumpkin_data::BlockState {
|
fn get_block_state<'a>(
|
||||||
self.get_block_state(&position.0).to_state()
|
&'a self,
|
||||||
|
position: &'a BlockPos,
|
||||||
|
) -> Pin<Box<dyn Future<Output = &'static BlockState> + Send + 'a>> {
|
||||||
|
Box::pin(async move { self.get_block_state(&position.0).to_state() })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_block_and_state(
|
fn get_block_and_state<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
position: &BlockPos,
|
position: &'a BlockPos,
|
||||||
) -> (&'static Block, &'static BlockState) {
|
) -> Pin<Box<dyn Future<Output = (&'static Block, &'static BlockState)> + Send + 'a>> {
|
||||||
let id = self.get_block_state(&position.0);
|
Box::pin(async move {
|
||||||
BlockState::from_id_with_block(id.0)
|
let id = self.get_block_state(&position.0);
|
||||||
|
BlockState::from_id_with_block(id.0)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use async_trait::async_trait;
|
|||||||
use pumpkin_data::item::Item;
|
use pumpkin_data::item::Item;
|
||||||
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
|
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::pin::Pin;
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
@ -139,9 +140,8 @@ pub trait Inventory: Send + Sync + Debug + Clearable {
|
|||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait Clearable {
|
pub trait Clearable {
|
||||||
async fn clear(&self);
|
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ComparableInventory(pub Arc<dyn Inventory>);
|
pub struct ComparableInventory(pub Arc<dyn Inventory>);
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::block::entities::BlockEntity;
|
use crate::block::entities::BlockEntity;
|
||||||
use crate::{BlockStateId, inventory::Inventory};
|
use crate::{BlockStateId, inventory::Inventory};
|
||||||
use async_trait::async_trait;
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use pumpkin_data::entity::EntityType;
|
use pumpkin_data::entity::EntityType;
|
||||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||||
@ -40,59 +40,81 @@ impl std::fmt::Display for GetBlockError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
pub type WorldFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
|
||||||
|
|
||||||
pub trait SimpleWorld: BlockAccessor + Send + Sync {
|
pub trait SimpleWorld: BlockAccessor + Send + Sync {
|
||||||
async fn set_block_state(
|
fn set_block_state<'a>(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
position: &BlockPos,
|
position: &'a BlockPos,
|
||||||
block_state_id: BlockStateId,
|
block_state_id: BlockStateId,
|
||||||
flags: BlockFlags,
|
flags: BlockFlags,
|
||||||
) -> BlockStateId;
|
) -> WorldFuture<'a, BlockStateId>;
|
||||||
|
|
||||||
async fn update_neighbor(
|
fn update_neighbor<'a>(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
neighbor_block_pos: &BlockPos,
|
neighbor_block_pos: &'a BlockPos,
|
||||||
source_block: &pumpkin_data::Block,
|
source_block: &'a pumpkin_data::Block,
|
||||||
);
|
) -> WorldFuture<'a, ()>;
|
||||||
|
|
||||||
async fn update_neighbors(
|
fn update_neighbors<'a>(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
block_pos: &BlockPos,
|
block_pos: &'a BlockPos,
|
||||||
except: Option<BlockDirection>,
|
except: Option<BlockDirection>,
|
||||||
);
|
) -> WorldFuture<'a, ()>;
|
||||||
|
|
||||||
async fn is_space_empty(&self, bounding_box: BoundingBox) -> bool;
|
fn is_space_empty<'a>(&'a self, bounding_box: BoundingBox) -> WorldFuture<'a, bool>;
|
||||||
|
|
||||||
async fn spawn_from_type(
|
fn spawn_from_type(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
entity_type: &'static EntityType,
|
entity_type: &'static EntityType,
|
||||||
position: Vector3<f64>,
|
position: Vector3<f64>,
|
||||||
);
|
) -> WorldFuture<'static, ()>;
|
||||||
|
|
||||||
async fn add_synced_block_event(&self, pos: BlockPos, r#type: u8, data: u8);
|
fn add_synced_block_event<'a>(
|
||||||
|
&'a self,
|
||||||
|
pos: BlockPos,
|
||||||
|
r#type: u8,
|
||||||
|
data: u8,
|
||||||
|
) -> WorldFuture<'a, ()>;
|
||||||
|
|
||||||
async fn sync_world_event(&self, world_event: WorldEvent, position: BlockPos, data: i32);
|
fn sync_world_event<'a>(
|
||||||
|
&'a self,
|
||||||
|
world_event: WorldEvent,
|
||||||
|
position: BlockPos,
|
||||||
|
data: i32,
|
||||||
|
) -> WorldFuture<'a, ()>;
|
||||||
|
|
||||||
async fn remove_block_entity(&self, block_pos: &BlockPos);
|
fn remove_block_entity<'a>(&'a self, block_pos: &'a BlockPos) -> WorldFuture<'a, ()>;
|
||||||
async fn get_block_entity(&self, block_pos: &BlockPos) -> Option<Arc<dyn BlockEntity>>;
|
|
||||||
async fn get_world_age(&self) -> i64;
|
|
||||||
|
|
||||||
async fn play_sound(&self, sound: Sound, category: SoundCategory, position: &Vector3<f64>);
|
fn get_block_entity<'a>(
|
||||||
async fn play_sound_fine(
|
&'a self,
|
||||||
&self,
|
block_pos: &'a BlockPos,
|
||||||
|
) -> WorldFuture<'a, Option<Arc<dyn BlockEntity>>>;
|
||||||
|
|
||||||
|
fn get_world_age<'a>(&'a self) -> WorldFuture<'a, i64>;
|
||||||
|
|
||||||
|
fn play_sound<'a>(
|
||||||
|
&'a self,
|
||||||
sound: Sound,
|
sound: Sound,
|
||||||
category: SoundCategory,
|
category: SoundCategory,
|
||||||
position: &Vector3<f64>,
|
position: &'a Vector3<f64>,
|
||||||
|
) -> WorldFuture<'a, ()>;
|
||||||
|
|
||||||
|
fn play_sound_fine<'a>(
|
||||||
|
&'a self,
|
||||||
|
sound: Sound,
|
||||||
|
category: SoundCategory,
|
||||||
|
position: &'a Vector3<f64>,
|
||||||
volume: f32,
|
volume: f32,
|
||||||
pitch: f32,
|
pitch: f32,
|
||||||
);
|
) -> WorldFuture<'a, ()>;
|
||||||
|
|
||||||
/* ItemScatterer */
|
/* ItemScatterer */
|
||||||
async fn scatter_inventory(
|
fn scatter_inventory<'a>(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
position: &BlockPos,
|
position: &'a BlockPos,
|
||||||
inventory: &Arc<dyn Inventory>,
|
inventory: &'a Arc<dyn Inventory>,
|
||||||
);
|
) -> WorldFuture<'a, ()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait BlockRegistryExt: Send + Sync {
|
pub trait BlockRegistryExt: Send + Sync {
|
||||||
@ -105,14 +127,19 @@ pub trait BlockRegistryExt: Send + Sync {
|
|||||||
) -> bool;
|
) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait BlockAccessor: Send + Sync {
|
pub trait BlockAccessor: Send + Sync {
|
||||||
async fn get_block(&self, position: &BlockPos) -> &'static Block;
|
fn get_block<'a>(
|
||||||
|
&'a self,
|
||||||
|
position: &'a BlockPos,
|
||||||
|
) -> Pin<Box<dyn Future<Output = &'static Block> + Send + 'a>>;
|
||||||
|
|
||||||
async fn get_block_state(&self, position: &BlockPos) -> &'static BlockState;
|
fn get_block_state<'a>(
|
||||||
|
&'a self,
|
||||||
|
position: &'a BlockPos,
|
||||||
|
) -> Pin<Box<dyn Future<Output = &'static BlockState> + Send + 'a>>;
|
||||||
|
|
||||||
async fn get_block_and_state(
|
fn get_block_and_state<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
position: &BlockPos,
|
position: &'a BlockPos,
|
||||||
) -> (&'static Block, &'static BlockState);
|
) -> Pin<Box<dyn Future<Output = (&'static Block, &'static BlockState)> + Send + 'a>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockDirection,
|
Block, BlockDirection,
|
||||||
block_properties::{BlockFace, HorizontalFacing},
|
block_properties::{BlockFace, HorizontalFacing},
|
||||||
@ -6,10 +5,12 @@ use pumpkin_data::{
|
|||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_world::{BlockStateId, world::BlockAccessor};
|
use pumpkin_world::{BlockStateId, world::BlockAccessor};
|
||||||
|
|
||||||
use crate::{block::GetStateForNeighborUpdateArgs, entity::player::Player};
|
use crate::{
|
||||||
|
block::{BlockFuture, GetStateForNeighborUpdateArgs},
|
||||||
|
entity::player::Player,
|
||||||
|
};
|
||||||
|
|
||||||
#[async_trait]
|
pub trait WallMountedBlock: Send + Sync {
|
||||||
pub trait WallMountedBlock {
|
|
||||||
fn get_direction(&self, state_id: BlockStateId, block: &Block) -> BlockDirection;
|
fn get_direction(&self, state_id: BlockStateId, block: &Block) -> BlockDirection;
|
||||||
|
|
||||||
fn get_placement_face(
|
fn get_placement_face(
|
||||||
@ -32,29 +33,33 @@ pub trait WallMountedBlock {
|
|||||||
(face, facing)
|
(face, facing)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn can_place_at(
|
fn can_place_at<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
world: &dyn BlockAccessor,
|
world: &'a dyn BlockAccessor,
|
||||||
pos: &BlockPos,
|
pos: &'a BlockPos,
|
||||||
direction: BlockDirection,
|
direction: BlockDirection,
|
||||||
) -> bool {
|
) -> BlockFuture<'a, bool> {
|
||||||
let block_pos = pos.offset(direction.to_offset());
|
Box::pin(async move {
|
||||||
let block_state = world.get_block_state(&block_pos).await;
|
let block_pos = pos.offset(direction.to_offset());
|
||||||
block_state.is_side_solid(direction.opposite())
|
let block_state = world.get_block_state(&block_pos).await;
|
||||||
|
block_state.is_side_solid(direction.opposite())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
if self.get_direction(args.state_id, args.block).opposite() == args.direction
|
Box::pin(async move {
|
||||||
&& !self
|
if self.get_direction(args.state_id, args.block).opposite() == args.direction
|
||||||
.can_place_at(args.world, args.position, args.direction)
|
&& !self
|
||||||
.await
|
.can_place_at(args.world, args.position, args.direction)
|
||||||
{
|
.await
|
||||||
Block::AIR.default_state.id
|
{
|
||||||
} else {
|
Block::AIR.default_state.id
|
||||||
args.state_id
|
} else {
|
||||||
}
|
args.state_id
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
use crate::block::blocks::falling::FallingBlock;
|
use crate::block::blocks::falling::FallingBlock;
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs,
|
BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs,
|
||||||
|
PlacedArgs,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties};
|
use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties};
|
||||||
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
||||||
use pumpkin_macros::pumpkin_block_from_tag;
|
use pumpkin_macros::pumpkin_block_from_tag;
|
||||||
@ -11,33 +11,40 @@ use pumpkin_world::BlockStateId;
|
|||||||
#[pumpkin_block_from_tag("minecraft:anvil")]
|
#[pumpkin_block_from_tag("minecraft:anvil")]
|
||||||
pub struct AnvilBlock;
|
pub struct AnvilBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for AnvilBlock {
|
impl BlockBehaviour for AnvilBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let dir = args
|
Box::pin(async move {
|
||||||
.player
|
let dir = args
|
||||||
.living_entity
|
.player
|
||||||
.entity
|
.living_entity
|
||||||
.get_horizontal_facing()
|
.entity
|
||||||
.rotate_clockwise();
|
.get_horizontal_facing()
|
||||||
|
.rotate_clockwise();
|
||||||
|
|
||||||
let mut props = WallTorchLikeProperties::default(args.block);
|
let mut props = WallTorchLikeProperties::default(args.block);
|
||||||
|
|
||||||
props.facing = dir;
|
props.facing = dir;
|
||||||
props.to_state_id(args.block)
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn placed(&self, args: PlacedArgs<'_>) {
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
FallingBlock::placed(&FallingBlock, args).await;
|
Box::pin(async move {
|
||||||
|
FallingBlock::placed(&FallingBlock, args).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
FallingBlock::get_state_for_neighbor_update(&FallingBlock, args).await
|
Box::pin(
|
||||||
|
async move { FallingBlock::get_state_for_neighbor_update(&FallingBlock, args).await },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
FallingBlock::on_scheduled_tick(&FallingBlock, args).await;
|
Box::pin(async move {
|
||||||
|
FallingBlock::on_scheduled_tick(&FallingBlock, args).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::tag;
|
use pumpkin_data::tag;
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
|
|
||||||
use crate::block::{BlockBehaviour, CanPlaceAtArgs};
|
use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs};
|
||||||
|
|
||||||
#[pumpkin_block("minecraft:bamboo")]
|
#[pumpkin_block("minecraft:bamboo")]
|
||||||
pub struct BambooBlock;
|
pub struct BambooBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for BambooBlock {
|
impl BlockBehaviour for BambooBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
let block_below = args.block_accessor.get_block(&args.position.down()).await;
|
Box::pin(async move {
|
||||||
block_below.has_tag(&tag::Block::MINECRAFT_BAMBOO_PLANTABLE_ON)
|
let block_below = args.block_accessor.get_block(&args.position.down()).await;
|
||||||
|
block_below.has_tag(&tag::Block::MINECRAFT_BAMBOO_PLANTABLE_ON)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use crate::block::{BlockBehaviour, OnPlaceArgs};
|
use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs};
|
||||||
use crate::entity::EntityBase;
|
use crate::entity::EntityBase;
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::{BlockProperties, WhiteBannerLikeProperties};
|
use pumpkin_data::block_properties::{BlockProperties, WhiteBannerLikeProperties};
|
||||||
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
||||||
use pumpkin_macros::pumpkin_block_from_tag;
|
use pumpkin_macros::pumpkin_block_from_tag;
|
||||||
@ -9,11 +8,12 @@ use pumpkin_world::BlockStateId;
|
|||||||
#[pumpkin_block_from_tag("minecraft:banners")]
|
#[pumpkin_block_from_tag("minecraft:banners")]
|
||||||
pub struct BannerBlock;
|
pub struct BannerBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for BannerBlock {
|
impl BlockBehaviour for BannerBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut props = WhiteBannerLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
props.rotation = args.player.get_entity().get_flipped_rotation_16();
|
let mut props = WhiteBannerLikeProperties::default(args.block);
|
||||||
props.to_state_id(args.block)
|
props.rotation = args.player.get_entity().get_flipped_rotation_16();
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::block::{OnPlaceArgs, PlacedArgs};
|
use crate::block::{BlockFuture, OnPlaceArgs, PlacedArgs};
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
registry::BlockActionResult,
|
registry::BlockActionResult,
|
||||||
{BlockBehaviour, NormalUseArgs},
|
{BlockBehaviour, NormalUseArgs},
|
||||||
@ -40,30 +40,35 @@ impl ScreenHandlerFactory for BarrelScreenFactory {
|
|||||||
#[pumpkin_block("minecraft:barrel")]
|
#[pumpkin_block("minecraft:barrel")]
|
||||||
pub struct BarrelBlock;
|
pub struct BarrelBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for BarrelBlock {
|
impl BlockBehaviour for BarrelBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut props = BarrelLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
props.facing = args.player.living_entity.entity.get_facing().opposite();
|
let mut props = BarrelLikeProperties::default(args.block);
|
||||||
props.to_state_id(args.block)
|
props.facing = args.player.living_entity.entity.get_facing().opposite();
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
Box::pin(async move {
|
||||||
&& let Some(inventory) = block_entity.get_inventory()
|
if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
||||||
{
|
&& let Some(inventory) = block_entity.get_inventory()
|
||||||
args.player
|
{
|
||||||
.open_handled_screen(&BarrelScreenFactory(inventory))
|
args.player
|
||||||
|
.open_handled_screen(&BarrelScreenFactory(inventory))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockActionResult::Success
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let barrel_block_entity = BarrelBlockEntity::new(*args.position);
|
||||||
|
args.world
|
||||||
|
.add_block_entity(Arc::new(barrel_block_entity))
|
||||||
.await;
|
.await;
|
||||||
}
|
})
|
||||||
|
|
||||||
BlockActionResult::Success
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn placed(&self, args: PlacedArgs<'_>) {
|
|
||||||
let barrel_block_entity = BarrelBlockEntity::new(*args.position);
|
|
||||||
args.world
|
|
||||||
.add_block_entity(Arc::new(barrel_block_entity))
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
use crate::block::{BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs};
|
use crate::block::{BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::{
|
use pumpkin_data::block_properties::{
|
||||||
BlockProperties, MangroveRootsLikeProperties as BarrierLikeProperties,
|
BlockProperties, MangroveRootsLikeProperties as BarrierLikeProperties,
|
||||||
};
|
};
|
||||||
@ -11,29 +10,32 @@ use pumpkin_world::tick::TickPriority;
|
|||||||
#[pumpkin_block("minecraft:barrier")]
|
#[pumpkin_block("minecraft:barrier")]
|
||||||
pub struct BarrierBlock;
|
pub struct BarrierBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for BarrierBlock {
|
impl BlockBehaviour for BarrierBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut props = BarrierLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
props.waterlogged = args.replacing.water_source();
|
let mut props = BarrierLikeProperties::default(args.block);
|
||||||
props.to_state_id(args.block)
|
props.waterlogged = args.replacing.water_source();
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
let props = BarrierLikeProperties::from_state_id(args.state_id, args.block);
|
Box::pin(async move {
|
||||||
if props.waterlogged {
|
let props = BarrierLikeProperties::from_state_id(args.state_id, args.block);
|
||||||
args.world
|
if props.waterlogged {
|
||||||
.schedule_fluid_tick(
|
args.world
|
||||||
&Fluid::WATER,
|
.schedule_fluid_tick(
|
||||||
*args.position,
|
&Fluid::WATER,
|
||||||
Fluid::WATER.flow_speed as u8,
|
*args.position,
|
||||||
TickPriority::Normal,
|
Fluid::WATER.flow_speed as u8,
|
||||||
)
|
TickPriority::Normal,
|
||||||
.await;
|
)
|
||||||
}
|
.await;
|
||||||
props.to_state_id(args.block)
|
}
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::block_properties::BedPart;
|
use pumpkin_data::block_properties::BedPart;
|
||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
@ -15,6 +14,7 @@ use pumpkin_world::BlockStateId;
|
|||||||
use pumpkin_world::block::entities::bed::BedBlockEntity;
|
use pumpkin_world::block::entities::bed::BedBlockEntity;
|
||||||
use pumpkin_world::world::BlockFlags;
|
use pumpkin_world::world::BlockFlags;
|
||||||
|
|
||||||
|
use crate::block::BlockFuture;
|
||||||
use crate::block::registry::BlockActionResult;
|
use crate::block::registry::BlockActionResult;
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, BrokenArgs, CanPlaceAtArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs,
|
BlockBehaviour, BrokenArgs, CanPlaceAtArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs,
|
||||||
@ -65,214 +65,223 @@ const NO_SLEEP_IDS: &[u16] = &[
|
|||||||
#[pumpkin_block_from_tag("minecraft:beds")]
|
#[pumpkin_block_from_tag("minecraft:beds")]
|
||||||
pub struct BedBlock;
|
pub struct BedBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for BedBlock {
|
impl BlockBehaviour for BedBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
if let Some(player) = args.player {
|
Box::pin(async move {
|
||||||
let facing = player.living_entity.entity.get_horizontal_facing();
|
if let Some(player) = args.player {
|
||||||
return args
|
let facing = player.living_entity.entity.get_horizontal_facing();
|
||||||
.block_accessor
|
return args
|
||||||
.get_block_state(args.position)
|
|
||||||
.await
|
|
||||||
.replaceable()
|
|
||||||
&& args
|
|
||||||
.block_accessor
|
.block_accessor
|
||||||
.get_block_state(&args.position.offset(facing.to_offset()))
|
.get_block_state(args.position)
|
||||||
.await
|
.await
|
||||||
.replaceable();
|
.replaceable()
|
||||||
}
|
&& args
|
||||||
false
|
.block_accessor
|
||||||
|
.get_block_state(&args.position.offset(facing.to_offset()))
|
||||||
|
.await
|
||||||
|
.replaceable();
|
||||||
|
}
|
||||||
|
false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut bed_props = BedProperties::default(args.block);
|
Box::pin(async move {
|
||||||
|
let mut bed_props = BedProperties::default(args.block);
|
||||||
|
|
||||||
bed_props.facing = args.player.living_entity.entity.get_horizontal_facing();
|
bed_props.facing = args.player.living_entity.entity.get_horizontal_facing();
|
||||||
bed_props.part = BedPart::Foot;
|
bed_props.part = BedPart::Foot;
|
||||||
|
|
||||||
bed_props.to_state_id(args.block)
|
bed_props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn placed(&self, args: PlacedArgs<'_>) {
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let bed_entity = BedBlockEntity::new(*args.position);
|
Box::pin(async move {
|
||||||
args.world.add_block_entity(Arc::new(bed_entity)).await;
|
let bed_entity = BedBlockEntity::new(*args.position);
|
||||||
|
args.world.add_block_entity(Arc::new(bed_entity)).await;
|
||||||
|
|
||||||
let mut bed_head_props = BedProperties::default(args.block);
|
let mut bed_head_props = BedProperties::default(args.block);
|
||||||
bed_head_props.facing = BedProperties::from_state_id(args.state_id, args.block).facing;
|
bed_head_props.facing = BedProperties::from_state_id(args.state_id, args.block).facing;
|
||||||
bed_head_props.part = BedPart::Head;
|
bed_head_props.part = BedPart::Head;
|
||||||
|
|
||||||
let bed_head_pos = args.position.offset(bed_head_props.facing.to_offset());
|
let bed_head_pos = args.position.offset(bed_head_props.facing.to_offset());
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
&bed_head_pos,
|
&bed_head_pos,
|
||||||
bed_head_props.to_state_id(args.block),
|
bed_head_props.to_state_id(args.block),
|
||||||
BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK,
|
BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let bed_head_entity = BedBlockEntity::new(bed_head_pos);
|
let bed_head_entity = BedBlockEntity::new(bed_head_pos);
|
||||||
args.world.add_block_entity(Arc::new(bed_head_entity)).await;
|
args.world.add_block_entity(Arc::new(bed_head_entity)).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn broken(&self, args: BrokenArgs<'_>) {
|
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let bed_props = BedProperties::from_state_id(args.state.id, args.block);
|
Box::pin(async move {
|
||||||
let other_half_pos = if bed_props.part == BedPart::Head {
|
let bed_props = BedProperties::from_state_id(args.state.id, args.block);
|
||||||
args.position
|
let other_half_pos = if bed_props.part == BedPart::Head {
|
||||||
.offset(bed_props.facing.opposite().to_offset())
|
args.position
|
||||||
} else {
|
.offset(bed_props.facing.opposite().to_offset())
|
||||||
args.position.offset(bed_props.facing.to_offset())
|
} else {
|
||||||
};
|
args.position.offset(bed_props.facing.to_offset())
|
||||||
|
};
|
||||||
|
|
||||||
args.world
|
args.world
|
||||||
.break_block(
|
.break_block(
|
||||||
&other_half_pos,
|
&other_half_pos,
|
||||||
Some(args.player.clone()),
|
Some(args.player.clone()),
|
||||||
if args.player.gamemode.load() == GameMode::Creative {
|
if args.player.gamemode.load() == GameMode::Creative {
|
||||||
BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_NEIGHBORS
|
BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_NEIGHBORS
|
||||||
} else {
|
} else {
|
||||||
BlockFlags::NOTIFY_NEIGHBORS
|
BlockFlags::NOTIFY_NEIGHBORS
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
Box::pin(async move {
|
||||||
let bed_props = BedProperties::from_state_id(state_id, args.block);
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
|
let bed_props = BedProperties::from_state_id(state_id, args.block);
|
||||||
|
|
||||||
let (bed_head_pos, bed_foot_pos) = if bed_props.part == BedPart::Head {
|
let (bed_head_pos, bed_foot_pos) = if bed_props.part == BedPart::Head {
|
||||||
(
|
(
|
||||||
*args.position,
|
*args.position,
|
||||||
args.position
|
args.position
|
||||||
.offset(bed_props.facing.opposite().to_offset()),
|
.offset(bed_props.facing.opposite().to_offset()),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
args.position.offset(bed_props.facing.to_offset()),
|
args.position.offset(bed_props.facing.to_offset()),
|
||||||
*args.position,
|
*args.position,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Explode if not in the overworld
|
// Explode if not in the overworld
|
||||||
if args.world.dimension_type != VanillaDimensionType::Overworld {
|
if args.world.dimension_type != VanillaDimensionType::Overworld {
|
||||||
args.world
|
args.world
|
||||||
.break_block(&bed_head_pos, None, BlockFlags::SKIP_DROPS)
|
.break_block(&bed_head_pos, None, BlockFlags::SKIP_DROPS)
|
||||||
.await;
|
.await;
|
||||||
args.world
|
args.world
|
||||||
.break_block(&bed_foot_pos, None, BlockFlags::SKIP_DROPS)
|
.break_block(&bed_foot_pos, None, BlockFlags::SKIP_DROPS)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
args.world
|
args.world
|
||||||
.explode(bed_head_pos.to_centered_f64(), 5.0)
|
.explode(bed_head_pos.to_centered_f64(), 5.0)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
return BlockActionResult::SuccessServer;
|
return BlockActionResult::SuccessServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the bed is not obstructed
|
// Make sure the bed is not obstructed
|
||||||
if args
|
if args
|
||||||
.world
|
|
||||||
.get_block_state(&bed_head_pos.up())
|
|
||||||
.await
|
|
||||||
.is_solid()
|
|
||||||
|| args
|
|
||||||
.world
|
.world
|
||||||
.get_block_state(&bed_head_pos.up())
|
.get_block_state(&bed_head_pos.up())
|
||||||
.await
|
.await
|
||||||
.is_solid()
|
.is_solid()
|
||||||
{
|
|| args
|
||||||
args.player
|
.world
|
||||||
.send_system_message_raw(
|
.get_block_state(&bed_head_pos.up())
|
||||||
&TextComponent::translate("block.minecraft.bed.obstructed", []),
|
.await
|
||||||
true,
|
.is_solid()
|
||||||
)
|
|
||||||
.await;
|
|
||||||
return BlockActionResult::SuccessServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the bed is not occupied
|
|
||||||
if bed_props.occupied {
|
|
||||||
// TODO: Wake up villager
|
|
||||||
|
|
||||||
args.player
|
|
||||||
.send_system_message_raw(
|
|
||||||
&TextComponent::translate("block.minecraft.bed.occupied", []),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
return BlockActionResult::SuccessServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure player is close enough
|
|
||||||
if !args
|
|
||||||
.player
|
|
||||||
.position()
|
|
||||||
.is_within_bounds(bed_head_pos.to_f64(), 3.0, 3.0, 3.0)
|
|
||||||
&& !args
|
|
||||||
.player
|
|
||||||
.position()
|
|
||||||
.is_within_bounds(bed_foot_pos.to_f64(), 3.0, 3.0, 3.0)
|
|
||||||
{
|
|
||||||
args.player
|
|
||||||
.send_system_message_raw(
|
|
||||||
&TextComponent::translate("block.minecraft.bed.too_far_away", []),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
return BlockActionResult::SuccessServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set respawn point
|
|
||||||
if args
|
|
||||||
.player
|
|
||||||
.set_respawn_point(
|
|
||||||
args.world.dimension_type,
|
|
||||||
bed_head_pos,
|
|
||||||
args.player.get_entity().yaw.load(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
args.player
|
|
||||||
.send_system_message(&TextComponent::translate("block.minecraft.set_spawn", []))
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the time and weather allows sleep
|
|
||||||
if !can_sleep(args.world).await {
|
|
||||||
args.player
|
|
||||||
.send_system_message_raw(
|
|
||||||
&TextComponent::translate("block.minecraft.bed.no_sleep", []),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
return BlockActionResult::SuccessServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure there are no monsters nearby
|
|
||||||
for entity in args.world.entities.read().await.values() {
|
|
||||||
if !entity_prevents_sleep(entity.get_entity()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pos = entity.get_entity().pos.load();
|
|
||||||
if pos.is_within_bounds(bed_head_pos.to_f64(), 8.0, 5.0, 8.0)
|
|
||||||
|| pos.is_within_bounds(bed_foot_pos.to_f64(), 8.0, 5.0, 8.0)
|
|
||||||
{
|
{
|
||||||
args.player
|
args.player
|
||||||
.send_system_message_raw(
|
.send_system_message_raw(
|
||||||
&TextComponent::translate("block.minecraft.bed.not_safe", []),
|
&TextComponent::translate("block.minecraft.bed.obstructed", []),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
return BlockActionResult::SuccessServer;
|
return BlockActionResult::SuccessServer;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
args.player.sleep(bed_head_pos).await;
|
// Make sure the bed is not occupied
|
||||||
Self::set_occupied(true, args.world, args.block, args.position, state_id).await;
|
if bed_props.occupied {
|
||||||
|
// TODO: Wake up villager
|
||||||
|
|
||||||
BlockActionResult::SuccessServer
|
args.player
|
||||||
|
.send_system_message_raw(
|
||||||
|
&TextComponent::translate("block.minecraft.bed.occupied", []),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return BlockActionResult::SuccessServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure player is close enough
|
||||||
|
if !args
|
||||||
|
.player
|
||||||
|
.position()
|
||||||
|
.is_within_bounds(bed_head_pos.to_f64(), 3.0, 3.0, 3.0)
|
||||||
|
&& !args
|
||||||
|
.player
|
||||||
|
.position()
|
||||||
|
.is_within_bounds(bed_foot_pos.to_f64(), 3.0, 3.0, 3.0)
|
||||||
|
{
|
||||||
|
args.player
|
||||||
|
.send_system_message_raw(
|
||||||
|
&TextComponent::translate("block.minecraft.bed.too_far_away", []),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return BlockActionResult::SuccessServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set respawn point
|
||||||
|
if args
|
||||||
|
.player
|
||||||
|
.set_respawn_point(
|
||||||
|
args.world.dimension_type,
|
||||||
|
bed_head_pos,
|
||||||
|
args.player.get_entity().yaw.load(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
args.player
|
||||||
|
.send_system_message(&TextComponent::translate("block.minecraft.set_spawn", []))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the time and weather allows sleep
|
||||||
|
if !can_sleep(args.world).await {
|
||||||
|
args.player
|
||||||
|
.send_system_message_raw(
|
||||||
|
&TextComponent::translate("block.minecraft.bed.no_sleep", []),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return BlockActionResult::SuccessServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure there are no monsters nearby
|
||||||
|
for entity in args.world.entities.read().await.values() {
|
||||||
|
if !entity_prevents_sleep(entity.get_entity()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pos = entity.get_entity().pos.load();
|
||||||
|
if pos.is_within_bounds(bed_head_pos.to_f64(), 8.0, 5.0, 8.0)
|
||||||
|
|| pos.is_within_bounds(bed_foot_pos.to_f64(), 8.0, 5.0, 8.0)
|
||||||
|
{
|
||||||
|
args.player
|
||||||
|
.send_system_message_raw(
|
||||||
|
&TextComponent::translate("block.minecraft.bed.not_safe", []),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return BlockActionResult::SuccessServer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args.player.sleep(bed_head_pos).await;
|
||||||
|
Self::set_occupied(true, args.world, args.block, args.position, state_id).await;
|
||||||
|
|
||||||
|
BlockActionResult::SuccessServer
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
use crate::block::{BlockBehaviour, OnPlaceArgs};
|
use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::{BlockProperties, FurnaceLikeProperties};
|
use pumpkin_data::block_properties::{BlockProperties, FurnaceLikeProperties};
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
@ -7,16 +6,17 @@ use pumpkin_world::BlockStateId;
|
|||||||
#[pumpkin_block("minecraft:blast_furnace")]
|
#[pumpkin_block("minecraft:blast_furnace")]
|
||||||
pub struct BlastFurnaceBlock;
|
pub struct BlastFurnaceBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for BlastFurnaceBlock {
|
impl BlockBehaviour for BlastFurnaceBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut props = FurnaceLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
props.facing = args
|
let mut props = FurnaceLikeProperties::default(args.block);
|
||||||
.player
|
props.facing = args
|
||||||
.living_entity
|
.player
|
||||||
.entity
|
.living_entity
|
||||||
.get_horizontal_facing()
|
.entity
|
||||||
.opposite();
|
.get_horizontal_facing()
|
||||||
props.to_state_id(args.block)
|
.opposite();
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::{
|
use pumpkin_data::block_properties::{
|
||||||
BlockProperties, CactusLikeProperties, EnumVariants, Integer0To15,
|
BlockProperties, CactusLikeProperties, EnumVariants, Integer0To15,
|
||||||
};
|
};
|
||||||
@ -11,82 +10,87 @@ use pumpkin_world::tick::TickPriority;
|
|||||||
use pumpkin_world::world::{BlockAccessor, BlockFlags};
|
use pumpkin_world::world::{BlockAccessor, BlockFlags};
|
||||||
|
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs,
|
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
||||||
OnScheduledTickArgs, RandomTickArgs,
|
OnScheduledTickArgs, RandomTickArgs,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[pumpkin_block("minecraft:cactus")]
|
#[pumpkin_block("minecraft:cactus")]
|
||||||
pub struct CactusBlock;
|
pub struct CactusBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for CactusBlock {
|
impl BlockBehaviour for CactusBlock {
|
||||||
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
if !can_place_at(args.world.as_ref(), args.position).await {
|
Box::pin(async move {
|
||||||
args.world
|
if !can_place_at(args.world.as_ref(), args.position).await {
|
||||||
.break_block(args.position, None, BlockFlags::empty())
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn random_tick(&self, args: RandomTickArgs<'_>) {
|
|
||||||
if args
|
|
||||||
.world
|
|
||||||
.get_block_state(&args.position.up())
|
|
||||||
.await
|
|
||||||
.is_air()
|
|
||||||
{
|
|
||||||
let state_id = args.world.get_block_state(args.position).await.id;
|
|
||||||
let age = CactusLikeProperties::from_state_id(state_id, args.block).age;
|
|
||||||
if age == Integer0To15::L15 {
|
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.break_block(args.position, None, BlockFlags::empty())
|
||||||
&args.position.up(),
|
|
||||||
Block::CACTUS.default_state.id,
|
|
||||||
BlockFlags::empty(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
args.world
|
|
||||||
.set_block_state(
|
|
||||||
args.position,
|
|
||||||
Block::CACTUS.default_state.id,
|
|
||||||
BlockFlags::empty(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
} else {
|
|
||||||
let props = CactusLikeProperties {
|
|
||||||
age: Integer0To15::from_index(age.to_index() + 1),
|
|
||||||
};
|
|
||||||
args.world
|
|
||||||
.set_block_state(
|
|
||||||
args.position,
|
|
||||||
props.to_state_id(args.block),
|
|
||||||
BlockFlags::empty(),
|
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_entity_collision(&self, _args: OnEntityCollisionArgs<'_>) {
|
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
// TODO
|
Box::pin(async move {
|
||||||
//args.entity.damage(1.0, DamageType::CACTUS).await;
|
if args
|
||||||
|
.world
|
||||||
|
.get_block_state(&args.position.up())
|
||||||
|
.await
|
||||||
|
.is_air()
|
||||||
|
{
|
||||||
|
let state_id = args.world.get_block_state(args.position).await.id;
|
||||||
|
let age = CactusLikeProperties::from_state_id(state_id, args.block).age;
|
||||||
|
if age == Integer0To15::L15 {
|
||||||
|
args.world
|
||||||
|
.set_block_state(
|
||||||
|
&args.position.up(),
|
||||||
|
Block::CACTUS.default_state.id,
|
||||||
|
BlockFlags::empty(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
args.world
|
||||||
|
.set_block_state(
|
||||||
|
args.position,
|
||||||
|
Block::CACTUS.default_state.id,
|
||||||
|
BlockFlags::empty(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
} else {
|
||||||
|
let props = CactusLikeProperties {
|
||||||
|
age: Integer0To15::from_index(age.to_index() + 1),
|
||||||
|
};
|
||||||
|
args.world
|
||||||
|
.set_block_state(
|
||||||
|
args.position,
|
||||||
|
props.to_state_id(args.block),
|
||||||
|
BlockFlags::empty(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
// async fn on_entity_collision(&self, _args: OnEntityCollisionArgs<'_>) {
|
||||||
&self,
|
// // TODO
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
// //args.entity.damage(1.0, DamageType::CACTUS).await;
|
||||||
) -> BlockStateId {
|
// }
|
||||||
if !can_place_at(args.world, args.position).await {
|
|
||||||
args.world
|
|
||||||
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.state_id
|
fn get_state_for_neighbor_update<'a>(
|
||||||
|
&'a self,
|
||||||
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
|
Box::pin(async move {
|
||||||
|
if !can_place_at(args.world, args.position).await {
|
||||||
|
args.world
|
||||||
|
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.state_id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
can_place_at(args.block_accessor, args.position).await
|
Box::pin(async move { can_place_at(args.block_accessor, args.position).await })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,14 +2,12 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
block::{
|
block::{
|
||||||
blocks::candle_cakes::cake_from_candle,
|
BlockBehaviour, BlockFuture, NormalUseArgs, UseWithItemArgs,
|
||||||
registry::BlockActionResult,
|
blocks::candle_cakes::cake_from_candle, registry::BlockActionResult,
|
||||||
{BlockBehaviour, NormalUseArgs, UseWithItemArgs},
|
|
||||||
},
|
},
|
||||||
entity::player::Player,
|
entity::player::Player,
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::item::Item;
|
use pumpkin_data::item::Item;
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block,
|
Block,
|
||||||
@ -78,17 +76,56 @@ impl CakeBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for CakeBlock {
|
impl BlockBehaviour for CakeBlock {
|
||||||
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
|
fn use_with_item<'a>(
|
||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
&'a self,
|
||||||
let properties = CakeLikeProperties::from_state_id(state_id, args.block);
|
args: UseWithItemArgs<'a>,
|
||||||
let item_lock = args.item_stack.lock().await;
|
) -> BlockFuture<'a, BlockActionResult> {
|
||||||
let item = item_lock.item;
|
Box::pin(async move {
|
||||||
drop(item_lock);
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
match item.id {
|
let properties = CakeLikeProperties::from_state_id(state_id, args.block);
|
||||||
id if (Item::CANDLE.id..=Item::BLACK_CANDLE.id).contains(&id) => {
|
let item_lock = args.item_stack.lock().await;
|
||||||
if properties.bites.to_index() != 0 {
|
let item = item_lock.item;
|
||||||
|
drop(item_lock);
|
||||||
|
match item.id {
|
||||||
|
id if (Item::CANDLE.id..=Item::BLACK_CANDLE.id).contains(&id) => {
|
||||||
|
if properties.bites.to_index() != 0 {
|
||||||
|
return Self::consume_if_hungry(
|
||||||
|
args.world,
|
||||||
|
args.player,
|
||||||
|
args.block,
|
||||||
|
args.position,
|
||||||
|
state_id,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.player.gamemode.load() != GameMode::Creative {
|
||||||
|
let held_item = args.player.inventory.held_item();
|
||||||
|
let mut held_item_guard = held_item.lock().await;
|
||||||
|
held_item_guard.decrement(1);
|
||||||
|
}
|
||||||
|
args.world
|
||||||
|
.set_block_state(
|
||||||
|
args.position,
|
||||||
|
cake_from_candle(item).default_state.id,
|
||||||
|
BlockFlags::NOTIFY_ALL,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let seed: f64 = rng().random();
|
||||||
|
args.player
|
||||||
|
.play_sound(
|
||||||
|
Sound::BlockCakeAddCandle as u16,
|
||||||
|
SoundCategory::Blocks,
|
||||||
|
&args.position.to_f64(),
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
seed,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
BlockActionResult::Consume
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
return Self::consume_if_hungry(
|
return Self::consume_if_hungry(
|
||||||
args.world,
|
args.world,
|
||||||
args.player,
|
args.player,
|
||||||
@ -98,47 +135,15 @@ impl BlockBehaviour for CakeBlock {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.player.gamemode.load() != GameMode::Creative {
|
|
||||||
let held_item = args.player.inventory.held_item();
|
|
||||||
let mut held_item_guard = held_item.lock().await;
|
|
||||||
held_item_guard.decrement(1);
|
|
||||||
}
|
|
||||||
args.world
|
|
||||||
.set_block_state(
|
|
||||||
args.position,
|
|
||||||
cake_from_candle(item).default_state.id,
|
|
||||||
BlockFlags::NOTIFY_ALL,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
let seed: f64 = rng().random();
|
|
||||||
args.player
|
|
||||||
.play_sound(
|
|
||||||
Sound::BlockCakeAddCandle as u16,
|
|
||||||
SoundCategory::Blocks,
|
|
||||||
&args.position.to_f64(),
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
seed,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
return BlockActionResult::Consume;
|
|
||||||
}
|
}
|
||||||
_ => {
|
})
|
||||||
return Self::consume_if_hungry(
|
|
||||||
args.world,
|
|
||||||
args.player,
|
|
||||||
args.block,
|
|
||||||
args.position,
|
|
||||||
state_id,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
Box::pin(async move {
|
||||||
Self::consume_if_hungry(args.world, args.player, args.block, args.position, state_id).await
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
|
Self::consume_if_hungry(args.world, args.player, args.block, args.position, state_id)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockDirection,
|
Block, BlockDirection,
|
||||||
block_properties::{BlockProperties, CampfireLikeProperties},
|
block_properties::{BlockProperties, CampfireLikeProperties},
|
||||||
@ -8,11 +7,8 @@ use pumpkin_world::{BlockStateId, tick::TickPriority};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
block::{
|
block::{
|
||||||
BlockIsReplacing,
|
BlockBehaviour, BlockFuture, BlockIsReplacing, BlockMetadata,
|
||||||
{
|
GetStateForNeighborUpdateArgs, OnEntityCollisionArgs, OnPlaceArgs,
|
||||||
BlockBehaviour, BlockMetadata, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs,
|
|
||||||
OnPlaceArgs,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
entity::EntityBase,
|
entity::EntityBase,
|
||||||
};
|
};
|
||||||
@ -29,53 +25,58 @@ impl BlockMetadata for CampfireBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for CampfireBlock {
|
impl BlockBehaviour for CampfireBlock {
|
||||||
// TODO: cooking food on campfire (CampfireBlockEntity)
|
// TODO: cooking food on campfire (CampfireBlockEntity)
|
||||||
async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) {
|
fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
if CampfireLikeProperties::from_state_id(args.state.id, args.block).lit
|
Box::pin(async move {
|
||||||
&& args.entity.get_living_entity().is_some()
|
if CampfireLikeProperties::from_state_id(args.state.id, args.block).lit
|
||||||
{
|
&& args.entity.get_living_entity().is_some()
|
||||||
// TODO
|
{
|
||||||
//args.entity.damage(args.entity, 1.0, DamageType::CAMPFIRE).await;
|
// TODO
|
||||||
}
|
//args.entity.damage(args.entity, 1.0, DamageType::CAMPFIRE).await;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let is_replacing_water = matches!(args.replacing, BlockIsReplacing::Water(_));
|
Box::pin(async move {
|
||||||
let mut props =
|
let is_replacing_water = matches!(args.replacing, BlockIsReplacing::Water(_));
|
||||||
CampfireLikeProperties::from_state_id(args.block.default_state.id, args.block);
|
let mut props =
|
||||||
props.waterlogged = is_replacing_water;
|
CampfireLikeProperties::from_state_id(args.block.default_state.id, args.block);
|
||||||
props.signal_fire =
|
props.waterlogged = is_replacing_water;
|
||||||
is_signal_fire_base_block(args.world.get_block(&args.position.down()).await);
|
|
||||||
props.lit = !is_replacing_water;
|
|
||||||
props.facing = args.player.get_entity().get_horizontal_facing();
|
|
||||||
props.to_state_id(args.block)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
|
||||||
&self,
|
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
|
||||||
) -> BlockStateId {
|
|
||||||
let mut props = CampfireLikeProperties::from_state_id(args.state_id, args.block);
|
|
||||||
if props.waterlogged {
|
|
||||||
props.lit = false;
|
|
||||||
args.world
|
|
||||||
.schedule_fluid_tick(
|
|
||||||
&Fluid::WATER,
|
|
||||||
*args.position,
|
|
||||||
Fluid::WATER.flow_speed as u8,
|
|
||||||
TickPriority::Normal,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.direction == BlockDirection::Down {
|
|
||||||
props.signal_fire =
|
props.signal_fire =
|
||||||
is_signal_fire_base_block(args.world.get_block(args.neighbor_position).await);
|
is_signal_fire_base_block(args.world.get_block(&args.position.down()).await);
|
||||||
}
|
props.lit = !is_replacing_water;
|
||||||
|
props.facing = args.player.get_entity().get_horizontal_facing();
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
props.to_state_id(args.block)
|
fn get_state_for_neighbor_update<'a>(
|
||||||
|
&'a self,
|
||||||
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut props = CampfireLikeProperties::from_state_id(args.state_id, args.block);
|
||||||
|
if props.waterlogged {
|
||||||
|
props.lit = false;
|
||||||
|
args.world
|
||||||
|
.schedule_fluid_tick(
|
||||||
|
&Fluid::WATER,
|
||||||
|
*args.position,
|
||||||
|
Fluid::WATER.flow_speed as u8,
|
||||||
|
TickPriority::Normal,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.direction == BlockDirection::Down {
|
||||||
|
props.signal_fire =
|
||||||
|
is_signal_fire_base_block(args.world.get_block(args.neighbor_position).await);
|
||||||
|
}
|
||||||
|
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: onProjectileHit
|
// TODO: onProjectileHit
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block,
|
Block,
|
||||||
item::Item,
|
item::Item,
|
||||||
@ -12,9 +11,8 @@ use pumpkin_world::{item::ItemStack, world::BlockFlags};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
block::{
|
block::{
|
||||||
blocks::cake::CakeBlock,
|
BlockBehaviour, BlockFuture, NormalUseArgs, UseWithItemArgs, blocks::cake::CakeBlock,
|
||||||
registry::BlockActionResult,
|
registry::BlockActionResult,
|
||||||
{BlockBehaviour, NormalUseArgs, UseWithItemArgs},
|
|
||||||
},
|
},
|
||||||
entity::player::Player,
|
entity::player::Player,
|
||||||
world::World,
|
world::World,
|
||||||
@ -100,19 +98,25 @@ impl CandleCakeBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for CandleCakeBlock {
|
impl BlockBehaviour for CandleCakeBlock {
|
||||||
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
|
fn use_with_item<'a>(
|
||||||
let item_id = args.item_stack.lock().await.item.id;
|
&'a self,
|
||||||
match item_id {
|
args: UseWithItemArgs<'a>,
|
||||||
id if id == Item::FIRE_CHARGE.id || id == Item::FLINT_AND_STEEL.id => {
|
) -> BlockFuture<'a, BlockActionResult> {
|
||||||
BlockActionResult::Pass
|
Box::pin(async move {
|
||||||
} // Item::FIRE_CHARGE | Item::FLINT_AND_STEEL
|
let item_id = args.item_stack.lock().await.item.id;
|
||||||
_ => BlockActionResult::PassToDefaultBlockAction,
|
match item_id {
|
||||||
}
|
id if id == Item::FIRE_CHARGE.id || id == Item::FLINT_AND_STEEL.id => {
|
||||||
|
BlockActionResult::Pass
|
||||||
|
} // Item::FIRE_CHARGE | Item::FLINT_AND_STEEL
|
||||||
|
_ => BlockActionResult::PassToDefaultBlockAction,
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
Self::consume_and_drop_candle(args.block, args.player, args.position, args.world).await
|
Box::pin(async move {
|
||||||
|
Self::consume_and_drop_candle(args.block, args.player, args.position, args.world).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::item::Item;
|
use pumpkin_data::item::Item;
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
BlockDirection,
|
BlockDirection,
|
||||||
@ -9,6 +8,7 @@ use pumpkin_data::{
|
|||||||
use pumpkin_macros::pumpkin_block_from_tag;
|
use pumpkin_macros::pumpkin_block_from_tag;
|
||||||
use pumpkin_world::{BlockStateId, world::BlockFlags};
|
use pumpkin_world::{BlockStateId, world::BlockFlags};
|
||||||
|
|
||||||
|
use crate::block::BlockFuture;
|
||||||
use crate::{
|
use crate::{
|
||||||
block::{
|
block::{
|
||||||
BlockIsReplacing,
|
BlockIsReplacing,
|
||||||
@ -24,99 +24,112 @@ use crate::{
|
|||||||
#[pumpkin_block_from_tag("minecraft:candles")]
|
#[pumpkin_block_from_tag("minecraft:candles")]
|
||||||
pub struct CandleBlock;
|
pub struct CandleBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for CandleBlock {
|
impl BlockBehaviour for CandleBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
if args.player.get_entity().pose.load() != EntityPose::Crouching
|
Box::pin(async move {
|
||||||
&& let BlockIsReplacing::Itself(state_id) = args.replacing
|
if args.player.get_entity().pose.load() != EntityPose::Crouching
|
||||||
{
|
&& let BlockIsReplacing::Itself(state_id) = args.replacing
|
||||||
let mut properties = CandleLikeProperties::from_state_id(state_id, args.block);
|
|
||||||
if properties.candles.to_index() < 3 {
|
|
||||||
properties.candles = Integer1To4::from_index(properties.candles.to_index() + 1);
|
|
||||||
}
|
|
||||||
return properties.to_state_id(args.block);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut properties = CandleLikeProperties::default(args.block);
|
|
||||||
properties.waterlogged = args.replacing.water_source();
|
|
||||||
properties.to_state_id(args.block)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
|
|
||||||
let state = args.world.get_block_state(args.position).await;
|
|
||||||
let mut properties = CandleLikeProperties::from_state_id(state.id, args.block);
|
|
||||||
|
|
||||||
let item_lock = args.item_stack.lock().await;
|
|
||||||
let item = item_lock.item;
|
|
||||||
drop(item_lock);
|
|
||||||
match item.id {
|
|
||||||
id if (Item::CANDLE.id..=Item::BLACK_CANDLE.id).contains(&id)
|
|
||||||
&& item.id == args.block.id =>
|
|
||||||
{
|
{
|
||||||
|
let mut properties = CandleLikeProperties::from_state_id(state_id, args.block);
|
||||||
if properties.candles.to_index() < 3 {
|
if properties.candles.to_index() < 3 {
|
||||||
properties.candles = Integer1To4::from_index(properties.candles.to_index() + 1);
|
properties.candles = Integer1To4::from_index(properties.candles.to_index() + 1);
|
||||||
}
|
}
|
||||||
|
return properties.to_state_id(args.block);
|
||||||
args.world
|
|
||||||
.set_block_state(
|
|
||||||
args.position,
|
|
||||||
properties.to_state_id(args.block),
|
|
||||||
BlockFlags::NOTIFY_ALL,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
BlockActionResult::Consume
|
|
||||||
}
|
}
|
||||||
_ => {
|
|
||||||
if properties.lit {
|
let mut properties = CandleLikeProperties::default(args.block);
|
||||||
properties.lit = false;
|
properties.waterlogged = args.replacing.water_source();
|
||||||
} else {
|
properties.to_state_id(args.block)
|
||||||
return BlockActionResult::Pass;
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_with_item<'a>(
|
||||||
|
&'a self,
|
||||||
|
args: UseWithItemArgs<'a>,
|
||||||
|
) -> BlockFuture<'a, BlockActionResult> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let state = args.world.get_block_state(args.position).await;
|
||||||
|
let mut properties = CandleLikeProperties::from_state_id(state.id, args.block);
|
||||||
|
|
||||||
|
let item_lock = args.item_stack.lock().await;
|
||||||
|
let item = item_lock.item;
|
||||||
|
drop(item_lock);
|
||||||
|
match item.id {
|
||||||
|
id if (Item::CANDLE.id..=Item::BLACK_CANDLE.id).contains(&id)
|
||||||
|
&& item.id == args.block.id =>
|
||||||
|
{
|
||||||
|
if properties.candles.to_index() < 3 {
|
||||||
|
properties.candles =
|
||||||
|
Integer1To4::from_index(properties.candles.to_index() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.world
|
||||||
|
.set_block_state(
|
||||||
|
args.position,
|
||||||
|
properties.to_state_id(args.block),
|
||||||
|
BlockFlags::NOTIFY_ALL,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
BlockActionResult::Consume
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
|
if properties.lit {
|
||||||
|
properties.lit = false;
|
||||||
|
} else {
|
||||||
|
return BlockActionResult::Pass;
|
||||||
|
}
|
||||||
|
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
properties.to_state_id(args.block),
|
properties.to_state_id(args.block),
|
||||||
BlockFlags::NOTIFY_ALL,
|
BlockFlags::NOTIFY_ALL,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
BlockActionResult::Consume
|
BlockActionResult::Consume
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
Box::pin(async move {
|
||||||
let mut properties = CandleLikeProperties::from_state_id(state_id, args.block);
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
|
let mut properties = CandleLikeProperties::from_state_id(state_id, args.block);
|
||||||
|
|
||||||
if properties.lit {
|
if properties.lit {
|
||||||
properties.lit = false;
|
properties.lit = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
properties.to_state_id(args.block),
|
properties.to_state_id(args.block),
|
||||||
BlockFlags::NOTIFY_ALL,
|
BlockFlags::NOTIFY_ALL,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
BlockActionResult::Consume
|
BlockActionResult::Consume
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
let (support_block, state) = args
|
Box::pin(async move {
|
||||||
.block_accessor
|
let (support_block, state) = args
|
||||||
.get_block_and_state(&args.position.down())
|
.block_accessor
|
||||||
.await;
|
.get_block_and_state(&args.position.down())
|
||||||
!support_block.is_waterlogged(state.id) && state.is_center_solid(BlockDirection::Up)
|
.await;
|
||||||
|
!support_block.is_waterlogged(state.id) && state.is_center_solid(BlockDirection::Up)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn can_update_at(&self, args: CanUpdateAtArgs<'_>) -> bool {
|
fn can_update_at<'a>(&'a self, args: CanUpdateAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
let b = args.world.get_block(args.position).await;
|
Box::pin(async move {
|
||||||
args.player.get_entity().pose.load() != EntityPose::Crouching
|
let b = args.world.get_block(args.position).await;
|
||||||
&& CandleLikeProperties::from_state_id(args.state_id, args.block).candles
|
args.player.get_entity().pose.load() != EntityPose::Crouching
|
||||||
!= Integer1To4::L4
|
&& CandleLikeProperties::from_state_id(args.state_id, args.block).candles
|
||||||
&& args.block.id == b.id // only the same color can update
|
!= Integer1To4::L4
|
||||||
|
&& args.block.id == b.id // only the same color can update
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnScheduledTickArgs,
|
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnScheduledTickArgs,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
||||||
use pumpkin_macros::{pumpkin_block, pumpkin_block_from_tag};
|
use pumpkin_macros::{pumpkin_block, pumpkin_block_from_tag};
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
@ -12,90 +11,99 @@ use pumpkin_world::world::{BlockAccessor, BlockFlags};
|
|||||||
#[pumpkin_block_from_tag("minecraft:wool_carpets")]
|
#[pumpkin_block_from_tag("minecraft:wool_carpets")]
|
||||||
pub struct CarpetBlock;
|
pub struct CarpetBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for CarpetBlock {
|
impl BlockBehaviour for CarpetBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
can_place_at(args.block_accessor, args.position).await
|
Box::pin(async move { can_place_at(args.block_accessor, args.position).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
if !can_place_at(args.world, args.position).await {
|
Box::pin(async move {
|
||||||
args.world
|
if !can_place_at(args.world, args.position).await {
|
||||||
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
args.world
|
||||||
.await;
|
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
||||||
}
|
.await;
|
||||||
args.state_id
|
}
|
||||||
|
args.state_id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
if !can_place_at(args.world.as_ref(), args.position).await {
|
Box::pin(async move {
|
||||||
args.world
|
if !can_place_at(args.world.as_ref(), args.position).await {
|
||||||
.break_block(args.position, None, BlockFlags::empty())
|
args.world
|
||||||
.await;
|
.break_block(args.position, None, BlockFlags::empty())
|
||||||
}
|
.await;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pumpkin_block("minecraft:moss_carpet")]
|
#[pumpkin_block("minecraft:moss_carpet")]
|
||||||
pub struct MossCarpetBlock;
|
pub struct MossCarpetBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for MossCarpetBlock {
|
impl BlockBehaviour for MossCarpetBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
can_place_at(args.block_accessor, args.position).await
|
Box::pin(async move { can_place_at(args.block_accessor, args.position).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
if !can_place_at(args.world, args.position).await {
|
Box::pin(async move {
|
||||||
args.world
|
if !can_place_at(args.world, args.position).await {
|
||||||
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
args.world
|
||||||
.await;
|
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
||||||
}
|
.await;
|
||||||
args.state_id
|
}
|
||||||
|
args.state_id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
if !can_place_at(args.world.as_ref(), args.position).await {
|
Box::pin(async move {
|
||||||
args.world
|
if !can_place_at(args.world.as_ref(), args.position).await {
|
||||||
.break_block(args.position, None, BlockFlags::empty())
|
args.world
|
||||||
.await;
|
.break_block(args.position, None, BlockFlags::empty())
|
||||||
}
|
.await;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pumpkin_block("minecraft:pale_moss_carpet")]
|
#[pumpkin_block("minecraft:pale_moss_carpet")]
|
||||||
pub struct PaleMossCarpetBlock;
|
pub struct PaleMossCarpetBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for PaleMossCarpetBlock {
|
impl BlockBehaviour for PaleMossCarpetBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
can_place_at(args.block_accessor, args.position).await
|
Box::pin(async move { can_place_at(args.block_accessor, args.position).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
if !can_place_at(args.world, args.position).await {
|
Box::pin(async move {
|
||||||
args.world
|
if !can_place_at(args.world, args.position).await {
|
||||||
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
args.world
|
||||||
.await;
|
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
||||||
}
|
.await;
|
||||||
args.state_id
|
}
|
||||||
|
args.state_id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
if !can_place_at(args.world.as_ref(), args.position).await {
|
Box::pin(async move {
|
||||||
args.world
|
if !can_place_at(args.world.as_ref(), args.position).await {
|
||||||
.break_block(args.position, None, BlockFlags::empty())
|
args.world
|
||||||
.await;
|
.break_block(args.position, None, BlockFlags::empty())
|
||||||
}
|
.await;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block,
|
Block,
|
||||||
block_properties::{BlockProperties, WallTorchLikeProperties},
|
block_properties::{BlockProperties, WallTorchLikeProperties},
|
||||||
};
|
};
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
|
|
||||||
use crate::block::{BlockBehaviour, BlockMetadata, OnPlaceArgs, PlacedArgs};
|
use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, OnPlaceArgs};
|
||||||
|
|
||||||
pub struct CarvedPumpkinBlock;
|
pub struct CarvedPumpkinBlock;
|
||||||
|
|
||||||
@ -19,18 +18,17 @@ impl BlockMetadata for CarvedPumpkinBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for CarvedPumpkinBlock {
|
impl BlockBehaviour for CarvedPumpkinBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut props = WallTorchLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
props.facing = args
|
let mut props = WallTorchLikeProperties::default(args.block);
|
||||||
.player
|
props.facing = args
|
||||||
.living_entity
|
.player
|
||||||
.entity
|
.living_entity
|
||||||
.get_horizontal_facing()
|
.entity
|
||||||
.opposite();
|
.get_horizontal_facing()
|
||||||
props.to_state_id(args.block)
|
.opposite();
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn placed(&self, _args: PlacedArgs<'_>) {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
use crate::block::BlockFuture;
|
||||||
use crate::block::{BlockBehaviour, OnPlaceArgs};
|
use crate::block::{BlockBehaviour, OnPlaceArgs};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::BlockDirection;
|
use pumpkin_data::BlockDirection;
|
||||||
use pumpkin_data::block_properties::Axis;
|
use pumpkin_data::block_properties::Axis;
|
||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
@ -9,18 +9,19 @@ use pumpkin_world::BlockStateId;
|
|||||||
#[pumpkin_block("minecraft:iron_chain")]
|
#[pumpkin_block("minecraft:iron_chain")]
|
||||||
pub struct ChainBlock;
|
pub struct ChainBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for ChainBlock {
|
impl BlockBehaviour for ChainBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut props =
|
Box::pin(async move {
|
||||||
pumpkin_data::block_properties::IronChainLikeProperties::default(args.block);
|
let mut props =
|
||||||
props.r#waterlogged = args.replacing.water_source();
|
pumpkin_data::block_properties::IronChainLikeProperties::default(args.block);
|
||||||
props.r#axis = match args.direction {
|
props.r#waterlogged = args.replacing.water_source();
|
||||||
BlockDirection::East | BlockDirection::West => Axis::X,
|
props.r#axis = match args.direction {
|
||||||
BlockDirection::Up | BlockDirection::Down => Axis::Y,
|
BlockDirection::East | BlockDirection::West => Axis::X,
|
||||||
BlockDirection::North | BlockDirection::South => Axis::Z,
|
BlockDirection::Up | BlockDirection::Down => Axis::Y,
|
||||||
};
|
BlockDirection::North | BlockDirection::South => Axis::Z,
|
||||||
|
};
|
||||||
|
|
||||||
props.to_state_id(args.block)
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,8 @@ use pumpkin_world::world::BlockFlags;
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockMetadata, BrokenArgs, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs,
|
BlockFuture, BlockMetadata, BrokenArgs, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs,
|
||||||
|
PlacedArgs,
|
||||||
};
|
};
|
||||||
use crate::entity::EntityBase;
|
use crate::entity::EntityBase;
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
@ -69,135 +70,147 @@ impl BlockMetadata for ChestBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for ChestBlock {
|
impl BlockBehaviour for ChestBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut chest_props = ChestLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
|
let mut chest_props = ChestLikeProperties::default(args.block);
|
||||||
|
|
||||||
chest_props.waterlogged = args.replacing.water_source();
|
chest_props.waterlogged = args.replacing.water_source();
|
||||||
|
|
||||||
let (r#type, facing) = compute_chest_props(
|
let (r#type, facing) = compute_chest_props(
|
||||||
args.world,
|
args.world,
|
||||||
args.player,
|
args.player,
|
||||||
args.block,
|
args.block,
|
||||||
args.position,
|
args.position,
|
||||||
args.direction,
|
args.direction,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
chest_props.facing = facing;
|
chest_props.facing = facing;
|
||||||
chest_props.r#type = r#type;
|
chest_props.r#type = r#type;
|
||||||
|
|
||||||
chest_props.to_state_id(args.block)
|
chest_props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool {
|
fn on_synced_block_event<'a>(
|
||||||
// On the server, we don't need to do more because the client is responsible for that.
|
&'a self,
|
||||||
args.r#type == Self::LID_ANIMATION_EVENT_TYPE
|
args: OnSyncedBlockEventArgs<'a>,
|
||||||
|
) -> BlockFuture<'a, bool> {
|
||||||
|
Box::pin(async move {
|
||||||
|
// On the server, we don't need to do more because the client is responsible for that.
|
||||||
|
args.r#type == Self::LID_ANIMATION_EVENT_TYPE
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn placed(&self, args: PlacedArgs<'_>) {
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let chest = ChestBlockEntity::new(*args.position);
|
Box::pin(async move {
|
||||||
args.world.add_block_entity(Arc::new(chest)).await;
|
let chest = ChestBlockEntity::new(*args.position);
|
||||||
|
args.world.add_block_entity(Arc::new(chest)).await;
|
||||||
|
|
||||||
let chest_props = ChestLikeProperties::from_state_id(args.state_id, args.block);
|
let chest_props = ChestLikeProperties::from_state_id(args.state_id, args.block);
|
||||||
let connected_towards = match chest_props.r#type {
|
let connected_towards = match chest_props.r#type {
|
||||||
ChestType::Single => return,
|
ChestType::Single => return,
|
||||||
ChestType::Left => chest_props.facing.rotate_clockwise(),
|
ChestType::Left => chest_props.facing.rotate_clockwise(),
|
||||||
ChestType::Right => chest_props.facing.rotate_counter_clockwise(),
|
ChestType::Right => chest_props.facing.rotate_counter_clockwise(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(mut neighbor_props) = get_chest_properties_if_can_connect(
|
if let Some(mut neighbor_props) = get_chest_properties_if_can_connect(
|
||||||
args.world,
|
args.world,
|
||||||
args.block,
|
args.block,
|
||||||
args.position,
|
args.position,
|
||||||
chest_props.facing,
|
chest_props.facing,
|
||||||
connected_towards,
|
connected_towards,
|
||||||
ChestType::Single,
|
ChestType::Single,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
neighbor_props.r#type = chest_props.r#type.opposite();
|
neighbor_props.r#type = chest_props.r#type.opposite();
|
||||||
|
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
&args.position.offset(connected_towards.to_offset()),
|
&args.position.offset(connected_towards.to_offset()),
|
||||||
neighbor_props.to_state_id(args.block),
|
neighbor_props.to_state_id(args.block),
|
||||||
BlockFlags::NOTIFY_LISTENERS,
|
BlockFlags::NOTIFY_LISTENERS,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
|
||||||
let (state, first_chest) = join(
|
|
||||||
args.world.get_block_state_id(args.position),
|
|
||||||
args.world.get_block_entity(args.position),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let Some(first_inventory) = first_chest.and_then(BlockEntity::get_inventory) else {
|
|
||||||
return BlockActionResult::Fail;
|
|
||||||
};
|
|
||||||
|
|
||||||
let chest_props = ChestLikeProperties::from_state_id(state, args.block);
|
|
||||||
let connected_towards = match chest_props.r#type {
|
|
||||||
ChestType::Single => None,
|
|
||||||
ChestType::Left => Some(chest_props.facing.rotate_clockwise()),
|
|
||||||
ChestType::Right => Some(chest_props.facing.rotate_counter_clockwise()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let inventory = if let Some(direction) = connected_towards
|
|
||||||
&& let Some(second_inventory) = args
|
|
||||||
.world
|
|
||||||
.get_block_entity(&args.position.offset(direction.to_offset()))
|
|
||||||
.await
|
|
||||||
.and_then(BlockEntity::get_inventory)
|
|
||||||
{
|
|
||||||
// Vanilla: chestType == ChestType.RIGHT ? DoubleBlockProperties.Type.FIRST : DoubleBlockProperties.Type.SECOND;
|
|
||||||
if matches!(chest_props.r#type, ChestType::Right) {
|
|
||||||
DoubleInventory::new(first_inventory, second_inventory)
|
|
||||||
} else {
|
|
||||||
DoubleInventory::new(second_inventory, first_inventory)
|
|
||||||
}
|
}
|
||||||
} else {
|
})
|
||||||
first_inventory
|
}
|
||||||
};
|
|
||||||
|
|
||||||
args.player
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
.open_handled_screen(&ChestScreenFactory(inventory))
|
Box::pin(async move {
|
||||||
|
let (state, first_chest) = join(
|
||||||
|
args.world.get_block_state_id(args.position),
|
||||||
|
args.world.get_block_entity(args.position),
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
BlockActionResult::Success
|
let Some(first_inventory) = first_chest.and_then(BlockEntity::get_inventory) else {
|
||||||
|
return BlockActionResult::Fail;
|
||||||
|
};
|
||||||
|
|
||||||
|
let chest_props = ChestLikeProperties::from_state_id(state, args.block);
|
||||||
|
let connected_towards = match chest_props.r#type {
|
||||||
|
ChestType::Single => None,
|
||||||
|
ChestType::Left => Some(chest_props.facing.rotate_clockwise()),
|
||||||
|
ChestType::Right => Some(chest_props.facing.rotate_counter_clockwise()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let inventory = if let Some(direction) = connected_towards
|
||||||
|
&& let Some(second_inventory) = args
|
||||||
|
.world
|
||||||
|
.get_block_entity(&args.position.offset(direction.to_offset()))
|
||||||
|
.await
|
||||||
|
.and_then(BlockEntity::get_inventory)
|
||||||
|
{
|
||||||
|
// Vanilla: chestType == ChestType.RIGHT ? DoubleBlockProperties.Type.FIRST : DoubleBlockProperties.Type.SECOND;
|
||||||
|
if matches!(chest_props.r#type, ChestType::Right) {
|
||||||
|
DoubleInventory::new(first_inventory, second_inventory)
|
||||||
|
} else {
|
||||||
|
DoubleInventory::new(second_inventory, first_inventory)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
first_inventory
|
||||||
|
};
|
||||||
|
|
||||||
|
args.player
|
||||||
|
.open_handled_screen(&ChestScreenFactory(inventory))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
BlockActionResult::Success
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn broken(&self, args: BrokenArgs<'_>) {
|
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let chest_props = ChestLikeProperties::from_state_id(args.state.id, args.block);
|
Box::pin(async move {
|
||||||
let connected_towards = match chest_props.r#type {
|
let chest_props = ChestLikeProperties::from_state_id(args.state.id, args.block);
|
||||||
ChestType::Single => return,
|
let connected_towards = match chest_props.r#type {
|
||||||
ChestType::Left => chest_props.facing.rotate_clockwise(),
|
ChestType::Single => return,
|
||||||
ChestType::Right => chest_props.facing.rotate_counter_clockwise(),
|
ChestType::Left => chest_props.facing.rotate_clockwise(),
|
||||||
};
|
ChestType::Right => chest_props.facing.rotate_counter_clockwise(),
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(mut neighbor_props) = get_chest_properties_if_can_connect(
|
if let Some(mut neighbor_props) = get_chest_properties_if_can_connect(
|
||||||
args.world,
|
args.world,
|
||||||
args.block,
|
args.block,
|
||||||
args.position,
|
args.position,
|
||||||
chest_props.facing,
|
chest_props.facing,
|
||||||
connected_towards,
|
connected_towards,
|
||||||
chest_props.r#type.opposite(),
|
chest_props.r#type.opposite(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
neighbor_props.r#type = ChestType::Single;
|
neighbor_props.r#type = ChestType::Single;
|
||||||
|
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
&args.position.offset(connected_towards.to_offset()),
|
&args.position.offset(connected_towards.to_offset()),
|
||||||
neighbor_props.to_state_id(args.block),
|
neighbor_props.to_state_id(args.block),
|
||||||
BlockFlags::NOTIFY_LISTENERS,
|
BlockFlags::NOTIFY_LISTENERS,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,16 +2,12 @@ use std::sync::{Arc, atomic::Ordering};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
block::{
|
block::{
|
||||||
registry::BlockActionResult,
|
BlockBehaviour, BlockFuture, BlockHitResult, GetComparatorOutputArgs, NormalUseArgs,
|
||||||
{
|
OnPlaceArgs, PlacedArgs, UseWithItemArgs, registry::BlockActionResult,
|
||||||
BlockBehaviour, BlockHitResult, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs,
|
|
||||||
PlacedArgs, UseWithItemArgs,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
entity::{EntityBase, player::Player},
|
entity::{EntityBase, player::Player},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
block_properties::{BlockProperties, ChiseledBookshelfLikeProperties, HorizontalFacing},
|
block_properties::{BlockProperties, ChiseledBookshelfLikeProperties, HorizontalFacing},
|
||||||
item::Item,
|
item::Item,
|
||||||
@ -31,98 +27,113 @@ use tokio::sync::Mutex;
|
|||||||
#[pumpkin_block("minecraft:chiseled_bookshelf")]
|
#[pumpkin_block("minecraft:chiseled_bookshelf")]
|
||||||
pub struct ChiseledBookshelfBlock;
|
pub struct ChiseledBookshelfBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for ChiseledBookshelfBlock {
|
impl BlockBehaviour for ChiseledBookshelfBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut properties = ChiseledBookshelfLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
|
let mut properties = ChiseledBookshelfLikeProperties::default(args.block);
|
||||||
|
|
||||||
// Face in the opposite direction the player is facing
|
// Face in the opposite direction the player is facing
|
||||||
properties.facing = args.player.get_entity().get_horizontal_facing().opposite();
|
properties.facing = args.player.get_entity().get_horizontal_facing().opposite();
|
||||||
|
|
||||||
properties.to_state_id(args.block)
|
properties.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
let state = args.world.get_block_state(args.position).await;
|
Box::pin(async move {
|
||||||
let properties = ChiseledBookshelfLikeProperties::from_state_id(state.id, args.block);
|
let state = args.world.get_block_state(args.position).await;
|
||||||
|
let properties = ChiseledBookshelfLikeProperties::from_state_id(state.id, args.block);
|
||||||
|
|
||||||
if let Some(slot) = Self::get_slot_for_hit(args.hit, properties.facing) {
|
if let Some(slot) = Self::get_slot_for_hit(args.hit, properties.facing) {
|
||||||
if Self::is_slot_used(properties, slot) {
|
if Self::is_slot_used(properties, slot) {
|
||||||
if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
||||||
|
&& let Some(block_entity) = block_entity
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<ChiseledBookshelfBlockEntity>()
|
||||||
|
{
|
||||||
|
Self::try_remove_book(
|
||||||
|
args.world,
|
||||||
|
args.player,
|
||||||
|
args.position,
|
||||||
|
block_entity,
|
||||||
|
properties,
|
||||||
|
slot,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return BlockActionResult::Success;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return BlockActionResult::Consume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BlockActionResult::Pass
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_with_item<'a>(
|
||||||
|
&'a self,
|
||||||
|
args: UseWithItemArgs<'a>,
|
||||||
|
) -> BlockFuture<'a, BlockActionResult> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let state = args.world.get_block_state(args.position).await;
|
||||||
|
let properties = ChiseledBookshelfLikeProperties::from_state_id(state.id, args.block);
|
||||||
|
|
||||||
|
if !args
|
||||||
|
.item_stack
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.get_item()
|
||||||
|
.has_tag(&tag::Item::MINECRAFT_BOOKSHELF_BOOKS)
|
||||||
|
{
|
||||||
|
return BlockActionResult::PassToDefaultBlockAction;
|
||||||
|
}
|
||||||
|
if let Some(slot) = Self::get_slot_for_hit(args.hit, properties.facing) {
|
||||||
|
if Self::is_slot_used(properties, slot) {
|
||||||
|
return BlockActionResult::PassToDefaultBlockAction;
|
||||||
|
} else if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
||||||
&& let Some(block_entity) = block_entity
|
&& let Some(block_entity) = block_entity
|
||||||
.as_any()
|
.as_any()
|
||||||
.downcast_ref::<ChiseledBookshelfBlockEntity>()
|
.downcast_ref::<ChiseledBookshelfBlockEntity>()
|
||||||
{
|
{
|
||||||
Self::try_remove_book(
|
Self::try_add_book(
|
||||||
args.world,
|
args.world,
|
||||||
args.player,
|
args.player,
|
||||||
args.position,
|
args.position,
|
||||||
block_entity,
|
block_entity,
|
||||||
properties,
|
properties,
|
||||||
slot,
|
slot,
|
||||||
|
args.item_stack,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
return BlockActionResult::Success;
|
return BlockActionResult::Success;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return BlockActionResult::Consume;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
BlockActionResult::Pass
|
BlockActionResult::Pass
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let state = args.world.get_block_state(args.position).await;
|
Box::pin(async move {
|
||||||
let properties = ChiseledBookshelfLikeProperties::from_state_id(state.id, args.block);
|
let block_entity = ChiseledBookshelfBlockEntity::new(*args.position);
|
||||||
|
args.world.add_block_entity(Arc::new(block_entity)).await;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if !args
|
fn get_comparator_output<'a>(
|
||||||
.item_stack
|
&'a self,
|
||||||
.lock()
|
args: GetComparatorOutputArgs<'a>,
|
||||||
.await
|
) -> BlockFuture<'a, Option<u8>> {
|
||||||
.get_item()
|
Box::pin(async move {
|
||||||
.has_tag(&tag::Item::MINECRAFT_BOOKSHELF_BOOKS)
|
if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
||||||
{
|
|
||||||
return BlockActionResult::PassToDefaultBlockAction;
|
|
||||||
}
|
|
||||||
if let Some(slot) = Self::get_slot_for_hit(args.hit, properties.facing) {
|
|
||||||
if Self::is_slot_used(properties, slot) {
|
|
||||||
return BlockActionResult::PassToDefaultBlockAction;
|
|
||||||
} else if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
|
||||||
&& let Some(block_entity) = block_entity
|
&& let Some(block_entity) = block_entity
|
||||||
.as_any()
|
.as_any()
|
||||||
.downcast_ref::<ChiseledBookshelfBlockEntity>()
|
.downcast_ref::<ChiseledBookshelfBlockEntity>()
|
||||||
{
|
{
|
||||||
Self::try_add_book(
|
return Some((block_entity.last_interacted_slot.load(Ordering::Relaxed) + 1) as u8);
|
||||||
args.world,
|
|
||||||
args.player,
|
|
||||||
args.position,
|
|
||||||
block_entity,
|
|
||||||
properties,
|
|
||||||
slot,
|
|
||||||
args.item_stack,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
return BlockActionResult::Success;
|
|
||||||
}
|
}
|
||||||
}
|
None
|
||||||
|
})
|
||||||
BlockActionResult::Pass
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn placed(&self, args: PlacedArgs<'_>) {
|
|
||||||
let block_entity = ChiseledBookshelfBlockEntity::new(*args.position);
|
|
||||||
args.world.add_block_entity(Arc::new(block_entity)).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option<u8> {
|
|
||||||
if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
|
||||||
&& let Some(block_entity) = block_entity
|
|
||||||
.as_any()
|
|
||||||
.downcast_ref::<ChiseledBookshelfBlockEntity>()
|
|
||||||
{
|
|
||||||
return Some((block_entity.last_interacted_slot.load(Ordering::Relaxed) + 1) as u8);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_util::{GameMode, math::position::BlockPos};
|
use pumpkin_util::{GameMode, math::position::BlockPos};
|
||||||
use pumpkin_world::{block::entities::command_block::CommandBlockEntity, tick::TickPriority};
|
use pumpkin_world::{block::entities::command_block::CommandBlockEntity, tick::TickPriority};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
block::{
|
block::{
|
||||||
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, OnNeighborUpdateArgs, OnScheduledTickArgs,
|
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, OnNeighborUpdateArgs,
|
||||||
|
OnScheduledTickArgs,
|
||||||
},
|
},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
@ -50,45 +50,49 @@ impl BlockMetadata for CommandBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for CommandBlock {
|
impl BlockBehaviour for CommandBlock {
|
||||||
async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) {
|
fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
if let Some(block_entity) = args.world.get_block_entity(args.position).await {
|
Box::pin(async move {
|
||||||
if block_entity.resource_location() != CommandBlockEntity::ID {
|
if let Some(block_entity) = args.world.get_block_entity(args.position).await {
|
||||||
return;
|
if block_entity.resource_location() != CommandBlockEntity::ID {
|
||||||
}
|
return;
|
||||||
let command_entity = block_entity
|
}
|
||||||
.as_any()
|
let command_entity = block_entity
|
||||||
.downcast_ref::<CommandBlockEntity>()
|
.as_any()
|
||||||
.unwrap();
|
.downcast_ref::<CommandBlockEntity>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
Self::update(
|
Self::update(
|
||||||
args.world,
|
args.world,
|
||||||
args.block,
|
args.block,
|
||||||
command_entity,
|
command_entity,
|
||||||
args.position,
|
args.position,
|
||||||
block_receives_redstone_power(args.world, args.position).await,
|
block_receives_redstone_power(args.world, args.position).await,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
Box::pin(async move {
|
||||||
&& block_entity.resource_location() != CommandBlockEntity::ID
|
if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
||||||
{
|
&& block_entity.resource_location() != CommandBlockEntity::ID
|
||||||
return;
|
{}
|
||||||
}
|
})
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
if let Some(player) = args.player
|
Box::pin(async move {
|
||||||
&& player.gamemode.load() == GameMode::Creative
|
if let Some(player) = args.player
|
||||||
{
|
&& player.gamemode.load() == GameMode::Creative
|
||||||
return true;
|
{
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,16 +2,12 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
block::{
|
block::{
|
||||||
registry::BlockActionResult,
|
BlockBehaviour, BlockFuture, GetComparatorOutputArgs, NormalUseArgs, OnScheduledTickArgs,
|
||||||
{
|
UseWithItemArgs, registry::BlockActionResult,
|
||||||
BlockBehaviour, GetComparatorOutputArgs, NormalUseArgs, OnScheduledTickArgs,
|
|
||||||
UseWithItemArgs,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
entity::{Entity, item::ItemEntity},
|
entity::{Entity, item::ItemEntity},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block,
|
Block,
|
||||||
block_properties::{BlockProperties, ComposterLikeProperties, EnumVariants, Integer0To8},
|
block_properties::{BlockProperties, ComposterLikeProperties, EnumVariants, Integer0To8},
|
||||||
@ -29,54 +25,79 @@ use uuid::Uuid;
|
|||||||
#[pumpkin_block("minecraft:composter")]
|
#[pumpkin_block("minecraft:composter")]
|
||||||
pub struct ComposterBlock;
|
pub struct ComposterBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for ComposterBlock {
|
impl BlockBehaviour for ComposterBlock {
|
||||||
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
Box::pin(async move {
|
||||||
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
if props.get_level() == 8 {
|
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
|
||||||
self.clear_composter(args.world, args.position, state_id, args.block)
|
if props.get_level() == 8 {
|
||||||
.await;
|
self.clear_composter(args.world, args.position, state_id, args.block)
|
||||||
}
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
BlockActionResult::Pass
|
BlockActionResult::Pass
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
|
fn use_with_item<'a>(
|
||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
&'a self,
|
||||||
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
|
args: UseWithItemArgs<'a>,
|
||||||
let level = props.get_level();
|
) -> BlockFuture<'a, BlockActionResult> {
|
||||||
if level == 8 {
|
Box::pin(async move {
|
||||||
self.clear_composter(args.world, args.position, state_id, args.block)
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
|
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
|
||||||
|
let level = props.get_level();
|
||||||
|
if level == 8 {
|
||||||
|
self.clear_composter(args.world, args.position, state_id, args.block)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
if level < 7
|
||||||
|
&& let Some(chance) =
|
||||||
|
get_composter_increase_chance_from_item_id(args.item_stack.lock().await.item.id)
|
||||||
|
&& (level == 0 || rand::rng().random_bool(f64::from(chance)))
|
||||||
|
{
|
||||||
|
self.update_level_composter(
|
||||||
|
args.world,
|
||||||
|
args.position,
|
||||||
|
state_id,
|
||||||
|
args.block,
|
||||||
|
level + 1,
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
args.world
|
||||||
if level < 7
|
.sync_world_event(WorldEvent::ComposterUsed, *args.position, 1)
|
||||||
&& let Some(chance) =
|
.await;
|
||||||
get_composter_increase_chance_from_item_id(args.item_stack.lock().await.item.id)
|
}
|
||||||
&& (level == 0 || rand::rng().random_bool(f64::from(chance)))
|
BlockActionResult::Consume
|
||||||
{
|
})
|
||||||
self.update_level_composter(args.world, args.position, state_id, args.block, level + 1)
|
|
||||||
.await;
|
|
||||||
args.world
|
|
||||||
.sync_world_event(WorldEvent::ComposterUsed, *args.position, 1)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
BlockActionResult::Consume
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
Box::pin(async move {
|
||||||
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
let level = props.get_level();
|
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
|
||||||
if level == 7 {
|
let level = props.get_level();
|
||||||
self.update_level_composter(args.world, args.position, state_id, args.block, level + 1)
|
if level == 7 {
|
||||||
|
self.update_level_composter(
|
||||||
|
args.world,
|
||||||
|
args.position,
|
||||||
|
state_id,
|
||||||
|
args.block,
|
||||||
|
level + 1,
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option<u8> {
|
fn get_comparator_output<'a>(
|
||||||
let props = ComposterLikeProperties::from_state_id(args.state.id, args.block);
|
&'a self,
|
||||||
Some(props.get_level())
|
args: GetComparatorOutputArgs<'a>,
|
||||||
|
) -> BlockFuture<'a, Option<u8>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let props = ComposterLikeProperties::from_state_id(args.state.id, args.block);
|
||||||
|
Some(props.get_level())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use crate::block::registry::BlockActionResult;
|
use crate::block::registry::BlockActionResult;
|
||||||
use crate::block::{BlockBehaviour, NormalUseArgs};
|
use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use pumpkin_inventory::crafting::crafting_screen_handler::CraftingTableScreenHandler;
|
use pumpkin_inventory::crafting::crafting_screen_handler::CraftingTableScreenHandler;
|
||||||
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
||||||
@ -12,14 +12,15 @@ use tokio::sync::Mutex;
|
|||||||
#[pumpkin_block("minecraft:crafting_table")]
|
#[pumpkin_block("minecraft:crafting_table")]
|
||||||
pub struct CraftingTableBlock;
|
pub struct CraftingTableBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for CraftingTableBlock {
|
impl BlockBehaviour for CraftingTableBlock {
|
||||||
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
args.player
|
Box::pin(async move {
|
||||||
.open_handled_screen(&CraftingTableScreenFactory)
|
args.player
|
||||||
.await;
|
.open_handled_screen(&CraftingTableScreenFactory)
|
||||||
|
.await;
|
||||||
|
|
||||||
BlockActionResult::Success
|
BlockActionResult::Success
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
use crate::block::BlockBehaviour;
|
use crate::block::BlockBehaviour;
|
||||||
|
use crate::block::BlockFuture;
|
||||||
use crate::block::CanPlaceAtArgs;
|
use crate::block::CanPlaceAtArgs;
|
||||||
use crate::block::GetStateForNeighborUpdateArgs;
|
use crate::block::GetStateForNeighborUpdateArgs;
|
||||||
use crate::block::OnPlaceArgs;
|
use crate::block::OnPlaceArgs;
|
||||||
use crate::block::OnScheduledTickArgs;
|
use crate::block::OnScheduledTickArgs;
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::BlockDirection;
|
use pumpkin_data::BlockDirection;
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
@ -16,41 +16,48 @@ use pumpkin_world::world::BlockFlags;
|
|||||||
#[pumpkin_block("minecraft:dirt_path")]
|
#[pumpkin_block("minecraft:dirt_path")]
|
||||||
pub struct DirtPathBlock;
|
pub struct DirtPathBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for DirtPathBlock {
|
impl BlockBehaviour for DirtPathBlock {
|
||||||
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
// TODO: push up entities
|
Box::pin(async move {
|
||||||
args.world
|
// TODO: push up entities
|
||||||
.set_block_state(
|
|
||||||
args.position,
|
|
||||||
Block::DIRT.default_state.id,
|
|
||||||
BlockFlags::NOTIFY_ALL,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
|
||||||
if !can_place_at(args.world, args.position).await {
|
|
||||||
return Block::DIRT.default_state.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.block.default_state.id
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
|
||||||
&self,
|
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
|
||||||
) -> BlockStateId {
|
|
||||||
if args.direction == BlockDirection::Up && !can_place_at(args.world, args.position).await {
|
|
||||||
args.world
|
args.world
|
||||||
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
.set_block_state(
|
||||||
|
args.position,
|
||||||
|
Block::DIRT.default_state.id,
|
||||||
|
BlockFlags::NOTIFY_ALL,
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
})
|
||||||
args.state_id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
can_place_at(args.block_accessor, args.position).await
|
Box::pin(async move {
|
||||||
|
if !can_place_at(args.world, args.position).await {
|
||||||
|
return Block::DIRT.default_state.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.block.default_state.id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_state_for_neighbor_update<'a>(
|
||||||
|
&'a self,
|
||||||
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
|
Box::pin(async move {
|
||||||
|
if args.direction == BlockDirection::Up
|
||||||
|
&& !can_place_at(args.world, args.position).await
|
||||||
|
{
|
||||||
|
args.world
|
||||||
|
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
args.state_id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
|
Box::pin(async move { can_place_at(args.block_accessor, args.position).await })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::BlockDirection;
|
use pumpkin_data::BlockDirection;
|
||||||
use pumpkin_data::HorizontalFacingExt;
|
use pumpkin_data::HorizontalFacingExt;
|
||||||
use pumpkin_data::block_properties::Axis;
|
use pumpkin_data::block_properties::Axis;
|
||||||
@ -20,6 +19,7 @@ use pumpkin_world::world::BlockFlags;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::block::BlockBehaviour;
|
use crate::block::BlockBehaviour;
|
||||||
|
use crate::block::BlockFuture;
|
||||||
use crate::block::CanPlaceAtArgs;
|
use crate::block::CanPlaceAtArgs;
|
||||||
use crate::block::GetStateForNeighborUpdateArgs;
|
use crate::block::GetStateForNeighborUpdateArgs;
|
||||||
use crate::block::NormalUseArgs;
|
use crate::block::NormalUseArgs;
|
||||||
@ -163,125 +163,136 @@ async fn get_hinge(
|
|||||||
#[pumpkin_block_from_tag("minecraft:doors")]
|
#[pumpkin_block_from_tag("minecraft:doors")]
|
||||||
pub struct DoorBlock;
|
pub struct DoorBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for DoorBlock {
|
impl BlockBehaviour for DoorBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let powered = block_receives_redstone_power(args.world, args.position).await
|
Box::pin(async move {
|
||||||
|| block_receives_redstone_power(args.world, &args.position.up()).await;
|
let powered = block_receives_redstone_power(args.world, args.position).await
|
||||||
|
|| block_receives_redstone_power(args.world, &args.position.up()).await;
|
||||||
|
|
||||||
let direction = args.player.living_entity.entity.get_horizontal_facing();
|
let direction = args.player.living_entity.entity.get_horizontal_facing();
|
||||||
let hinge = get_hinge(args.world, args.position, args.use_item_on, direction).await;
|
let hinge = get_hinge(args.world, args.position, args.use_item_on, direction).await;
|
||||||
|
|
||||||
let mut door_props = DoorProperties::default(args.block);
|
let mut door_props = DoorProperties::default(args.block);
|
||||||
door_props.half = DoubleBlockHalf::Lower;
|
door_props.half = DoubleBlockHalf::Lower;
|
||||||
door_props.facing = direction;
|
door_props.facing = direction;
|
||||||
door_props.hinge = hinge;
|
door_props.hinge = hinge;
|
||||||
door_props.powered = powered;
|
door_props.powered = powered;
|
||||||
door_props.open = powered;
|
door_props.open = powered;
|
||||||
|
|
||||||
door_props.to_state_id(args.block)
|
door_props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
can_place_at(args.block_accessor, args.position).await
|
Box::pin(async move { can_place_at(args.block_accessor, args.position).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn placed(&self, args: PlacedArgs<'_>) {
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let mut door_props = DoorProperties::from_state_id(args.state_id, args.block);
|
Box::pin(async move {
|
||||||
door_props.half = DoubleBlockHalf::Upper;
|
let mut door_props = DoorProperties::from_state_id(args.state_id, args.block);
|
||||||
|
door_props.half = DoubleBlockHalf::Upper;
|
||||||
|
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
&args.position.offset(BlockDirection::Up.to_offset()),
|
&args.position.offset(BlockDirection::Up.to_offset()),
|
||||||
door_props.to_state_id(args.block),
|
door_props.to_state_id(args.block),
|
||||||
BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK,
|
BlockFlags::NOTIFY_ALL | BlockFlags::SKIP_BLOCK_ADDED_CALLBACK,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
if !can_open_door(args.block) {
|
Box::pin(async move {
|
||||||
return BlockActionResult::Pass;
|
if !can_open_door(args.block) {
|
||||||
}
|
return BlockActionResult::Pass;
|
||||||
|
}
|
||||||
|
|
||||||
toggle_door(args.player, args.world, args.position).await;
|
toggle_door(args.player, args.world, args.position).await;
|
||||||
|
|
||||||
BlockActionResult::Success
|
BlockActionResult::Success
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) {
|
fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let block_state = args.world.get_block_state(args.position).await;
|
Box::pin(async move {
|
||||||
let mut door_props = DoorProperties::from_state_id(block_state.id, args.block);
|
let block_state = args.world.get_block_state(args.position).await;
|
||||||
|
let mut door_props = DoorProperties::from_state_id(block_state.id, args.block);
|
||||||
|
|
||||||
let other_half = match door_props.half {
|
let other_half = match door_props.half {
|
||||||
DoubleBlockHalf::Upper => BlockDirection::Down,
|
DoubleBlockHalf::Upper => BlockDirection::Down,
|
||||||
DoubleBlockHalf::Lower => BlockDirection::Up,
|
DoubleBlockHalf::Lower => BlockDirection::Up,
|
||||||
};
|
};
|
||||||
let other_pos = args.position.offset(other_half.to_offset());
|
let other_pos = args.position.offset(other_half.to_offset());
|
||||||
let (other_block, other_state_id) = args.world.get_block_and_state_id(&other_pos).await;
|
let (other_block, other_state_id) = args.world.get_block_and_state_id(&other_pos).await;
|
||||||
|
|
||||||
let powered = block_receives_redstone_power(args.world, args.position).await
|
let powered = block_receives_redstone_power(args.world, args.position).await
|
||||||
|| block_receives_redstone_power(args.world, &other_pos).await;
|
|| block_receives_redstone_power(args.world, &other_pos).await;
|
||||||
|
|
||||||
if args.block.id == other_block.id && powered != door_props.powered {
|
if args.block.id == other_block.id && powered != door_props.powered {
|
||||||
let mut other_door_props = DoorProperties::from_state_id(other_state_id, other_block);
|
let mut other_door_props =
|
||||||
door_props.powered = !door_props.powered;
|
DoorProperties::from_state_id(other_state_id, other_block);
|
||||||
other_door_props.powered = door_props.powered;
|
door_props.powered = !door_props.powered;
|
||||||
|
other_door_props.powered = door_props.powered;
|
||||||
|
|
||||||
if powered != door_props.open {
|
if powered != door_props.open {
|
||||||
door_props.open = door_props.powered;
|
door_props.open = door_props.powered;
|
||||||
other_door_props.open = other_door_props.powered;
|
other_door_props.open = other_door_props.powered;
|
||||||
|
|
||||||
|
args.world
|
||||||
|
.play_block_sound(
|
||||||
|
get_sound(args.block, powered),
|
||||||
|
SoundCategory::Blocks,
|
||||||
|
*args.position,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
args.world
|
args.world
|
||||||
.play_block_sound(
|
.set_block_state(
|
||||||
get_sound(args.block, powered),
|
args.position,
|
||||||
SoundCategory::Blocks,
|
door_props.to_state_id(args.block),
|
||||||
*args.position,
|
BlockFlags::NOTIFY_LISTENERS,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
args.world
|
||||||
|
.set_block_state(
|
||||||
|
&other_pos,
|
||||||
|
other_door_props.to_state_id(other_block),
|
||||||
|
BlockFlags::NOTIFY_LISTENERS,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
})
|
||||||
args.world
|
|
||||||
.set_block_state(
|
|
||||||
args.position,
|
|
||||||
door_props.to_state_id(args.block),
|
|
||||||
BlockFlags::NOTIFY_LISTENERS,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
args.world
|
|
||||||
.set_block_state(
|
|
||||||
&other_pos,
|
|
||||||
other_door_props.to_state_id(other_block),
|
|
||||||
BlockFlags::NOTIFY_LISTENERS,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
let lv = DoorProperties::from_state_id(args.state_id, args.block).half;
|
Box::pin(async move {
|
||||||
if args.direction.to_axis() != Axis::Y
|
let lv = DoorProperties::from_state_id(args.state_id, args.block).half;
|
||||||
|| (lv == DoubleBlockHalf::Lower) != (args.direction == BlockDirection::Up)
|
if args.direction.to_axis() != Axis::Y
|
||||||
{
|
|| (lv == DoubleBlockHalf::Lower) != (args.direction == BlockDirection::Up)
|
||||||
if lv == DoubleBlockHalf::Lower
|
|
||||||
&& args.direction == BlockDirection::Down
|
|
||||||
&& !can_place_at(args.world, args.position).await
|
|
||||||
{
|
{
|
||||||
|
if lv == DoubleBlockHalf::Lower
|
||||||
|
&& args.direction == BlockDirection::Down
|
||||||
|
&& !can_place_at(args.world, args.position).await
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else if Block::from_state_id(args.neighbor_state_id).id == args.block.id
|
||||||
|
&& DoorProperties::from_state_id(args.neighbor_state_id, args.block).half != lv
|
||||||
|
{
|
||||||
|
let mut new_state =
|
||||||
|
DoorProperties::from_state_id(args.neighbor_state_id, args.block);
|
||||||
|
new_state.half = lv;
|
||||||
|
return new_state.to_state_id(args.block);
|
||||||
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else if Block::from_state_id(args.neighbor_state_id).id == args.block.id
|
args.state_id
|
||||||
&& DoorProperties::from_state_id(args.neighbor_state_id, args.block).half != lv
|
})
|
||||||
{
|
|
||||||
let mut new_state = DoorProperties::from_state_id(args.neighbor_state_id, args.block);
|
|
||||||
new_state.half = lv;
|
|
||||||
return new_state.to_state_id(args.block);
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
args.state_id
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::block::BlockBehaviour;
|
use crate::block::BlockBehaviour;
|
||||||
|
use crate::block::BlockFuture;
|
||||||
use crate::block::OnEntityCollisionArgs;
|
use crate::block::OnEntityCollisionArgs;
|
||||||
use crate::block::PlacedArgs;
|
use crate::block::PlacedArgs;
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_registry::VanillaDimensionType;
|
use pumpkin_registry::VanillaDimensionType;
|
||||||
use pumpkin_world::block::entities::end_portal::EndPortalBlockEntity;
|
use pumpkin_world::block::entities::end_portal::EndPortalBlockEntity;
|
||||||
@ -11,27 +11,30 @@ use pumpkin_world::block::entities::end_portal::EndPortalBlockEntity;
|
|||||||
#[pumpkin_block("minecraft:end_portal")]
|
#[pumpkin_block("minecraft:end_portal")]
|
||||||
pub struct EndPortalBlock;
|
pub struct EndPortalBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for EndPortalBlock {
|
impl BlockBehaviour for EndPortalBlock {
|
||||||
async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) {
|
fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let world = if args.world.dimension_type == VanillaDimensionType::TheEnd {
|
Box::pin(async move {
|
||||||
args.server
|
let world = if args.world.dimension_type == VanillaDimensionType::TheEnd {
|
||||||
.get_world_from_dimension(VanillaDimensionType::Overworld)
|
args.server
|
||||||
.await
|
.get_world_from_dimension(VanillaDimensionType::Overworld)
|
||||||
} else {
|
.await
|
||||||
args.server
|
} else {
|
||||||
.get_world_from_dimension(VanillaDimensionType::TheEnd)
|
args.server
|
||||||
.await
|
.get_world_from_dimension(VanillaDimensionType::TheEnd)
|
||||||
};
|
.await
|
||||||
args.entity
|
};
|
||||||
.get_entity()
|
args.entity
|
||||||
.try_use_portal(0, world, *args.position)
|
.get_entity()
|
||||||
.await;
|
.try_use_portal(0, world, *args.position)
|
||||||
|
.await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn placed(&self, args: PlacedArgs<'_>) {
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
args.world
|
Box::pin(async move {
|
||||||
.add_block_entity(Arc::new(EndPortalBlockEntity::new(*args.position)))
|
args.world
|
||||||
.await;
|
.add_block_entity(Arc::new(EndPortalBlockEntity::new(*args.position)))
|
||||||
|
.await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
block::{BlockBehaviour, OnPlaceArgs},
|
block::{BlockBehaviour, BlockFuture, OnPlaceArgs},
|
||||||
entity::EntityBase,
|
entity::EntityBase,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -13,12 +12,14 @@ type EndPortalFrameProperties = pumpkin_data::block_properties::EndPortalFrameLi
|
|||||||
#[pumpkin_block("minecraft:end_portal_frame")]
|
#[pumpkin_block("minecraft:end_portal_frame")]
|
||||||
pub struct EndPortalFrameBlock;
|
pub struct EndPortalFrameBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for EndPortalFrameBlock {
|
impl BlockBehaviour for EndPortalFrameBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut end_portal_frame_props = EndPortalFrameProperties::default(args.block);
|
Box::pin(async move {
|
||||||
end_portal_frame_props.facing = args.player.get_entity().get_horizontal_facing().opposite();
|
let mut end_portal_frame_props = EndPortalFrameProperties::default(args.block);
|
||||||
|
end_portal_frame_props.facing =
|
||||||
|
args.player.get_entity().get_horizontal_facing().opposite();
|
||||||
|
|
||||||
end_portal_frame_props.to_state_id(args.block)
|
end_portal_frame_props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
use crate::block::BlockFuture;
|
||||||
use crate::block::{BlockBehaviour, OnPlaceArgs};
|
use crate::block::{BlockBehaviour, OnPlaceArgs};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
use pumpkin_data::block_properties::EndRodLikeProperties;
|
use pumpkin_data::block_properties::EndRodLikeProperties;
|
||||||
@ -9,25 +9,26 @@ use pumpkin_world::BlockStateId;
|
|||||||
#[pumpkin_block("minecraft:end_rod")]
|
#[pumpkin_block("minecraft:end_rod")]
|
||||||
pub struct EndRodBlock;
|
pub struct EndRodBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for EndRodBlock {
|
impl BlockBehaviour for EndRodBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut props = EndRodLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
|
let mut props = EndRodLikeProperties::default(args.block);
|
||||||
|
|
||||||
let blockstate = args
|
let blockstate = args
|
||||||
.world
|
.world
|
||||||
.get_block_state_id(&args.position.offset(args.direction.to_offset()))
|
.get_block_state_id(&args.position.offset(args.direction.to_offset()))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if Block::from_state_id(blockstate).eq(args.block)
|
if Block::from_state_id(blockstate).eq(args.block)
|
||||||
&& EndRodLikeProperties::from_state_id(blockstate, args.block).facing
|
&& EndRodLikeProperties::from_state_id(blockstate, args.block).facing
|
||||||
== args.direction.to_facing().opposite()
|
== args.direction.to_facing().opposite()
|
||||||
{
|
{
|
||||||
props.facing = args.direction.to_facing();
|
props.facing = args.direction.to_facing();
|
||||||
} else {
|
} else {
|
||||||
props.facing = args.direction.to_facing().opposite();
|
props.facing = args.direction.to_facing().opposite();
|
||||||
}
|
}
|
||||||
|
|
||||||
props.to_state_id(args.block)
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs,
|
BlockBehaviour, BlockFuture, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs,
|
||||||
registry::BlockActionResult,
|
registry::BlockActionResult,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
@ -41,46 +41,56 @@ impl ScreenHandlerFactory for EnderChestScreenFactory {
|
|||||||
#[pumpkin_block("minecraft:ender_chest")]
|
#[pumpkin_block("minecraft:ender_chest")]
|
||||||
pub struct EnderChestBlock;
|
pub struct EnderChestBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for EnderChestBlock {
|
impl BlockBehaviour for EnderChestBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut props = LadderLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
props.facing = args
|
let mut props = LadderLikeProperties::default(args.block);
|
||||||
.player
|
props.facing = args
|
||||||
.living_entity
|
.player
|
||||||
.entity
|
.living_entity
|
||||||
.get_horizontal_facing()
|
.entity
|
||||||
.opposite();
|
.get_horizontal_facing()
|
||||||
props.to_state_id(args.block)
|
.opposite();
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool {
|
fn on_synced_block_event<'a>(
|
||||||
// On the server, we don't need to do more because the client is responsible for that.
|
&'a self,
|
||||||
args.r#type == Self::LID_ANIMATION_EVENT_TYPE
|
args: OnSyncedBlockEventArgs<'a>,
|
||||||
|
) -> BlockFuture<'a, bool> {
|
||||||
|
Box::pin(async move {
|
||||||
|
// On the server, we don't need to do more because the client is responsible for that.
|
||||||
|
args.r#type == Self::LID_ANIMATION_EVENT_TYPE
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
Box::pin(async move {
|
||||||
&& let Some(block_entity) = block_entity
|
if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
||||||
.as_any()
|
&& let Some(block_entity) = block_entity
|
||||||
.downcast_ref::<EnderChestBlockEntity>()
|
.as_any()
|
||||||
{
|
.downcast_ref::<EnderChestBlockEntity>()
|
||||||
let inventory = args.player.ender_chest_inventory();
|
{
|
||||||
inventory.set_tracker(block_entity.get_tracker()).await;
|
let inventory = args.player.ender_chest_inventory();
|
||||||
args.player
|
inventory.set_tracker(block_entity.get_tracker()).await;
|
||||||
.open_handled_screen(&EnderChestScreenFactory(inventory.clone()))
|
args.player
|
||||||
.await;
|
.open_handled_screen(&EnderChestScreenFactory(inventory.clone()))
|
||||||
|
.await;
|
||||||
|
|
||||||
// TODO: player.incrementStat(Stats.OPEN_ENDERCHEST);
|
// TODO: player.incrementStat(Stats.OPEN_ENDERCHEST);
|
||||||
// TODO: PiglinBrain.onGuardedBlockInteracted(serverWorld, player, true);
|
// TODO: PiglinBrain.onGuardedBlockInteracted(serverWorld, player, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockActionResult::Success
|
BlockActionResult::Success
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn placed(&self, args: PlacedArgs<'_>) {
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let block_entity = EnderChestBlockEntity::new(*args.position);
|
Box::pin(async move {
|
||||||
args.world.add_block_entity(Arc::new(block_entity)).await;
|
let block_entity = EnderChestBlockEntity::new(*args.position);
|
||||||
|
args.world.add_block_entity(Arc::new(block_entity)).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
block::{
|
block::{
|
||||||
BlockBehaviour, BlockMetadata, GetStateForNeighborUpdateArgs, OnScheduledTickArgs,
|
BlockBehaviour, BlockFuture, BlockMetadata, GetStateForNeighborUpdateArgs,
|
||||||
PlacedArgs,
|
OnScheduledTickArgs, PlacedArgs,
|
||||||
},
|
},
|
||||||
entity::falling::FallingEntity,
|
entity::falling::FallingEntity,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockState,
|
Block, BlockState,
|
||||||
tag::{self, Taggable},
|
tag::{self, Taggable},
|
||||||
@ -33,30 +32,36 @@ impl BlockMetadata for FallingBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for FallingBlock {
|
impl BlockBehaviour for FallingBlock {
|
||||||
async fn placed(&self, args: PlacedArgs<'_>) {
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
// TODO: make delay configurable
|
Box::pin(async move {
|
||||||
args.world
|
// TODO: make delay configurable
|
||||||
.schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal)
|
args.world
|
||||||
.await;
|
.schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal)
|
||||||
|
.await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
// TODO: make delay configurable
|
Box::pin(async move {
|
||||||
args.world
|
// TODO: make delay configurable
|
||||||
.schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal)
|
args.world
|
||||||
.await;
|
.schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal)
|
||||||
args.state_id
|
.await;
|
||||||
|
args.state_id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let (block, state) = args.world.get_block_and_state(&args.position.down()).await;
|
Box::pin(async move {
|
||||||
if !Self::can_fall_through(state, block) || args.position.0.y < args.world.min_y {
|
let (block, state) = args.world.get_block_and_state(&args.position.down()).await;
|
||||||
return;
|
if !Self::can_fall_through(state, block) || args.position.0.y < args.world.min_y {
|
||||||
}
|
return;
|
||||||
FallingEntity::replace_spawn(args.world, *args.position, args.block.default_state.id).await;
|
}
|
||||||
|
FallingEntity::replace_spawn(args.world, *args.position, args.block.default_state.id)
|
||||||
|
.await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::block::BlockBehaviour;
|
use crate::block::BlockBehaviour;
|
||||||
|
use crate::block::BlockFuture;
|
||||||
use crate::block::CanPlaceAtArgs;
|
use crate::block::CanPlaceAtArgs;
|
||||||
use crate::block::GetStateForNeighborUpdateArgs;
|
use crate::block::GetStateForNeighborUpdateArgs;
|
||||||
use crate::block::OnPlaceArgs;
|
use crate::block::OnPlaceArgs;
|
||||||
use crate::block::OnScheduledTickArgs;
|
use crate::block::OnScheduledTickArgs;
|
||||||
use crate::block::RandomTickArgs;
|
use crate::block::RandomTickArgs;
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::BlockDirection;
|
use pumpkin_data::BlockDirection;
|
||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
@ -29,75 +29,55 @@ type FarmlandProperties = FarmlandLikeProperties;
|
|||||||
#[pumpkin_block("minecraft:farmland")]
|
#[pumpkin_block("minecraft:farmland")]
|
||||||
pub struct FarmlandBlock;
|
pub struct FarmlandBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for FarmlandBlock {
|
impl BlockBehaviour for FarmlandBlock {
|
||||||
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
// TODO: push up entities
|
Box::pin(async move {
|
||||||
args.world
|
// TODO: push up entities
|
||||||
.set_block_state(
|
|
||||||
args.position,
|
|
||||||
Block::DIRT.default_state.id,
|
|
||||||
BlockFlags::NOTIFY_ALL,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
|
||||||
if !can_place_at(args.world, args.position).await {
|
|
||||||
return Block::DIRT.default_state.id;
|
|
||||||
}
|
|
||||||
args.block.default_state.id
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
|
||||||
&self,
|
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
|
||||||
) -> BlockStateId {
|
|
||||||
if args.direction == BlockDirection::Up && !can_place_at(args.world, args.position).await {
|
|
||||||
args.world
|
|
||||||
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
args.state_id
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
|
||||||
can_place_at(args.block_accessor, args.position).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn random_tick(&self, args: RandomTickArgs<'_>) {
|
|
||||||
// TODO: add rain check. Remember to check which one is most optimized.
|
|
||||||
if is_water_nearby(args.world, args.position).await {
|
|
||||||
let mut props = FarmlandProperties::default(args.block);
|
|
||||||
props.moisture = Integer0To7::L7;
|
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
props.to_state_id(args.block),
|
Block::DIRT.default_state.id,
|
||||||
BlockFlags::NOTIFY_NEIGHBORS,
|
BlockFlags::NOTIFY_ALL,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
} else {
|
})
|
||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
}
|
||||||
let mut props = FarmlandProperties::from_state_id(state_id, args.block);
|
|
||||||
if props.moisture == Integer0To7::L0 {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
if !args
|
Box::pin(async move {
|
||||||
.world
|
if !can_place_at(args.world, args.position).await {
|
||||||
.get_block(&args.position.up())
|
return Block::DIRT.default_state.id;
|
||||||
.await
|
}
|
||||||
.has_tag(&tag::Block::MINECRAFT_MAINTAINS_FARMLAND)
|
args.block.default_state.id
|
||||||
{
|
})
|
||||||
//TODO push entities up
|
}
|
||||||
args.world
|
|
||||||
.set_block_state(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
args.position,
|
&'a self,
|
||||||
Block::DIRT.default_state.id,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
BlockFlags::NOTIFY_NEIGHBORS,
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
)
|
Box::pin(async move {
|
||||||
.await;
|
if args.direction == BlockDirection::Up
|
||||||
}
|
&& !can_place_at(args.world, args.position).await
|
||||||
} else {
|
{
|
||||||
props.moisture = Integer0To7::from_index(props.moisture.to_index() - 1);
|
args.world
|
||||||
|
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
args.state_id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
|
Box::pin(async move { can_place_at(args.block_accessor, args.position).await })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
// TODO: add rain check. Remember to check which one is most optimized.
|
||||||
|
if is_water_nearby(args.world, args.position).await {
|
||||||
|
let mut props = FarmlandProperties::default(args.block);
|
||||||
|
props.moisture = Integer0To7::L7;
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
@ -105,8 +85,37 @@ impl BlockBehaviour for FarmlandBlock {
|
|||||||
BlockFlags::NOTIFY_NEIGHBORS,
|
BlockFlags::NOTIFY_NEIGHBORS,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
} else {
|
||||||
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
|
let mut props = FarmlandProperties::from_state_id(state_id, args.block);
|
||||||
|
if props.moisture == Integer0To7::L0 {
|
||||||
|
if !args
|
||||||
|
.world
|
||||||
|
.get_block(&args.position.up())
|
||||||
|
.await
|
||||||
|
.has_tag(&tag::Block::MINECRAFT_MAINTAINS_FARMLAND)
|
||||||
|
{
|
||||||
|
//TODO push entities up
|
||||||
|
args.world
|
||||||
|
.set_block_state(
|
||||||
|
args.position,
|
||||||
|
Block::DIRT.default_state.id,
|
||||||
|
BlockFlags::NOTIFY_NEIGHBORS,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
props.moisture = Integer0To7::from_index(props.moisture.to_index() - 1);
|
||||||
|
args.world
|
||||||
|
.set_block_state(
|
||||||
|
args.position,
|
||||||
|
props.to_state_id(args.block),
|
||||||
|
BlockFlags::NOTIFY_NEIGHBORS,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::block::BlockFuture;
|
||||||
use crate::block::GetStateForNeighborUpdateArgs;
|
use crate::block::GetStateForNeighborUpdateArgs;
|
||||||
use crate::block::NormalUseArgs;
|
use crate::block::NormalUseArgs;
|
||||||
use crate::block::OnPlaceArgs;
|
use crate::block::OnPlaceArgs;
|
||||||
use crate::entity::player::Player;
|
use crate::entity::player::Player;
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
use pumpkin_data::tag;
|
use pumpkin_data::tag;
|
||||||
use pumpkin_data::tag::RegistryKey;
|
use pumpkin_data::tag::RegistryKey;
|
||||||
@ -57,26 +57,31 @@ pub async fn toggle_fence_gate(
|
|||||||
#[pumpkin_block_from_tag("minecraft:fence_gates")]
|
#[pumpkin_block_from_tag("minecraft:fence_gates")]
|
||||||
pub struct FenceGateBlock;
|
pub struct FenceGateBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for FenceGateBlock {
|
impl BlockBehaviour for FenceGateBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut fence_gate_props = FenceGateProperties::default(args.block);
|
Box::pin(async move {
|
||||||
fence_gate_props.facing = args.player.living_entity.entity.get_horizontal_facing();
|
let mut fence_gate_props = FenceGateProperties::default(args.block);
|
||||||
fence_gate_props.to_state_id(args.block)
|
fence_gate_props.facing = args.player.living_entity.entity.get_horizontal_facing();
|
||||||
|
fence_gate_props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
let fence_props = is_in_wall(&args).await;
|
Box::pin(async move {
|
||||||
fence_props.to_state_id(args.block)
|
let fence_props = is_in_wall(&args).await;
|
||||||
|
fence_props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
toggle_fence_gate(args.world, args.position, args.player).await;
|
Box::pin(async move {
|
||||||
|
toggle_fence_gate(args.world, args.position, args.player).await;
|
||||||
|
|
||||||
BlockActionResult::Success
|
BlockActionResult::Success
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
use crate::block::BlockFuture;
|
||||||
use crate::block::GetStateForNeighborUpdateArgs;
|
use crate::block::GetStateForNeighborUpdateArgs;
|
||||||
use crate::block::OnPlaceArgs;
|
use crate::block::OnPlaceArgs;
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::BlockDirection;
|
use pumpkin_data::BlockDirection;
|
||||||
use pumpkin_data::BlockState;
|
use pumpkin_data::BlockState;
|
||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
@ -28,21 +28,24 @@ impl BlockMetadata for FenceBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for FenceBlock {
|
impl BlockBehaviour for FenceBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut fence_props = FenceProperties::default(args.block);
|
Box::pin(async move {
|
||||||
fence_props.waterlogged = args.replacing.water_source();
|
let mut fence_props = FenceProperties::default(args.block);
|
||||||
|
fence_props.waterlogged = args.replacing.water_source();
|
||||||
|
|
||||||
compute_fence_state(fence_props, args.world, args.block, args.position).await
|
compute_fence_state(fence_props, args.world, args.block, args.position).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
let fence_props = FenceProperties::from_state_id(args.state_id, args.block);
|
Box::pin(async move {
|
||||||
compute_fence_state(fence_props, args.world, args.block, args.position).await
|
let fence_props = FenceProperties::from_state_id(args.state_id, args.block);
|
||||||
|
compute_fence_state(fence_props, args.world, args.block, args.position).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,6 @@ use rand::Rng;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::{Block, BlockDirection, BlockState};
|
use pumpkin_data::{Block, BlockDirection, BlockState};
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
@ -17,7 +16,7 @@ use pumpkin_world::tick::TickPriority;
|
|||||||
|
|
||||||
use crate::block::blocks::tnt::TNTBlock;
|
use crate::block::blocks::tnt::TNTBlock;
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
BlockBehaviour, BlockFuture, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
||||||
OnEntityCollisionArgs, OnScheduledTickArgs, PlacedArgs,
|
OnEntityCollisionArgs, OnScheduledTickArgs, PlacedArgs,
|
||||||
};
|
};
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
@ -165,144 +164,125 @@ impl FireBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for FireBlock {
|
impl BlockBehaviour for FireBlock {
|
||||||
async fn placed(&self, args: PlacedArgs<'_>) {
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
if args.old_state_id == args.state_id {
|
Box::pin(async move {
|
||||||
// Already a fire
|
if args.old_state_id == args.state_id {
|
||||||
return;
|
// Already a fire
|
||||||
}
|
return;
|
||||||
|
|
||||||
let dimension = args.world.dimension_type;
|
|
||||||
// First lets check if we are in OverWorld or Nether, its not possible to place an Nether portal in other dimensions in Vanilla
|
|
||||||
if (dimension == VanillaDimensionType::Overworld
|
|
||||||
|| dimension == VanillaDimensionType::TheNether)
|
|
||||||
&& let Some(portal) =
|
|
||||||
NetherPortal::get_new_portal(args.world, args.position, HorizontalAxis::X).await
|
|
||||||
{
|
|
||||||
portal.create(args.world).await;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.world
|
|
||||||
.schedule_block_tick(
|
|
||||||
args.block,
|
|
||||||
*args.position,
|
|
||||||
Self::get_fire_tick_delay() as u8,
|
|
||||||
TickPriority::Normal,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) {
|
|
||||||
let base_entity = args.entity.get_entity();
|
|
||||||
if !base_entity.entity_type.fire_immune {
|
|
||||||
let ticks = base_entity.fire_ticks.load(Ordering::Relaxed);
|
|
||||||
if ticks < 0 {
|
|
||||||
base_entity.fire_ticks.store(ticks + 1, Ordering::Relaxed);
|
|
||||||
} else if base_entity.entity_type == &EntityType::PLAYER {
|
|
||||||
let rnd_ticks = rand::rng().random_range(1..3);
|
|
||||||
base_entity
|
|
||||||
.fire_ticks
|
|
||||||
.store(ticks + rnd_ticks, Ordering::Relaxed);
|
|
||||||
}
|
}
|
||||||
if base_entity.fire_ticks.load(Ordering::Relaxed) >= 0 {
|
|
||||||
base_entity.set_on_fire_for(8.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
let dimension = args.world.dimension_type;
|
||||||
&self,
|
// First lets check if we are in OverWorld or Nether, its not possible to place an Nether portal in other dimensions in Vanilla
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
if (dimension == VanillaDimensionType::Overworld
|
||||||
) -> BlockStateId {
|
|| dimension == VanillaDimensionType::TheNether)
|
||||||
if self
|
&& let Some(portal) =
|
||||||
.can_place_at(CanPlaceAtArgs {
|
NetherPortal::get_new_portal(args.world, args.position, HorizontalAxis::X).await
|
||||||
server: None,
|
{
|
||||||
world: Some(args.world),
|
portal.create(args.world).await;
|
||||||
block_accessor: args.world,
|
return;
|
||||||
block: &Block::FIRE,
|
}
|
||||||
position: args.position,
|
|
||||||
direction: BlockDirection::Up,
|
args.world
|
||||||
player: None,
|
.schedule_block_tick(
|
||||||
use_item_on: None,
|
args.block,
|
||||||
})
|
*args.position,
|
||||||
.await
|
Self::get_fire_tick_delay() as u8,
|
||||||
{
|
TickPriority::Normal,
|
||||||
let old_fire_props = FireProperties::from_state_id(args.state_id, &Block::FIRE);
|
)
|
||||||
let fire_state_id = self
|
|
||||||
.get_state_for_position(args.world, &Block::FIRE, args.position)
|
|
||||||
.await;
|
.await;
|
||||||
let mut fire_props = FireProperties::from_state_id(fire_state_id, &Block::FIRE);
|
})
|
||||||
fire_props.age = EnumVariants::from_index(old_fire_props.age.to_index());
|
|
||||||
return fire_props.to_state_id(&Block::FIRE);
|
|
||||||
}
|
|
||||||
Block::AIR.default_state.id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let state = args
|
Box::pin(async move {
|
||||||
.block_accessor
|
let base_entity = args.entity.get_entity();
|
||||||
.get_block_state(&args.position.down())
|
if !base_entity.entity_type.fire_immune {
|
||||||
.await;
|
let ticks = base_entity.fire_ticks.load(Ordering::Relaxed);
|
||||||
if state.is_side_solid(BlockDirection::Up) {
|
if ticks < 0 {
|
||||||
return true;
|
base_entity.fire_ticks.store(ticks + 1, Ordering::Relaxed);
|
||||||
}
|
} else if base_entity.entity_type == &EntityType::PLAYER {
|
||||||
self.are_blocks_around_flammable(args.block_accessor, args.position)
|
let rnd_ticks = rand::rng().random_range(1..3);
|
||||||
.await
|
base_entity
|
||||||
|
.fire_ticks
|
||||||
|
.store(ticks + rnd_ticks, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
if base_entity.fire_ticks.load(Ordering::Relaxed) >= 0 {
|
||||||
|
base_entity.set_on_fire_for(8.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_state_for_neighbor_update<'a>(
|
||||||
|
&'a self,
|
||||||
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
|
Box::pin(async move {
|
||||||
|
if self
|
||||||
|
.can_place_at(CanPlaceAtArgs {
|
||||||
|
server: None,
|
||||||
|
world: Some(args.world),
|
||||||
|
block_accessor: args.world,
|
||||||
|
block: &Block::FIRE,
|
||||||
|
position: args.position,
|
||||||
|
direction: BlockDirection::Up,
|
||||||
|
player: None,
|
||||||
|
use_item_on: None,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
let old_fire_props = FireProperties::from_state_id(args.state_id, &Block::FIRE);
|
||||||
|
let fire_state_id = self
|
||||||
|
.get_state_for_position(args.world, &Block::FIRE, args.position)
|
||||||
|
.await;
|
||||||
|
let mut fire_props = FireProperties::from_state_id(fire_state_id, &Block::FIRE);
|
||||||
|
fire_props.age = EnumVariants::from_index(old_fire_props.age.to_index());
|
||||||
|
return fire_props.to_state_id(&Block::FIRE);
|
||||||
|
}
|
||||||
|
Block::AIR.default_state.id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let state = args
|
||||||
|
.block_accessor
|
||||||
|
.get_block_state(&args.position.down())
|
||||||
|
.await;
|
||||||
|
if state.is_side_solid(BlockDirection::Up) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
self.are_blocks_around_flammable(args.block_accessor, args.position)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let (world, block, pos) = (args.world, args.block, args.position);
|
Box::pin(async move {
|
||||||
world
|
let (world, block, pos) = (args.world, args.block, args.position);
|
||||||
.schedule_block_tick(
|
|
||||||
block,
|
|
||||||
*pos,
|
|
||||||
Self::get_fire_tick_delay() as u8,
|
|
||||||
TickPriority::Normal,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
if !Self
|
|
||||||
.can_place_at(CanPlaceAtArgs {
|
|
||||||
server: None,
|
|
||||||
world: Some(world),
|
|
||||||
block_accessor: world.as_ref(),
|
|
||||||
block,
|
|
||||||
position: pos,
|
|
||||||
direction: BlockDirection::Up,
|
|
||||||
player: None,
|
|
||||||
use_item_on: None,
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
world
|
world
|
||||||
.set_block_state(
|
.schedule_block_tick(
|
||||||
pos,
|
block,
|
||||||
Block::AIR.default_state.id,
|
*pos,
|
||||||
BlockFlags::NOTIFY_NEIGHBORS,
|
Self::get_fire_tick_delay() as u8,
|
||||||
|
TickPriority::Normal,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
return;
|
if !Self
|
||||||
}
|
.can_place_at(CanPlaceAtArgs {
|
||||||
let block_state = world.get_block_state(pos).await;
|
server: None,
|
||||||
//TODO add checks for raining and infiniburn
|
world: Some(world),
|
||||||
let mut fire_props = FireProperties::from_state_id(block_state.id, &Block::FIRE);
|
block_accessor: world.as_ref(),
|
||||||
let age = fire_props.age.to_index() + 1;
|
block,
|
||||||
|
position: pos,
|
||||||
let random = rand::rng().random_range(0..3) / 2;
|
direction: BlockDirection::Up,
|
||||||
let new_age = (age + random).min(15);
|
player: None,
|
||||||
if new_age != age {
|
use_item_on: None,
|
||||||
fire_props.age = EnumVariants::from_index(new_age);
|
})
|
||||||
let new_state_id = fire_props.to_state_id(&Block::FIRE);
|
.await
|
||||||
world
|
{
|
||||||
.set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Self.are_blocks_around_flammable(world.as_ref(), pos).await {
|
|
||||||
let block_below_state = world.get_block_state(&pos.down()).await;
|
|
||||||
if block_below_state.is_side_solid(BlockDirection::Up) {
|
|
||||||
world
|
world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
pos,
|
pos,
|
||||||
@ -310,100 +290,132 @@ impl BlockBehaviour for FireBlock {
|
|||||||
BlockFlags::NOTIFY_NEIGHBORS,
|
BlockFlags::NOTIFY_NEIGHBORS,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
let block_state = world.get_block_state(pos).await;
|
||||||
}
|
//TODO add checks for raining and infiniburn
|
||||||
|
let mut fire_props = FireProperties::from_state_id(block_state.id, &Block::FIRE);
|
||||||
|
let age = fire_props.age.to_index() + 1;
|
||||||
|
|
||||||
if age == 15
|
let random = rand::rng().random_range(0..3) / 2;
|
||||||
&& rand::rng().random_range(0..4) == 0
|
let new_age = (age + random).min(15);
|
||||||
&& !Self::is_flammable(world.get_block_state(&pos.down()).await)
|
if new_age != age {
|
||||||
{
|
fire_props.age = EnumVariants::from_index(new_age);
|
||||||
world
|
let new_state_id = fire_props.to_state_id(&Block::FIRE);
|
||||||
.set_block_state(
|
world
|
||||||
pos,
|
.set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS)
|
||||||
Block::AIR.default_state.id,
|
.await;
|
||||||
BlockFlags::NOTIFY_NEIGHBORS,
|
}
|
||||||
)
|
|
||||||
.await;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Self.try_spreading_fire(
|
if !Self.are_blocks_around_flammable(world.as_ref(), pos).await {
|
||||||
world,
|
let block_below_state = world.get_block_state(&pos.down()).await;
|
||||||
&pos.offset(BlockDirection::East.to_offset()),
|
if block_below_state.is_side_solid(BlockDirection::Up) {
|
||||||
300,
|
world
|
||||||
age,
|
.set_block_state(
|
||||||
)
|
pos,
|
||||||
.await;
|
Block::AIR.default_state.id,
|
||||||
Self.try_spreading_fire(
|
BlockFlags::NOTIFY_NEIGHBORS,
|
||||||
world,
|
)
|
||||||
&pos.offset(BlockDirection::West.to_offset()),
|
.await;
|
||||||
300,
|
}
|
||||||
age,
|
return;
|
||||||
)
|
}
|
||||||
.await;
|
|
||||||
Self.try_spreading_fire(
|
if age == 15
|
||||||
world,
|
&& rand::rng().random_range(0..4) == 0
|
||||||
&pos.offset(BlockDirection::North.to_offset()),
|
&& !Self::is_flammable(world.get_block_state(&pos.down()).await)
|
||||||
300,
|
{
|
||||||
age,
|
world
|
||||||
)
|
.set_block_state(
|
||||||
.await;
|
pos,
|
||||||
Self.try_spreading_fire(
|
Block::AIR.default_state.id,
|
||||||
world,
|
BlockFlags::NOTIFY_NEIGHBORS,
|
||||||
&pos.offset(BlockDirection::South.to_offset()),
|
)
|
||||||
300,
|
.await;
|
||||||
age,
|
return;
|
||||||
)
|
}
|
||||||
.await;
|
|
||||||
Self.try_spreading_fire(world, &pos.offset(BlockDirection::Up.to_offset()), 250, age)
|
Self.try_spreading_fire(
|
||||||
|
world,
|
||||||
|
&pos.offset(BlockDirection::East.to_offset()),
|
||||||
|
300,
|
||||||
|
age,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
Self.try_spreading_fire(
|
||||||
|
world,
|
||||||
|
&pos.offset(BlockDirection::West.to_offset()),
|
||||||
|
300,
|
||||||
|
age,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
Self.try_spreading_fire(
|
||||||
|
world,
|
||||||
|
&pos.offset(BlockDirection::North.to_offset()),
|
||||||
|
300,
|
||||||
|
age,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
Self.try_spreading_fire(
|
||||||
|
world,
|
||||||
|
&pos.offset(BlockDirection::South.to_offset()),
|
||||||
|
300,
|
||||||
|
age,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
Self.try_spreading_fire(world, &pos.offset(BlockDirection::Up.to_offset()), 250, age)
|
||||||
|
.await;
|
||||||
|
Self.try_spreading_fire(
|
||||||
|
world,
|
||||||
|
&pos.offset(BlockDirection::Down.to_offset()),
|
||||||
|
250,
|
||||||
|
age,
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
Self.try_spreading_fire(
|
|
||||||
world,
|
|
||||||
&pos.offset(BlockDirection::Down.to_offset()),
|
|
||||||
250,
|
|
||||||
age,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
for l in -1..=1 {
|
for l in -1..=1 {
|
||||||
for m in -1..=1 {
|
for m in -1..=1 {
|
||||||
for n in -1..=4 {
|
for n in -1..=4 {
|
||||||
if l != 0 || n != 0 || m != 0 {
|
if l != 0 || n != 0 || m != 0 {
|
||||||
let offset_pos = pos.offset(Vector3::new(l, n, m));
|
let offset_pos = pos.offset(Vector3::new(l, n, m));
|
||||||
let burn_chance = Self.get_burn_chance(world, &offset_pos).await;
|
let burn_chance = Self.get_burn_chance(world, &offset_pos).await;
|
||||||
if burn_chance > 0 {
|
if burn_chance > 0 {
|
||||||
let o = 100 + if n > 1 { (n - 1) * 100 } else { 0 };
|
let o = 100 + if n > 1 { (n - 1) * 100 } else { 0 };
|
||||||
let p: i32 = burn_chance
|
let p: i32 = burn_chance
|
||||||
+ 40
|
+ 40
|
||||||
+ (world.level_info.read().await.difficulty as i32) * 7
|
+ (world.level_info.read().await.difficulty as i32) * 7
|
||||||
/ i32::from(age + 30);
|
/ i32::from(age + 30);
|
||||||
|
|
||||||
if p > 0 && rand::rng().random_range(0..o) <= p {
|
if p > 0 && rand::rng().random_range(0..o) <= p {
|
||||||
let new_age = (age + rand::rng().random_range(0..5) / 4).min(15);
|
let new_age =
|
||||||
let fire_state_id =
|
(age + rand::rng().random_range(0..5) / 4).min(15);
|
||||||
self.get_state_for_position(world, block, &offset_pos).await;
|
let fire_state_id = self
|
||||||
let mut new_fire_props =
|
.get_state_for_position(world, block, &offset_pos)
|
||||||
FireProperties::from_state_id(fire_state_id, &Block::FIRE);
|
.await;
|
||||||
new_fire_props.age = EnumVariants::from_index(new_age);
|
let mut new_fire_props =
|
||||||
|
FireProperties::from_state_id(fire_state_id, &Block::FIRE);
|
||||||
|
new_fire_props.age = EnumVariants::from_index(new_age);
|
||||||
|
|
||||||
//TODO drop items for burned blocks
|
//TODO drop items for burned blocks
|
||||||
world
|
world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
&offset_pos,
|
&offset_pos,
|
||||||
new_fire_props.to_state_id(&Block::FIRE),
|
new_fire_props.to_state_id(&Block::FIRE),
|
||||||
BlockFlags::NOTIFY_NEIGHBORS,
|
BlockFlags::NOTIFY_NEIGHBORS,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn broken(&self, args: BrokenArgs<'_>) {
|
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
FireBlockBase::broken(args.world.clone(), *args.position).await;
|
Box::pin(async move {
|
||||||
|
FireBlockBase::broken(args.world.clone(), *args.position).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
use pumpkin_data::{Block, tag};
|
use pumpkin_data::{Block, tag};
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
|
|
||||||
use crate::block::{BlockBehaviour, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs};
|
use crate::block::{
|
||||||
|
BlockBehaviour, BlockFuture, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
||||||
|
};
|
||||||
|
|
||||||
use super::FireBlockBase;
|
use super::FireBlockBase;
|
||||||
|
|
||||||
@ -18,24 +19,29 @@ impl SoulFireBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for SoulFireBlock {
|
impl BlockBehaviour for SoulFireBlock {
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
if !Self::is_soul_base(args.world.get_block(&args.position.down()).await) {
|
Box::pin(async move {
|
||||||
return Block::AIR.default_state.id;
|
if !Self::is_soul_base(args.world.get_block(&args.position.down()).await) {
|
||||||
}
|
return Block::AIR.default_state.id;
|
||||||
|
}
|
||||||
|
|
||||||
args.state_id
|
args.state_id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
Self::is_soul_base(args.block_accessor.get_block(&args.position.down()).await)
|
Box::pin(async move {
|
||||||
|
Self::is_soul_base(args.block_accessor.get_block(&args.position.down()).await)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn broken(&self, args: BrokenArgs<'_>) {
|
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
FireBlockBase::broken(args.world.clone(), *args.position).await;
|
Box::pin(async move {
|
||||||
|
FireBlockBase::broken(args.world.clone(), *args.position).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use crate::block::registry::BlockActionResult;
|
use crate::block::registry::BlockActionResult;
|
||||||
use crate::block::{BlockBehaviour, RandomTickArgs, UseWithItemArgs};
|
use crate::block::{BlockBehaviour, BlockFuture, RandomTickArgs, UseWithItemArgs};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::flower_pot_transformations::get_potted_item;
|
use pumpkin_data::flower_pot_transformations::get_potted_item;
|
||||||
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
||||||
@ -11,69 +10,75 @@ use pumpkin_world::world::BlockFlags;
|
|||||||
#[pumpkin_block_from_tag("minecraft:flower_pots")]
|
#[pumpkin_block_from_tag("minecraft:flower_pots")]
|
||||||
pub struct FlowerPotBlock;
|
pub struct FlowerPotBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for FlowerPotBlock {
|
impl BlockBehaviour for FlowerPotBlock {
|
||||||
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
|
fn use_with_item<'a>(
|
||||||
let item = args.item_stack.lock().await.item;
|
&'a self,
|
||||||
//Place the flower inside the pot
|
args: UseWithItemArgs<'a>,
|
||||||
let potted_block_id = get_potted_item(item.id);
|
) -> BlockFuture<'a, BlockActionResult> {
|
||||||
if args.block.eq(&Block::FLOWER_POT) {
|
Box::pin(async move {
|
||||||
if potted_block_id != 0 {
|
let item = args.item_stack.lock().await.item;
|
||||||
|
//Place the flower inside the pot
|
||||||
|
let potted_block_id = get_potted_item(item.id);
|
||||||
|
if args.block.eq(&Block::FLOWER_POT) {
|
||||||
|
if potted_block_id != 0 {
|
||||||
|
args.world
|
||||||
|
.set_block_state(
|
||||||
|
args.position,
|
||||||
|
Block::from_id(potted_block_id).default_state.id,
|
||||||
|
BlockFlags::NOTIFY_ALL,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
return BlockActionResult::Success;
|
||||||
|
} else if potted_block_id != 0 {
|
||||||
|
//if the player have an item that can be potted in his hand, nothing happens
|
||||||
|
return BlockActionResult::Consume;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the flower + empty the pot
|
||||||
|
args.world
|
||||||
|
.set_block_state(
|
||||||
|
args.position,
|
||||||
|
Block::FLOWER_POT.default_state.id,
|
||||||
|
BlockFlags::NOTIFY_ALL,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
BlockActionResult::Success
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
if (args
|
||||||
|
.world
|
||||||
|
.dimension_type
|
||||||
|
.eq(&VanillaDimensionType::Overworld)
|
||||||
|
|| args
|
||||||
|
.world
|
||||||
|
.dimension_type
|
||||||
|
.eq(&VanillaDimensionType::OverworldCaves))
|
||||||
|
&& args.block.eq(&Block::POTTED_CLOSED_EYEBLOSSOM)
|
||||||
|
&& args.world.level_time.lock().await.time_of_day % 24000 > 14500
|
||||||
|
{
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
Block::from_id(potted_block_id).default_state.id,
|
Block::POTTED_OPEN_EYEBLOSSOM.default_state.id,
|
||||||
BlockFlags::NOTIFY_ALL,
|
BlockFlags::NOTIFY_ALL,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
return BlockActionResult::Success;
|
if args.block.eq(&Block::POTTED_OPEN_EYEBLOSSOM)
|
||||||
} else if potted_block_id != 0 {
|
&& args.world.level_time.lock().await.time_of_day % 24000 <= 14500
|
||||||
//if the player have an item that can be potted in his hand, nothing happens
|
{
|
||||||
return BlockActionResult::Consume;
|
args.world
|
||||||
}
|
.set_block_state(
|
||||||
|
args.position,
|
||||||
//get the flower + empty the pot
|
Block::POTTED_CLOSED_EYEBLOSSOM.default_state.id,
|
||||||
args.world
|
BlockFlags::NOTIFY_ALL,
|
||||||
.set_block_state(
|
)
|
||||||
args.position,
|
.await;
|
||||||
Block::FLOWER_POT.default_state.id,
|
}
|
||||||
BlockFlags::NOTIFY_ALL,
|
})
|
||||||
)
|
|
||||||
.await;
|
|
||||||
BlockActionResult::Success
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn random_tick(&self, args: RandomTickArgs<'_>) {
|
|
||||||
if (args
|
|
||||||
.world
|
|
||||||
.dimension_type
|
|
||||||
.eq(&VanillaDimensionType::Overworld)
|
|
||||||
|| args
|
|
||||||
.world
|
|
||||||
.dimension_type
|
|
||||||
.eq(&VanillaDimensionType::OverworldCaves))
|
|
||||||
&& args.block.eq(&Block::POTTED_CLOSED_EYEBLOSSOM)
|
|
||||||
&& args.world.level_time.lock().await.time_of_day % 24000 > 14500
|
|
||||||
{
|
|
||||||
args.world
|
|
||||||
.set_block_state(
|
|
||||||
args.position,
|
|
||||||
Block::POTTED_OPEN_EYEBLOSSOM.default_state.id,
|
|
||||||
BlockFlags::NOTIFY_ALL,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
if args.block.eq(&Block::POTTED_OPEN_EYEBLOSSOM)
|
|
||||||
&& args.world.level_time.lock().await.time_of_day % 24000 <= 14500
|
|
||||||
{
|
|
||||||
args.world
|
|
||||||
.set_block_state(
|
|
||||||
args.position,
|
|
||||||
Block::POTTED_CLOSED_EYEBLOSSOM.default_state.id,
|
|
||||||
BlockFlags::NOTIFY_ALL,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,12 +8,16 @@ use pumpkin_inventory::{
|
|||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_util::text::TextComponent;
|
use pumpkin_util::text::TextComponent;
|
||||||
use pumpkin_world::{
|
use pumpkin_world::{
|
||||||
|
BlockStateId,
|
||||||
block::entities::{BlockEntity, furnace::FurnaceBlockEntity},
|
block::entities::{BlockEntity, furnace::FurnaceBlockEntity},
|
||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
};
|
};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::block::BlockBehaviour;
|
use crate::block::{
|
||||||
|
BlockBehaviour, BlockFuture, BrokenArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs,
|
||||||
|
registry::BlockActionResult,
|
||||||
|
};
|
||||||
|
|
||||||
struct FurnaceScreenFactory {
|
struct FurnaceScreenFactory {
|
||||||
inventory: Arc<dyn Inventory>,
|
inventory: Arc<dyn Inventory>,
|
||||||
@ -55,109 +59,47 @@ impl ScreenHandlerFactory for FurnaceScreenFactory {
|
|||||||
#[pumpkin_block("minecraft:furnace")]
|
#[pumpkin_block("minecraft:furnace")]
|
||||||
pub struct FurnaceBlock;
|
pub struct FurnaceBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for FurnaceBlock {
|
impl BlockBehaviour for FurnaceBlock {
|
||||||
async fn normal_use(
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
&self,
|
Box::pin(async move {
|
||||||
args: crate::block::NormalUseArgs<'_>,
|
if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
||||||
) -> crate::block::registry::BlockActionResult {
|
&& let Some(inventory) = block_entity.clone().get_inventory()
|
||||||
if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
{
|
||||||
&& let Some(inventory) = block_entity.clone().get_inventory()
|
let furnace_screen_factory = FurnaceScreenFactory::new(inventory, block_entity);
|
||||||
{
|
args.player
|
||||||
let furnace_screen_factory = FurnaceScreenFactory::new(inventory, block_entity);
|
.open_handled_screen(&furnace_screen_factory)
|
||||||
args.player
|
.await;
|
||||||
.open_handled_screen(&furnace_screen_factory)
|
}
|
||||||
|
crate::block::registry::BlockActionResult::Consume
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut props = FurnaceLikeProperties::default(args.block);
|
||||||
|
props.facing = args
|
||||||
|
.player
|
||||||
|
.living_entity
|
||||||
|
.entity
|
||||||
|
.get_horizontal_facing()
|
||||||
|
.opposite();
|
||||||
|
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let furnace_block_entity = FurnaceBlockEntity::new(*args.position);
|
||||||
|
args.world
|
||||||
|
.add_block_entity(Arc::new(furnace_block_entity))
|
||||||
.await;
|
.await;
|
||||||
}
|
})
|
||||||
crate::block::registry::BlockActionResult::Consume
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Same to normal_use
|
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
async fn use_with_item(
|
Box::pin(async move {
|
||||||
&self,
|
args.world.remove_block_entity(args.position).await;
|
||||||
_args: crate::block::UseWithItemArgs<'_>,
|
})
|
||||||
) -> crate::block::registry::BlockActionResult {
|
|
||||||
crate::block::registry::BlockActionResult::PassToDefaultBlockAction
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn on_entity_collision(&self, _args: crate::block::OnEntityCollisionArgs<'_>) {}
|
|
||||||
|
|
||||||
fn should_drop_items_on_explosion(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn explode(&self, _args: crate::block::ExplodeArgs<'_>) {}
|
|
||||||
|
|
||||||
async fn on_synced_block_event(&self, _args: crate::block::OnSyncedBlockEventArgs<'_>) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn on_place(&self, args: crate::block::OnPlaceArgs<'_>) -> pumpkin_world::BlockStateId {
|
|
||||||
let mut props = FurnaceLikeProperties::default(args.block);
|
|
||||||
props.facing = args
|
|
||||||
.player
|
|
||||||
.living_entity
|
|
||||||
.entity
|
|
||||||
.get_horizontal_facing()
|
|
||||||
.opposite();
|
|
||||||
|
|
||||||
props.to_state_id(args.block)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn random_tick(&self, _args: crate::block::RandomTickArgs<'_>) {}
|
|
||||||
|
|
||||||
async fn can_place_at(&self, _args: crate::block::CanPlaceAtArgs<'_>) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn can_update_at(&self, _args: crate::block::CanUpdateAtArgs<'_>) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn placed(&self, args: crate::block::PlacedArgs<'_>) {
|
|
||||||
let furnace_block_entity = FurnaceBlockEntity::new(*args.position);
|
|
||||||
args.world
|
|
||||||
.add_block_entity(Arc::new(furnace_block_entity))
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn player_placed(&self, _args: crate::block::PlayerPlacedArgs<'_>) {}
|
|
||||||
|
|
||||||
async fn broken(&self, args: crate::block::BrokenArgs<'_>) {
|
|
||||||
args.world.remove_block_entity(args.position).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn on_neighbor_update(&self, _args: crate::block::OnNeighborUpdateArgs<'_>) {}
|
|
||||||
|
|
||||||
async fn prepare(&self, _args: crate::block::PrepareArgs<'_>) {}
|
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
|
||||||
&self,
|
|
||||||
args: crate::block::GetStateForNeighborUpdateArgs<'_>,
|
|
||||||
) -> pumpkin_world::BlockStateId {
|
|
||||||
args.state_id
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn on_scheduled_tick(&self, _args: crate::block::OnScheduledTickArgs<'_>) {}
|
|
||||||
|
|
||||||
async fn on_state_replaced(&self, _args: crate::block::OnStateReplacedArgs<'_>) {}
|
|
||||||
|
|
||||||
async fn emits_redstone_power(&self, _args: crate::block::EmitsRedstonePowerArgs<'_>) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_weak_redstone_power(&self, _args: crate::block::GetRedstonePowerArgs<'_>) -> u8 {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_strong_redstone_power(&self, _args: crate::block::GetRedstonePowerArgs<'_>) -> u8 {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_comparator_output(
|
|
||||||
&self,
|
|
||||||
_args: crate::block::GetComparatorOutputArgs<'_>,
|
|
||||||
) -> Option<u8> {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
use crate::block::BlockFuture;
|
||||||
use crate::block::GetStateForNeighborUpdateArgs;
|
use crate::block::GetStateForNeighborUpdateArgs;
|
||||||
use crate::block::OnPlaceArgs;
|
use crate::block::OnPlaceArgs;
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::BlockDirection;
|
use pumpkin_data::BlockDirection;
|
||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
use pumpkin_data::tag::RegistryKey;
|
use pumpkin_data::tag::RegistryKey;
|
||||||
@ -26,21 +26,24 @@ impl BlockMetadata for GlassPaneBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for GlassPaneBlock {
|
impl BlockBehaviour for GlassPaneBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut pane_props = GlassPaneProperties::default(args.block);
|
Box::pin(async move {
|
||||||
pane_props.waterlogged = args.replacing.water_source();
|
let mut pane_props = GlassPaneProperties::default(args.block);
|
||||||
|
pane_props.waterlogged = args.replacing.water_source();
|
||||||
|
|
||||||
compute_pane_state(pane_props, args.world, args.block, args.position).await
|
compute_pane_state(pane_props, args.world, args.block, args.position).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
let pane_props = GlassPaneProperties::from_state_id(args.state_id, args.block);
|
Box::pin(async move {
|
||||||
compute_pane_state(pane_props, args.world, args.block, args.position).await
|
let pane_props = GlassPaneProperties::from_state_id(args.state_id, args.block);
|
||||||
|
compute_pane_state(pane_props, args.world, args.block, args.position).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
use crate::block::{BlockBehaviour, BlockMetadata, OnPlaceArgs};
|
use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, OnPlaceArgs};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties};
|
use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties};
|
||||||
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
@ -15,16 +14,17 @@ impl BlockMetadata for GlazedTerracottaBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for GlazedTerracottaBlock {
|
impl BlockBehaviour for GlazedTerracottaBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut prop = WallTorchLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
prop.facing = args
|
let mut prop = WallTorchLikeProperties::default(args.block);
|
||||||
.player
|
prop.facing = args
|
||||||
.living_entity
|
.player
|
||||||
.entity
|
.living_entity
|
||||||
.get_horizontal_facing()
|
.entity
|
||||||
.opposite();
|
.get_horizontal_facing()
|
||||||
prop.to_state_id(args.block)
|
.opposite();
|
||||||
|
prop.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockDirection, HorizontalFacingExt,
|
Block, BlockDirection, HorizontalFacingExt,
|
||||||
block_properties::{BlockFace, BlockProperties, GrindstoneLikeProperties},
|
block_properties::{BlockFace, BlockProperties, GrindstoneLikeProperties},
|
||||||
@ -7,8 +6,8 @@ use pumpkin_macros::pumpkin_block;
|
|||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_world::{BlockStateId, world::BlockAccessor};
|
use pumpkin_world::{BlockStateId, world::BlockAccessor};
|
||||||
|
|
||||||
use crate::block::BlockBehaviour;
|
|
||||||
use crate::block::CanPlaceAtArgs;
|
use crate::block::CanPlaceAtArgs;
|
||||||
|
use crate::block::{BlockBehaviour, BlockFuture};
|
||||||
use crate::block::{GetStateForNeighborUpdateArgs, OnPlaceArgs};
|
use crate::block::{GetStateForNeighborUpdateArgs, OnPlaceArgs};
|
||||||
|
|
||||||
use super::abstract_wall_mounting::WallMountedBlock;
|
use super::abstract_wall_mounting::WallMountedBlock;
|
||||||
@ -16,39 +15,41 @@ use super::abstract_wall_mounting::WallMountedBlock;
|
|||||||
#[pumpkin_block("minecraft:grindstone")]
|
#[pumpkin_block("minecraft:grindstone")]
|
||||||
pub struct GrindstoneBlock;
|
pub struct GrindstoneBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for GrindstoneBlock {
|
impl BlockBehaviour for GrindstoneBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut props =
|
Box::pin(async move {
|
||||||
GrindstoneLikeProperties::from_state_id(args.block.default_state.id, args.block);
|
let mut props =
|
||||||
(props.face, props.facing) =
|
GrindstoneLikeProperties::from_state_id(args.block.default_state.id, args.block);
|
||||||
WallMountedBlock::get_placement_face(self, args.player, args.direction);
|
(props.face, props.facing) =
|
||||||
|
WallMountedBlock::get_placement_face(self, args.player, args.direction);
|
||||||
|
|
||||||
props.to_state_id(args.block)
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
WallMountedBlock::can_place_at(self, args.block_accessor, args.position, args.direction)
|
Box::pin(async move {
|
||||||
.await
|
WallMountedBlock::can_place_at(self, args.block_accessor, args.position, args.direction)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
WallMountedBlock::get_state_for_neighbor_update(self, args).await
|
Box::pin(async move { WallMountedBlock::get_state_for_neighbor_update(self, args).await })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WallMountedBlock for GrindstoneBlock {
|
impl WallMountedBlock for GrindstoneBlock {
|
||||||
async fn can_place_at(
|
fn can_place_at<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
_world: &dyn BlockAccessor,
|
_world: &'a dyn BlockAccessor,
|
||||||
_pos: &BlockPos,
|
_pos: &'a BlockPos,
|
||||||
_direction: BlockDirection,
|
_direction: BlockDirection,
|
||||||
) -> bool {
|
) -> BlockFuture<'a, bool> {
|
||||||
true
|
Box::pin(async move { true })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_direction(&self, state_id: BlockStateId, block: &Block) -> BlockDirection {
|
fn get_direction(&self, state_id: BlockStateId, block: &Block) -> BlockDirection {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::block::blocks::redstone::block_receives_redstone_power;
|
use crate::block::blocks::redstone::block_receives_redstone_power;
|
||||||
use crate::block::{OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs};
|
use crate::block::{BlockFuture, OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs};
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
registry::BlockActionResult,
|
registry::BlockActionResult,
|
||||||
{BlockBehaviour, NormalUseArgs},
|
{BlockBehaviour, NormalUseArgs},
|
||||||
@ -47,52 +47,59 @@ pub struct HopperBlock;
|
|||||||
|
|
||||||
type HopperLikeProperties = pumpkin_data::block_properties::HopperLikeProperties;
|
type HopperLikeProperties = pumpkin_data::block_properties::HopperLikeProperties;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for HopperBlock {
|
impl BlockBehaviour for HopperBlock {
|
||||||
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
Box::pin(async move {
|
||||||
&& let Some(inventory) = block_entity.get_inventory()
|
if let Some(block_entity) = args.world.get_block_entity(args.position).await
|
||||||
{
|
&& let Some(inventory) = block_entity.get_inventory()
|
||||||
args.player
|
{
|
||||||
.open_handled_screen(&HopperBlockScreenFactory(inventory))
|
args.player
|
||||||
|
.open_handled_screen(&HopperBlockScreenFactory(inventory))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockActionResult::Success
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut props = HopperLikeProperties::default(args.block);
|
||||||
|
props.facing = match args.direction {
|
||||||
|
BlockDirection::North => HopperFacing::North,
|
||||||
|
BlockDirection::East => HopperFacing::East,
|
||||||
|
BlockDirection::South => HopperFacing::South,
|
||||||
|
BlockDirection::West => HopperFacing::West,
|
||||||
|
BlockDirection::Up | BlockDirection::Down => HopperFacing::Down,
|
||||||
|
};
|
||||||
|
props.enabled = true;
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let props = HopperLikeProperties::from_state_id(args.state_id, args.block);
|
||||||
|
let hopper_block_entity = HopperBlockEntity::new(*args.position, props.facing);
|
||||||
|
args.world
|
||||||
|
.add_block_entity(Arc::new(hopper_block_entity))
|
||||||
.await;
|
.await;
|
||||||
}
|
if Block::from_state_id(args.old_state_id) != Block::from_state_id(args.state_id) {
|
||||||
|
check_powered_state(args.world, args.position, args.state_id, args.block).await;
|
||||||
BlockActionResult::Success
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let mut props = HopperLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
props.facing = match args.direction {
|
check_powered_state(
|
||||||
BlockDirection::North => HopperFacing::North,
|
args.world,
|
||||||
BlockDirection::East => HopperFacing::East,
|
args.position,
|
||||||
BlockDirection::South => HopperFacing::South,
|
args.world.get_block_state_id(args.position).await,
|
||||||
BlockDirection::West => HopperFacing::West,
|
args.block,
|
||||||
BlockDirection::Up | BlockDirection::Down => HopperFacing::Down,
|
)
|
||||||
};
|
|
||||||
props.enabled = true;
|
|
||||||
props.to_state_id(args.block)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn placed(&self, args: PlacedArgs<'_>) {
|
|
||||||
let props = HopperLikeProperties::from_state_id(args.state_id, args.block);
|
|
||||||
let hopper_block_entity = HopperBlockEntity::new(*args.position, props.facing);
|
|
||||||
args.world
|
|
||||||
.add_block_entity(Arc::new(hopper_block_entity))
|
|
||||||
.await;
|
.await;
|
||||||
if Block::from_state_id(args.old_state_id) != Block::from_state_id(args.state_id) {
|
})
|
||||||
check_powered_state(args.world, args.position, args.state_id, args.block).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) {
|
|
||||||
check_powered_state(
|
|
||||||
args.world,
|
|
||||||
args.position,
|
|
||||||
args.world.get_block_state_id(args.position).await,
|
|
||||||
args.block,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
use crate::block::BlockFuture;
|
||||||
use crate::block::GetStateForNeighborUpdateArgs;
|
use crate::block::GetStateForNeighborUpdateArgs;
|
||||||
use crate::block::OnPlaceArgs;
|
use crate::block::OnPlaceArgs;
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::BlockDirection;
|
use pumpkin_data::BlockDirection;
|
||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
@ -17,21 +17,24 @@ use crate::world::World;
|
|||||||
#[pumpkin_block("minecraft:iron_bars")]
|
#[pumpkin_block("minecraft:iron_bars")]
|
||||||
pub struct IronBarsBlock;
|
pub struct IronBarsBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for IronBarsBlock {
|
impl BlockBehaviour for IronBarsBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut bars_props = IronBarsProperties::default(args.block);
|
Box::pin(async move {
|
||||||
bars_props.waterlogged = args.replacing.water_source();
|
let mut bars_props = IronBarsProperties::default(args.block);
|
||||||
|
bars_props.waterlogged = args.replacing.water_source();
|
||||||
|
|
||||||
compute_bars_state(bars_props, args.world, args.block, args.position).await
|
compute_bars_state(bars_props, args.world, args.block, args.position).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
let bars_props = IronBarsProperties::from_state_id(args.state_id, args.block);
|
Box::pin(async move {
|
||||||
compute_bars_state(bars_props, args.world, args.block, args.position).await
|
let bars_props = IronBarsProperties::from_state_id(args.state_id, args.block);
|
||||||
|
compute_bars_state(bars_props, args.world, args.block, args.position).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::block::registry::BlockActionResult;
|
use crate::block::registry::BlockActionResult;
|
||||||
use crate::block::{BlockBehaviour, BrokenArgs, UseWithItemArgs};
|
use crate::block::{BlockBehaviour, BlockFuture, BrokenArgs, UseWithItemArgs};
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::data_component_impl::JukeboxPlayableImpl;
|
use pumpkin_data::data_component_impl::JukeboxPlayableImpl;
|
||||||
use pumpkin_data::world::WorldEvent;
|
use pumpkin_data::world::WorldEvent;
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
@ -45,56 +44,62 @@ impl JukeboxBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for JukeboxBlock {
|
impl BlockBehaviour for JukeboxBlock {
|
||||||
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
|
fn use_with_item<'a>(
|
||||||
let world = &args.player.living_entity.entity.world;
|
&'a self,
|
||||||
|
args: UseWithItemArgs<'a>,
|
||||||
|
) -> BlockFuture<'a, BlockActionResult> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let world = &args.player.living_entity.entity.world;
|
||||||
|
|
||||||
// if the jukebox already has a record, stop playing
|
// if the jukebox already has a record, stop playing
|
||||||
if self.has_record(args.block, args.position, world).await {
|
if self.has_record(args.block, args.position, world).await {
|
||||||
self.stop_music(args.block, args.position, world).await;
|
self.stop_music(args.block, args.position, world).await;
|
||||||
return BlockActionResult::Success;
|
return BlockActionResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
let jukebox_playable = args
|
let jukebox_playable = args
|
||||||
.item_stack
|
.item_stack
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.get_data_component::<JukeboxPlayableImpl>()
|
.get_data_component::<JukeboxPlayableImpl>()
|
||||||
.map(|i| i.song);
|
.map(|i| i.song);
|
||||||
|
|
||||||
let Some(jukebox_playable) = jukebox_playable else {
|
let Some(jukebox_playable) = jukebox_playable else {
|
||||||
return BlockActionResult::Pass;
|
return BlockActionResult::Pass;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(song) = jukebox_playable.split(':').nth(1) else {
|
let Some(song) = jukebox_playable.split(':').nth(1) else {
|
||||||
return BlockActionResult::Pass;
|
return BlockActionResult::Pass;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(jukebox_song) = SYNCED_REGISTRIES.get_jukebox_song_index(song) else {
|
let Some(jukebox_song) = SYNCED_REGISTRIES.get_jukebox_song_index(song) else {
|
||||||
log::error!("Jukebox playable song not registered!");
|
log::error!("Jukebox playable song not registered!");
|
||||||
return BlockActionResult::Pass;
|
return BlockActionResult::Pass;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Update block nbt
|
// TODO: Update block nbt
|
||||||
|
|
||||||
self.set_record(true, args.block, args.position, world)
|
self.set_record(true, args.block, args.position, world)
|
||||||
.await;
|
.await;
|
||||||
world
|
world
|
||||||
.sync_world_event(
|
.sync_world_event(
|
||||||
WorldEvent::JukeboxStartsPlaying,
|
WorldEvent::JukeboxStartsPlaying,
|
||||||
*args.position,
|
*args.position,
|
||||||
jukebox_song as i32,
|
jukebox_song as i32,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
BlockActionResult::Success
|
BlockActionResult::Success
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn broken(&self, args: BrokenArgs<'_>) {
|
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
// For now just stop the music at this position
|
Box::pin(async move {
|
||||||
args.world
|
// For now just stop the music at this position
|
||||||
.sync_world_event(WorldEvent::JukeboxStopsPlaying, *args.position, 0)
|
args.world
|
||||||
.await;
|
.sync_world_event(WorldEvent::JukeboxStopsPlaying, *args.position, 0)
|
||||||
|
.await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs,
|
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
|
||||||
|
OnScheduledTickArgs,
|
||||||
};
|
};
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::BlockDirection;
|
use pumpkin_data::BlockDirection;
|
||||||
use pumpkin_data::block_properties::HorizontalFacing;
|
use pumpkin_data::block_properties::HorizontalFacing;
|
||||||
use pumpkin_data::block_properties::{BlockProperties, LadderLikeProperties};
|
use pumpkin_data::block_properties::{BlockProperties, LadderLikeProperties};
|
||||||
@ -15,39 +15,46 @@ use pumpkin_world::world::BlockFlags;
|
|||||||
#[pumpkin_block("minecraft:ladder")]
|
#[pumpkin_block("minecraft:ladder")]
|
||||||
pub struct LadderBlock;
|
pub struct LadderBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for LadderBlock {
|
impl BlockBehaviour for LadderBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut props = LadderLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
props.facing = args.direction.opposite().to_cardinal_direction();
|
let mut props = LadderLikeProperties::default(args.block);
|
||||||
props.to_state_id(args.block)
|
props.facing = args.direction.opposite().to_cardinal_direction();
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
args.block_accessor
|
Box::pin(async move {
|
||||||
.get_block_state(&args.use_item_on.unwrap().position)
|
args.block_accessor
|
||||||
.await
|
.get_block_state(&args.use_item_on.unwrap().position)
|
||||||
.is_side_solid(args.direction.opposite())
|
.await
|
||||||
&& args.direction.is_horizontal()
|
.is_side_solid(args.direction.opposite())
|
||||||
|
&& args.direction.is_horizontal()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
if !can_place_at(args.world, args.position).await {
|
Box::pin(async move {
|
||||||
args.world
|
if !can_place_at(args.world, args.position).await {
|
||||||
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
args.world
|
||||||
.await;
|
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
||||||
}
|
.await;
|
||||||
args.state_id
|
}
|
||||||
|
args.state_id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
if !can_place_at(args.world, args.position).await {
|
Box::pin(async move {
|
||||||
args.world
|
if !can_place_at(args.world, args.position).await {
|
||||||
.break_block(args.position, None, BlockFlags::empty())
|
args.world
|
||||||
.await;
|
.break_block(args.position, None, BlockFlags::empty())
|
||||||
}
|
.await;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
|
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
||||||
OnScheduledTickArgs,
|
OnPlaceArgs, OnScheduledTickArgs,
|
||||||
};
|
};
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
use pumpkin_data::{Block, BlockDirection, tag};
|
use pumpkin_data::{Block, BlockDirection, tag};
|
||||||
@ -24,42 +23,48 @@ impl BlockMetadata for LanternBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for LanternBlock {
|
impl BlockBehaviour for LanternBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut props = pumpkin_data::block_properties::LanternLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
props.r#waterlogged = args.replacing.water_source();
|
let mut props =
|
||||||
|
pumpkin_data::block_properties::LanternLikeProperties::default(args.block);
|
||||||
|
props.r#waterlogged = args.replacing.water_source();
|
||||||
|
|
||||||
let block_up_state = args.world.get_block_state(&args.position.up()).await;
|
let block_up_state = args.world.get_block_state(&args.position.up()).await;
|
||||||
if block_up_state.is_center_solid(BlockDirection::Down) {
|
if block_up_state.is_center_solid(BlockDirection::Down) {
|
||||||
props.r#hanging = true;
|
props.r#hanging = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
props.to_state_id(args.block)
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
can_place_at(args.world.unwrap(), args.position).await
|
Box::pin(async move { can_place_at(args.world.unwrap(), args.position).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
if !can_place_at(args.world, args.position).await {
|
Box::pin(async move {
|
||||||
args.world
|
if !can_place_at(args.world, args.position).await {
|
||||||
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
args.world
|
||||||
.await;
|
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
|
||||||
}
|
.await;
|
||||||
args.state_id
|
}
|
||||||
|
args.state_id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
if !can_place_at(args.world, args.position).await {
|
Box::pin(async move {
|
||||||
args.world
|
if !can_place_at(args.world, args.position).await {
|
||||||
.break_block(args.position, None, BlockFlags::empty())
|
args.world
|
||||||
.await;
|
.break_block(args.position, None, BlockFlags::empty())
|
||||||
}
|
.await;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
use crate::block::{BlockBehaviour, OnPlaceArgs};
|
use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::{BlockProperties, LecternLikeProperties};
|
use pumpkin_data::block_properties::{BlockProperties, LecternLikeProperties};
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
@ -7,16 +6,17 @@ use pumpkin_world::BlockStateId;
|
|||||||
#[pumpkin_block("minecraft:lectern")]
|
#[pumpkin_block("minecraft:lectern")]
|
||||||
pub struct LecternBlock;
|
pub struct LecternBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for LecternBlock {
|
impl BlockBehaviour for LecternBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut props = LecternLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
props.facing = args
|
let mut props = LecternLikeProperties::default(args.block);
|
||||||
.player
|
props.facing = args
|
||||||
.living_entity
|
.player
|
||||||
.entity
|
.living_entity
|
||||||
.get_horizontal_facing()
|
.entity
|
||||||
.opposite();
|
.get_horizontal_facing()
|
||||||
props.to_state_id(args.block)
|
.opposite();
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +1,23 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
||||||
use pumpkin_macros::pumpkin_block_from_tag;
|
use pumpkin_macros::pumpkin_block_from_tag;
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
|
|
||||||
use crate::block::BlockBehaviour;
|
|
||||||
use crate::block::OnPlaceArgs;
|
use crate::block::OnPlaceArgs;
|
||||||
|
use crate::block::{BlockBehaviour, BlockFuture};
|
||||||
|
|
||||||
type LogProperties = pumpkin_data::block_properties::PaleOakWoodLikeProperties;
|
type LogProperties = pumpkin_data::block_properties::PaleOakWoodLikeProperties;
|
||||||
|
|
||||||
#[pumpkin_block_from_tag("minecraft:logs")]
|
#[pumpkin_block_from_tag("minecraft:logs")]
|
||||||
pub struct LogBlock;
|
pub struct LogBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for LogBlock {
|
impl BlockBehaviour for LogBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut log_props = LogProperties::default(args.block);
|
Box::pin(async move {
|
||||||
log_props.axis = args.direction.to_axis();
|
let mut log_props = LogProperties::default(args.block);
|
||||||
|
log_props.axis = args.direction.to_axis();
|
||||||
|
|
||||||
log_props.to_state_id(args.block)
|
log_props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
use crate::block::{BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs};
|
use crate::block::{BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::{BlockProperties, MangroveRootsLikeProperties};
|
use pumpkin_data::block_properties::{BlockProperties, MangroveRootsLikeProperties};
|
||||||
use pumpkin_data::fluid::Fluid;
|
use pumpkin_data::fluid::Fluid;
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
@ -9,29 +8,32 @@ use pumpkin_world::tick::TickPriority;
|
|||||||
#[pumpkin_block("minecraft:mangrove_roots")]
|
#[pumpkin_block("minecraft:mangrove_roots")]
|
||||||
pub struct MangroveRootsBlock;
|
pub struct MangroveRootsBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for MangroveRootsBlock {
|
impl BlockBehaviour for MangroveRootsBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut props = MangroveRootsLikeProperties::default(args.block);
|
Box::pin(async move {
|
||||||
props.waterlogged = args.replacing.water_source();
|
let mut props = MangroveRootsLikeProperties::default(args.block);
|
||||||
props.to_state_id(args.block)
|
props.waterlogged = args.replacing.water_source();
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
let props = MangroveRootsLikeProperties::from_state_id(args.state_id, args.block);
|
Box::pin(async move {
|
||||||
if props.waterlogged {
|
let props = MangroveRootsLikeProperties::from_state_id(args.state_id, args.block);
|
||||||
args.world
|
if props.waterlogged {
|
||||||
.schedule_fluid_tick(
|
args.world
|
||||||
&Fluid::WATER,
|
.schedule_fluid_tick(
|
||||||
*args.position,
|
&Fluid::WATER,
|
||||||
Fluid::WATER.flow_speed as u8,
|
*args.position,
|
||||||
TickPriority::Normal,
|
Fluid::WATER.flow_speed as u8,
|
||||||
)
|
TickPriority::Normal,
|
||||||
.await;
|
)
|
||||||
}
|
.await;
|
||||||
props.to_state_id(args.block)
|
}
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::block::{BlockBehaviour, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs};
|
use crate::block::{
|
||||||
|
BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs,
|
||||||
|
};
|
||||||
use crate::entity::EntityBase;
|
use crate::entity::EntityBase;
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
use crate::world::portal::nether::NetherPortal;
|
use crate::world::portal::nether::NetherPortal;
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::block_properties::{Axis, BlockProperties, NetherPortalLikeProperties};
|
use pumpkin_data::block_properties::{Axis, BlockProperties, NetherPortalLikeProperties};
|
||||||
use pumpkin_data::entity::EntityType;
|
use pumpkin_data::entity::EntityType;
|
||||||
@ -36,43 +37,47 @@ impl NetherPortalBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for NetherPortalBlock {
|
impl BlockBehaviour for NetherPortalBlock {
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
let axis = args.direction.to_axis();
|
Box::pin(async move {
|
||||||
let is_horizontal = axis == Axis::X && axis == Axis::Z;
|
let axis = args.direction.to_axis();
|
||||||
let state_axis =
|
let is_horizontal = axis == Axis::X && axis == Axis::Z;
|
||||||
NetherPortalLikeProperties::from_state_id(args.state_id, &Block::NETHER_PORTAL).axis;
|
let state_axis =
|
||||||
if is_horizontal
|
NetherPortalLikeProperties::from_state_id(args.state_id, &Block::NETHER_PORTAL)
|
||||||
|| args.neighbor_state_id == args.state_id
|
.axis;
|
||||||
|| NetherPortal::get_on_axis(args.world, args.position, state_axis)
|
if is_horizontal
|
||||||
.await
|
|| args.neighbor_state_id == args.state_id
|
||||||
.is_some_and(|e| e.was_already_valid())
|
|| NetherPortal::get_on_axis(args.world, args.position, state_axis)
|
||||||
{
|
.await
|
||||||
return args.state_id;
|
.is_some_and(|e| e.was_already_valid())
|
||||||
}
|
{
|
||||||
Block::AIR.default_state.id
|
return args.state_id;
|
||||||
|
}
|
||||||
|
Block::AIR.default_state.id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) {
|
fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let target_world = if args.world.dimension_type == VanillaDimensionType::TheNether {
|
Box::pin(async move {
|
||||||
args.server
|
let target_world = if args.world.dimension_type == VanillaDimensionType::TheNether {
|
||||||
.get_world_from_dimension(VanillaDimensionType::Overworld)
|
args.server
|
||||||
.await
|
.get_world_from_dimension(VanillaDimensionType::Overworld)
|
||||||
} else {
|
.await
|
||||||
args.server
|
} else {
|
||||||
.get_world_from_dimension(VanillaDimensionType::TheNether)
|
args.server
|
||||||
.await
|
.get_world_from_dimension(VanillaDimensionType::TheNether)
|
||||||
};
|
.await
|
||||||
|
};
|
||||||
|
|
||||||
let portal_delay = Self::get_portal_time(args.world, args.entity).await;
|
let portal_delay = Self::get_portal_time(args.world, args.entity).await;
|
||||||
|
|
||||||
args.entity
|
args.entity
|
||||||
.get_entity()
|
.get_entity()
|
||||||
.try_use_portal(portal_delay, target_world, *args.position)
|
.try_use_portal(portal_delay, target_world, *args.position)
|
||||||
.await;
|
.await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
use crate::block::registry::BlockActionResult;
|
use crate::block::registry::BlockActionResult;
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs,
|
BlockFuture, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs,
|
||||||
UseWithItemArgs,
|
UseWithItemArgs,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::Axis;
|
use pumpkin_data::block_properties::Axis;
|
||||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
@ -61,18 +60,40 @@ impl NoteBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for NoteBlock {
|
impl BlockBehaviour for NoteBlock {
|
||||||
async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) {
|
fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let block_state = args.world.get_block_state(args.position).await;
|
Box::pin(async move {
|
||||||
let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block);
|
let block_state = args.world.get_block_state(args.position).await;
|
||||||
let powered = block_receives_redstone_power(args.world, args.position).await;
|
let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block);
|
||||||
// check if powered state changed
|
let powered = block_receives_redstone_power(args.world, args.position).await;
|
||||||
if note_props.powered != powered {
|
// check if powered state changed
|
||||||
if powered {
|
if note_props.powered != powered {
|
||||||
Self::play_note(¬e_props, args.world, args.position).await;
|
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
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
@ -80,83 +101,78 @@ impl BlockBehaviour for NoteBlock {
|
|||||||
BlockFlags::NOTIFY_ALL,
|
BlockFlags::NOTIFY_ALL,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
Self::play_note(¬e_props, args.world, args.position).await;
|
||||||
|
|
||||||
|
BlockActionResult::Success
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
fn use_with_item<'a>(
|
||||||
let block_state = args.world.get_block_state(args.position).await;
|
&'a self,
|
||||||
let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block);
|
_args: UseWithItemArgs<'a>,
|
||||||
let next_index = note_props.note.to_index() + 1;
|
) -> BlockFuture<'a, BlockActionResult> {
|
||||||
// Increment and check if max
|
Box::pin(async move {
|
||||||
note_props.note = if next_index >= Integer0To24::variant_count() {
|
// TODO
|
||||||
Integer0To24::from_index(0)
|
BlockActionResult::PassToDefaultBlockAction
|
||||||
} else {
|
})
|
||||||
Integer0To24::from_index(next_index)
|
|
||||||
};
|
|
||||||
args.world
|
|
||||||
.set_block_state(
|
|
||||||
args.position,
|
|
||||||
note_props.to_state_id(args.block),
|
|
||||||
BlockFlags::NOTIFY_ALL,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
Self::play_note(¬e_props, args.world, args.position).await;
|
|
||||||
|
|
||||||
BlockActionResult::Success
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn use_with_item(&self, _args: UseWithItemArgs<'_>) -> BlockActionResult {
|
fn on_synced_block_event<'a>(
|
||||||
// TODO
|
&'a self,
|
||||||
BlockActionResult::PassToDefaultBlockAction
|
args: OnSyncedBlockEventArgs<'a>,
|
||||||
|
) -> BlockFuture<'a, bool> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let block_state = args.world.get_block_state(args.position).await;
|
||||||
|
let note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block);
|
||||||
|
let instrument = note_props.instrument;
|
||||||
|
let pitch = if is_base_block(instrument) {
|
||||||
|
// checks if can be pitched
|
||||||
|
Self::get_note_pitch(note_props.note.to_index())
|
||||||
|
} else {
|
||||||
|
1.0 // default pitch
|
||||||
|
};
|
||||||
|
// check hasCustomSound
|
||||||
|
args.world
|
||||||
|
.play_sound_raw(
|
||||||
|
convert_instrument_to_sound(instrument) as u16,
|
||||||
|
SoundCategory::Records,
|
||||||
|
&args.position.to_f64(),
|
||||||
|
3.0,
|
||||||
|
pitch,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let block_state = args.world.get_block_state(args.position).await;
|
Box::pin(async move {
|
||||||
let note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block);
|
Self::get_state_with_instrument(
|
||||||
let instrument = note_props.instrument;
|
|
||||||
let pitch = if is_base_block(instrument) {
|
|
||||||
// checks if can be pitched
|
|
||||||
Self::get_note_pitch(note_props.note.to_index())
|
|
||||||
} else {
|
|
||||||
1.0 // default pitch
|
|
||||||
};
|
|
||||||
// check hasCustomSound
|
|
||||||
args.world
|
|
||||||
.play_sound_raw(
|
|
||||||
convert_instrument_to_sound(instrument) as u16,
|
|
||||||
SoundCategory::Records,
|
|
||||||
&args.position.to_f64(),
|
|
||||||
3.0,
|
|
||||||
pitch,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
|
||||||
Self::get_state_with_instrument(
|
|
||||||
args.world,
|
|
||||||
args.position,
|
|
||||||
Block::NOTE_BLOCK.default_state.id,
|
|
||||||
args.block,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
|
||||||
&self,
|
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
|
||||||
) -> BlockStateId {
|
|
||||||
if args.direction.to_axis() == Axis::Y {
|
|
||||||
return Self::get_state_with_instrument(
|
|
||||||
args.world,
|
args.world,
|
||||||
args.position,
|
args.position,
|
||||||
args.state_id,
|
Block::NOTE_BLOCK.default_state.id,
|
||||||
args.block,
|
args.block,
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
}
|
})
|
||||||
args.state_id
|
}
|
||||||
|
|
||||||
|
fn get_state_for_neighbor_update<'a>(
|
||||||
|
&'a self,
|
||||||
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
|
Box::pin(async move {
|
||||||
|
if args.direction.to_axis() == Axis::Y {
|
||||||
|
return Self::get_state_with_instrument(
|
||||||
|
args.world,
|
||||||
|
args.position,
|
||||||
|
args.state_id,
|
||||||
|
args.block,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
args.state_id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockDirection, BlockState, FacingExt,
|
Block, BlockDirection, BlockState, FacingExt,
|
||||||
block_properties::{
|
block_properties::{
|
||||||
@ -17,11 +16,8 @@ use pumpkin_world::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
block::{
|
block::{
|
||||||
blocks::redstone::is_emitting_redstone_power,
|
BlockBehaviour, BlockFuture, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs,
|
||||||
{
|
OnSyncedBlockEventArgs, PlacedArgs, blocks::redstone::is_emitting_redstone_power,
|
||||||
BlockBehaviour, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs,
|
|
||||||
OnSyncedBlockEventArgs, PlacedArgs,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
@ -84,162 +80,172 @@ impl PistonBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for PistonBlock {
|
impl BlockBehaviour for PistonBlock {
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
let mut props = PistonProps::default(args.block);
|
Box::pin(async move {
|
||||||
props.extended = false;
|
let mut props = PistonProps::default(args.block);
|
||||||
props.facing = args.player.living_entity.entity.get_facing().opposite();
|
props.extended = false;
|
||||||
props.to_state_id(args.block)
|
props.facing = args.player.living_entity.entity.get_facing().opposite();
|
||||||
|
props.to_state_id(args.block)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn placed(&self, args: PlacedArgs<'_>) {
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
if args.old_state_id == args.state_id {
|
Box::pin(async move {
|
||||||
return;
|
if args.old_state_id == args.state_id {
|
||||||
}
|
return;
|
||||||
try_move(args.world, args.block, args.position).await;
|
}
|
||||||
|
try_move(args.world, args.block, args.position).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) {
|
fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
try_move(args.world, args.block, args.position).await;
|
Box::pin(async move {
|
||||||
|
try_move(args.world, args.block, args.position).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
async fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool {
|
fn on_synced_block_event<'a>(
|
||||||
let (block, world, pos, r#type, data) = (
|
&'a self,
|
||||||
args.block,
|
args: OnSyncedBlockEventArgs<'a>,
|
||||||
args.world,
|
) -> BlockFuture<'a, bool> {
|
||||||
args.position,
|
Box::pin(async move {
|
||||||
args.r#type,
|
let (block, world, pos, r#type, data) = (
|
||||||
args.data,
|
args.block,
|
||||||
);
|
args.world,
|
||||||
|
args.position,
|
||||||
|
args.r#type,
|
||||||
|
args.data,
|
||||||
|
);
|
||||||
|
|
||||||
let state = world.get_block_state(pos).await;
|
let state = world.get_block_state(pos).await;
|
||||||
let mut props = PistonProps::from_state_id(state.id, block);
|
let mut props = PistonProps::from_state_id(state.id, block);
|
||||||
let dir = props.facing.to_block_direction();
|
let dir = props.facing.to_block_direction();
|
||||||
|
|
||||||
// I don't think this is optimal ?
|
// I don't think this is optimal ?
|
||||||
let sticky = block == &Block::STICKY_PISTON;
|
let sticky = block == &Block::STICKY_PISTON;
|
||||||
|
|
||||||
let should_extend = should_extend(world, pos, dir).await;
|
let should_extend = should_extend(world, pos, dir).await;
|
||||||
if should_extend && (r#type == 1 || r#type == 2) {
|
if should_extend && (r#type == 1 || r#type == 2) {
|
||||||
props.extended = true;
|
props.extended = true;
|
||||||
world
|
world
|
||||||
.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS)
|
.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS)
|
||||||
.await;
|
.await;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This may prevents when something happens in the one tick before this function got called
|
|
||||||
if !should_extend && r#type == 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extend Piston
|
|
||||||
if r#type == 0 {
|
|
||||||
if !move_piston(world, dir, pos, true, sticky).await {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
props.extended = true;
|
|
||||||
|
// This may prevents when something happens in the one tick before this function got called
|
||||||
|
if !should_extend && r#type == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend Piston
|
||||||
|
if r#type == 0 {
|
||||||
|
if !move_piston(world, dir, pos, true, sticky).await {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
props.extended = true;
|
||||||
|
world
|
||||||
|
.set_block_state(
|
||||||
|
pos,
|
||||||
|
props.to_state_id(block),
|
||||||
|
BlockFlags::NOTIFY_ALL | BlockFlags::MOVED,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Reduce Piston
|
||||||
|
|
||||||
|
let extended_pos = pos.offset(dir.to_offset());
|
||||||
|
|
||||||
|
if let Some(block_entity) = world.get_block_entity(&extended_pos).await {
|
||||||
|
let piston = block_entity
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<PistonBlockEntity>()
|
||||||
|
.unwrap();
|
||||||
|
piston.finish(world.clone()).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut props = MovingPistonLikeProperties::default(&Block::MOVING_PISTON);
|
||||||
|
props.facing = dir.to_facing();
|
||||||
|
props.r#type = if sticky {
|
||||||
|
PistonType::Sticky
|
||||||
|
} else {
|
||||||
|
PistonType::Normal
|
||||||
|
};
|
||||||
|
|
||||||
world
|
world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
pos,
|
pos,
|
||||||
props.to_state_id(block),
|
props.to_state_id(&Block::MOVING_PISTON),
|
||||||
BlockFlags::NOTIFY_ALL | BlockFlags::MOVED,
|
BlockFlags::FORCE_STATE,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Reduce Piston
|
|
||||||
|
|
||||||
let extended_pos = pos.offset(dir.to_offset());
|
let mut props = PistonProps::default(block);
|
||||||
|
props.facing = BlockDirection::by_index((data & 7) as usize)
|
||||||
|
.unwrap()
|
||||||
|
.to_facing();
|
||||||
|
|
||||||
if let Some(block_entity) = world.get_block_entity(&extended_pos).await {
|
|
||||||
let piston = block_entity
|
|
||||||
.as_any()
|
|
||||||
.downcast_ref::<PistonBlockEntity>()
|
|
||||||
.unwrap();
|
|
||||||
piston.finish(world.clone()).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut props = MovingPistonLikeProperties::default(&Block::MOVING_PISTON);
|
|
||||||
props.facing = dir.to_facing();
|
|
||||||
props.r#type = if sticky {
|
|
||||||
PistonType::Sticky
|
|
||||||
} else {
|
|
||||||
PistonType::Normal
|
|
||||||
};
|
|
||||||
|
|
||||||
world
|
|
||||||
.set_block_state(
|
|
||||||
pos,
|
|
||||||
props.to_state_id(&Block::MOVING_PISTON),
|
|
||||||
BlockFlags::FORCE_STATE,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let mut props = PistonProps::default(block);
|
|
||||||
props.facing = BlockDirection::by_index((data & 7) as usize)
|
|
||||||
.unwrap()
|
|
||||||
.to_facing();
|
|
||||||
|
|
||||||
world
|
|
||||||
.add_block_entity(Arc::new(PistonBlockEntity {
|
|
||||||
position: *pos,
|
|
||||||
facing: dir,
|
|
||||||
pushed_block_state: BlockState::from_id(props.to_state_id(block)),
|
|
||||||
current_progress: 0.0.into(),
|
|
||||||
last_progress: 0.0.into(),
|
|
||||||
extending: false,
|
|
||||||
source: true,
|
|
||||||
}))
|
|
||||||
.await;
|
|
||||||
|
|
||||||
world.update_neighbors(pos, None).await;
|
|
||||||
if sticky {
|
|
||||||
let pos = pos.offset_dir(dir.to_offset(), 2);
|
|
||||||
let (block, state) = world.get_block_and_state(&pos).await;
|
|
||||||
let mut bl2 = false;
|
|
||||||
if block == &Block::MOVING_PISTON
|
|
||||||
&& let Some(entity) = world.get_block_entity(&pos).await
|
|
||||||
{
|
|
||||||
let piston = entity.as_any().downcast_ref::<PistonBlockEntity>().unwrap();
|
|
||||||
if piston.facing == dir && piston.extending {
|
|
||||||
piston.finish(world.clone()).await;
|
|
||||||
bl2 = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !bl2 {
|
|
||||||
if r#type == 1
|
|
||||||
&& !state.is_air()
|
|
||||||
&& Self::is_movable(block, state, dir, false, dir)
|
|
||||||
&& (state.piston_behavior == PistonBehavior::Normal
|
|
||||||
|| block == &Block::PISTON
|
|
||||||
|| block == &Block::STICKY_PISTON)
|
|
||||||
{
|
|
||||||
move_piston(world, dir, &pos, false, sticky).await;
|
|
||||||
} else {
|
|
||||||
// remove
|
|
||||||
world
|
|
||||||
.set_block_state(
|
|
||||||
&extended_pos,
|
|
||||||
Block::AIR.default_state.id,
|
|
||||||
BlockFlags::NOTIFY_ALL,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// remove
|
|
||||||
world
|
world
|
||||||
.set_block_state(
|
.add_block_entity(Arc::new(PistonBlockEntity {
|
||||||
&extended_pos,
|
position: *pos,
|
||||||
Block::AIR.default_state.id,
|
facing: dir,
|
||||||
BlockFlags::NOTIFY_ALL,
|
pushed_block_state: BlockState::from_id(props.to_state_id(block)),
|
||||||
)
|
current_progress: 0.0.into(),
|
||||||
|
last_progress: 0.0.into(),
|
||||||
|
extending: false,
|
||||||
|
source: true,
|
||||||
|
}))
|
||||||
.await;
|
.await;
|
||||||
}
|
|
||||||
return true;
|
world.update_neighbors(pos, None).await;
|
||||||
|
if sticky {
|
||||||
|
let pos = pos.offset_dir(dir.to_offset(), 2);
|
||||||
|
let (block, state) = world.get_block_and_state(&pos).await;
|
||||||
|
let mut bl2 = false;
|
||||||
|
if block == &Block::MOVING_PISTON
|
||||||
|
&& let Some(entity) = world.get_block_entity(&pos).await
|
||||||
|
{
|
||||||
|
let piston = entity.as_any().downcast_ref::<PistonBlockEntity>().unwrap();
|
||||||
|
if piston.facing == dir && piston.extending {
|
||||||
|
piston.finish(world.clone()).await;
|
||||||
|
bl2 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !bl2 {
|
||||||
|
if r#type == 1
|
||||||
|
&& !state.is_air()
|
||||||
|
&& Self::is_movable(block, state, dir, false, dir)
|
||||||
|
&& (state.piston_behavior == PistonBehavior::Normal
|
||||||
|
|| block == &Block::PISTON
|
||||||
|
|| block == &Block::STICKY_PISTON)
|
||||||
|
{
|
||||||
|
move_piston(world, dir, &pos, false, sticky).await;
|
||||||
|
} else {
|
||||||
|
// remove
|
||||||
|
world
|
||||||
|
.set_block_state(
|
||||||
|
&extended_pos,
|
||||||
|
Block::AIR.default_state.id,
|
||||||
|
BlockFlags::NOTIFY_ALL,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// remove
|
||||||
|
world
|
||||||
|
.set_block_state(
|
||||||
|
&extended_pos,
|
||||||
|
Block::AIR.default_state.id,
|
||||||
|
BlockFlags::NOTIFY_ALL,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
use pumpkin_data::{Block, FacingExt};
|
use pumpkin_data::{Block, FacingExt};
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_world::world::BlockFlags;
|
use pumpkin_world::world::BlockFlags;
|
||||||
|
|
||||||
use crate::block::BlockBehaviour;
|
|
||||||
use crate::block::BrokenArgs;
|
use crate::block::BrokenArgs;
|
||||||
|
use crate::block::{BlockBehaviour, BlockFuture};
|
||||||
|
|
||||||
use super::piston::PistonProps;
|
use super::piston::PistonProps;
|
||||||
|
|
||||||
@ -14,22 +13,23 @@ pub(crate) type MovingPistonProps = pumpkin_data::block_properties::MovingPiston
|
|||||||
#[pumpkin_block("minecraft:moving_piston")]
|
#[pumpkin_block("minecraft:moving_piston")]
|
||||||
pub struct PistonExtensionBlock;
|
pub struct PistonExtensionBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for PistonExtensionBlock {
|
impl BlockBehaviour for PistonExtensionBlock {
|
||||||
async fn broken(&self, args: BrokenArgs<'_>) {
|
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let props = MovingPistonProps::from_state_id(args.state.id, &Block::MOVING_PISTON);
|
Box::pin(async move {
|
||||||
let pos = args
|
let props = MovingPistonProps::from_state_id(args.state.id, &Block::MOVING_PISTON);
|
||||||
.position
|
let pos = args
|
||||||
.offset(props.facing.opposite().to_block_direction().to_offset());
|
.position
|
||||||
let (new_block, new_state) = args.world.get_block_and_state_id(&pos).await;
|
.offset(props.facing.opposite().to_block_direction().to_offset());
|
||||||
if &Block::PISTON == new_block || &Block::STICKY_PISTON == new_block {
|
let (new_block, new_state) = args.world.get_block_and_state_id(&pos).await;
|
||||||
let props = PistonProps::from_state_id(new_state, new_block);
|
if &Block::PISTON == new_block || &Block::STICKY_PISTON == new_block {
|
||||||
if props.extended {
|
let props = PistonProps::from_state_id(new_state, new_block);
|
||||||
// TODO: use player
|
if props.extended {
|
||||||
args.world
|
// TODO: use player
|
||||||
.break_block(&pos, None, BlockFlags::SKIP_DROPS)
|
args.world
|
||||||
.await;
|
.break_block(&pos, None, BlockFlags::SKIP_DROPS)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
use pumpkin_data::{Block, FacingExt};
|
use pumpkin_data::{Block, FacingExt};
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_world::world::BlockFlags;
|
use pumpkin_world::world::BlockFlags;
|
||||||
|
|
||||||
use crate::block::BlockBehaviour;
|
|
||||||
use crate::block::BrokenArgs;
|
use crate::block::BrokenArgs;
|
||||||
|
use crate::block::{BlockBehaviour, BlockFuture};
|
||||||
|
|
||||||
use super::piston::PistonProps;
|
use super::piston::PistonProps;
|
||||||
|
|
||||||
@ -14,22 +13,23 @@ pub(crate) type PistonHeadProperties = pumpkin_data::block_properties::PistonHea
|
|||||||
#[pumpkin_block("minecraft:piston_head")]
|
#[pumpkin_block("minecraft:piston_head")]
|
||||||
pub struct PistonHeadBlock;
|
pub struct PistonHeadBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for PistonHeadBlock {
|
impl BlockBehaviour for PistonHeadBlock {
|
||||||
async fn broken(&self, args: BrokenArgs<'_>) {
|
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
let props = PistonHeadProperties::from_state_id(args.state.id, &Block::PISTON_HEAD);
|
Box::pin(async move {
|
||||||
let pos = args
|
let props = PistonHeadProperties::from_state_id(args.state.id, &Block::PISTON_HEAD);
|
||||||
.position
|
let pos = args
|
||||||
.offset(props.facing.opposite().to_block_direction().to_offset());
|
.position
|
||||||
let (new_block, new_state) = args.world.get_block_and_state_id(&pos).await;
|
.offset(props.facing.opposite().to_block_direction().to_offset());
|
||||||
if &Block::PISTON == new_block || &Block::STICKY_PISTON == new_block {
|
let (new_block, new_state) = args.world.get_block_and_state_id(&pos).await;
|
||||||
let props = PistonProps::from_state_id(new_state, new_block);
|
if &Block::PISTON == new_block || &Block::STICKY_PISTON == new_block {
|
||||||
if props.extended {
|
let props = PistonProps::from_state_id(new_state, new_block);
|
||||||
// TODO: use player
|
if props.extended {
|
||||||
args.world
|
// TODO: use player
|
||||||
.break_block(&pos, None, BlockFlags::SKIP_DROPS)
|
args.world
|
||||||
.await;
|
.break_block(&pos, None, BlockFlags::SKIP_DROPS)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
|
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
||||||
blocks::plant::PlantBlockBase,
|
blocks::plant::PlantBlockBase,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -19,23 +18,26 @@ impl BlockMetadata for BushBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for BushBlock {
|
impl BlockBehaviour for BushBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
Box::pin(async move {
|
||||||
|
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
Box::pin(async move {
|
||||||
self,
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
args.world,
|
self,
|
||||||
args.position,
|
args.world,
|
||||||
args.state_id,
|
args.position,
|
||||||
)
|
args.state_id,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::block_properties::{
|
use pumpkin_data::block_properties::{
|
||||||
BlockProperties, EnumVariants, Integer0To3, NetherWartLikeProperties,
|
BlockProperties, EnumVariants, Integer0To3, NetherWartLikeProperties,
|
||||||
@ -9,37 +8,48 @@ use rand::Rng;
|
|||||||
|
|
||||||
use crate::block::blocks::plant::PlantBlockBase;
|
use crate::block::blocks::plant::PlantBlockBase;
|
||||||
use crate::block::blocks::plant::crop::CropBlockBase;
|
use crate::block::blocks::plant::crop::CropBlockBase;
|
||||||
use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs};
|
use crate::block::{
|
||||||
|
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs,
|
||||||
|
};
|
||||||
|
|
||||||
type BeetrootProperties = NetherWartLikeProperties;
|
type BeetrootProperties = NetherWartLikeProperties;
|
||||||
|
|
||||||
#[pumpkin_block("minecraft:beetroots")]
|
#[pumpkin_block("minecraft:beetroots")]
|
||||||
pub struct BeetrootBlock;
|
pub struct BeetrootBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for BeetrootBlock {
|
impl BlockBehaviour for BeetrootBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
<Self as CropBlockBase>::can_plant_on_top(self, args.block_accessor, &args.position.down())
|
Box::pin(async move {
|
||||||
|
<Self as CropBlockBase>::can_plant_on_top(
|
||||||
|
self,
|
||||||
|
args.block_accessor,
|
||||||
|
&args.position.down(),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
Box::pin(async move {
|
||||||
self,
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
args.world,
|
self,
|
||||||
args.position,
|
args.world,
|
||||||
args.state_id,
|
args.position,
|
||||||
)
|
args.state_id,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn random_tick(&self, args: RandomTickArgs<'_>) {
|
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
if rand::rng().random_range(0..2) != 0 {
|
Box::pin(async move {
|
||||||
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
|
if rand::rng().random_range(0..2) != 0 {
|
||||||
}
|
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,36 +1,46 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
|
|
||||||
use crate::block::blocks::plant::PlantBlockBase;
|
use crate::block::blocks::plant::PlantBlockBase;
|
||||||
use crate::block::blocks::plant::crop::CropBlockBase;
|
use crate::block::blocks::plant::crop::CropBlockBase;
|
||||||
use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs};
|
use crate::block::{
|
||||||
|
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs,
|
||||||
|
};
|
||||||
|
|
||||||
#[pumpkin_block("minecraft:carrots")]
|
#[pumpkin_block("minecraft:carrots")]
|
||||||
pub struct CarrotBlock;
|
pub struct CarrotBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for CarrotBlock {
|
impl BlockBehaviour for CarrotBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
<Self as CropBlockBase>::can_plant_on_top(self, args.block_accessor, &args.position.down())
|
Box::pin(async move {
|
||||||
|
<Self as CropBlockBase>::can_plant_on_top(
|
||||||
|
self,
|
||||||
|
args.block_accessor,
|
||||||
|
&args.position.down(),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
Box::pin(async move {
|
||||||
self,
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
args.world,
|
self,
|
||||||
args.position,
|
args.world,
|
||||||
args.state_id,
|
args.position,
|
||||||
)
|
args.state_id,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn random_tick(&self, args: RandomTickArgs<'_>) {
|
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
|
Box::pin(async move {
|
||||||
|
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block,
|
Block,
|
||||||
block_properties::{
|
block_properties::{
|
||||||
@ -9,7 +8,7 @@ use pumpkin_util::math::position::BlockPos;
|
|||||||
use pumpkin_world::{BlockStateId, world::BlockAccessor};
|
use pumpkin_world::{BlockStateId, world::BlockAccessor};
|
||||||
|
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
||||||
blocks::plant::PlantBlockBase,
|
blocks::plant::PlantBlockBase,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -49,31 +48,34 @@ impl AttachedStemBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for AttachedStemBlock {
|
impl BlockBehaviour for AttachedStemBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
Box::pin(async move {
|
||||||
|
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
let props = AttachedStemProperties::from_state_id(args.state_id, args.block);
|
Box::pin(async move {
|
||||||
if args.direction.to_horizontal_facing() == Some(props.facing)
|
let props = AttachedStemProperties::from_state_id(args.state_id, args.block);
|
||||||
&& args.neighbor_state_id != Self::get_gourd(args.block).default_state.id
|
if args.direction.to_horizontal_facing() == Some(props.facing)
|
||||||
{
|
&& args.neighbor_state_id != Self::get_gourd(args.block).default_state.id
|
||||||
let mut props = StemProperties::default(Self::get_stem(args.block));
|
{
|
||||||
props.age = Integer0To7::from_index(7);
|
let mut props = StemProperties::default(Self::get_stem(args.block));
|
||||||
return props.to_state_id(Self::get_stem(args.block));
|
props.age = Integer0To7::from_index(7);
|
||||||
}
|
return props.to_state_id(Self::get_stem(args.block));
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
}
|
||||||
self,
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
args.world,
|
self,
|
||||||
args.position,
|
args.world,
|
||||||
args.state_id,
|
args.position,
|
||||||
)
|
args.state_id,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs,
|
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
||||||
|
RandomTickArgs,
|
||||||
blocks::plant::{PlantBlockBase, crop::get_available_moisture},
|
blocks::plant::{PlantBlockBase, crop::get_available_moisture},
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockDirection,
|
Block, BlockDirection,
|
||||||
block_properties::{
|
block_properties::{
|
||||||
@ -63,66 +63,75 @@ impl StemBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for StemBlock {
|
impl BlockBehaviour for StemBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
Box::pin(async move {
|
||||||
|
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
Box::pin(async move {
|
||||||
self,
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
args.world,
|
self,
|
||||||
args.position,
|
args.world,
|
||||||
args.state_id,
|
args.position,
|
||||||
)
|
args.state_id,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn random_tick(&self, args: RandomTickArgs<'_>) {
|
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
// TODO add light level check
|
Box::pin(async move {
|
||||||
let f: f32 = get_available_moisture(args.world, args.position, args.block).await;
|
// TODO add light level check
|
||||||
if rand::rng().random_range(0..=(25.0 / f).floor() as i32) == 0 {
|
let f: f32 = get_available_moisture(args.world, args.position, args.block).await;
|
||||||
let (block, state) = args.world.get_block_and_state_id(args.position).await;
|
if rand::rng().random_range(0..=(25.0 / f).floor() as i32) == 0 {
|
||||||
let props = StemProperties::from_state_id(state, block);
|
let (block, state) = args.world.get_block_and_state_id(args.position).await;
|
||||||
let age = i32::from(props.age.to_index());
|
let props = StemProperties::from_state_id(state, block);
|
||||||
if age < 7 {
|
let age = i32::from(props.age.to_index());
|
||||||
args.world
|
if age < 7 {
|
||||||
.set_block_state(
|
|
||||||
args.position,
|
|
||||||
Self::state_with_age(block, state, age + 1),
|
|
||||||
BlockFlags::NOTIFY_NEIGHBORS,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
} else {
|
|
||||||
let dir = BlockDirection::random_horizontal(&mut RandomGenerator::Xoroshiro(
|
|
||||||
Xoroshiro::from_seed(rand::rng().random()),
|
|
||||||
));
|
|
||||||
let plant_block_pos = args.position.offset(dir.to_offset());
|
|
||||||
let plant_block_state = args.world.get_block_state(&plant_block_pos).await;
|
|
||||||
let under_block: &Block = args.world.get_block(&plant_block_pos.down()).await;
|
|
||||||
if plant_block_state.is_air()
|
|
||||||
&& (under_block == &Block::FARMLAND
|
|
||||||
|| under_block.has_tag(&tag::Block::MINECRAFT_DIRT))
|
|
||||||
{
|
|
||||||
let attached_stem = Self::get_attached_stem(dir, block);
|
|
||||||
let gourd = Self::get_gourd(block);
|
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
&plant_block_pos,
|
args.position,
|
||||||
gourd.default_state.id,
|
Self::state_with_age(block, state, age + 1),
|
||||||
BlockFlags::NOTIFY_NEIGHBORS,
|
BlockFlags::NOTIFY_NEIGHBORS,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
args.world
|
} else {
|
||||||
.set_block_state(args.position, attached_stem, BlockFlags::NOTIFY_NEIGHBORS)
|
let dir = BlockDirection::random_horizontal(&mut RandomGenerator::Xoroshiro(
|
||||||
.await;
|
Xoroshiro::from_seed(rand::rng().random()),
|
||||||
|
));
|
||||||
|
let plant_block_pos = args.position.offset(dir.to_offset());
|
||||||
|
let plant_block_state = args.world.get_block_state(&plant_block_pos).await;
|
||||||
|
let under_block: &Block = args.world.get_block(&plant_block_pos.down()).await;
|
||||||
|
if plant_block_state.is_air()
|
||||||
|
&& (under_block == &Block::FARMLAND
|
||||||
|
|| under_block.has_tag(&tag::Block::MINECRAFT_DIRT))
|
||||||
|
{
|
||||||
|
let attached_stem = Self::get_attached_stem(dir, block);
|
||||||
|
let gourd = Self::get_gourd(block);
|
||||||
|
args.world
|
||||||
|
.set_block_state(
|
||||||
|
&plant_block_pos,
|
||||||
|
gourd.default_state.id,
|
||||||
|
BlockFlags::NOTIFY_NEIGHBORS,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
args.world
|
||||||
|
.set_block_state(
|
||||||
|
args.position,
|
||||||
|
attached_stem,
|
||||||
|
BlockFlags::NOTIFY_NEIGHBORS,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,36 +1,46 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
|
|
||||||
use crate::block::blocks::plant::PlantBlockBase;
|
use crate::block::blocks::plant::PlantBlockBase;
|
||||||
use crate::block::blocks::plant::crop::CropBlockBase;
|
use crate::block::blocks::plant::crop::CropBlockBase;
|
||||||
use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs};
|
use crate::block::{
|
||||||
|
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs,
|
||||||
|
};
|
||||||
|
|
||||||
#[pumpkin_block("minecraft:potatoes")]
|
#[pumpkin_block("minecraft:potatoes")]
|
||||||
pub struct PotatoBlock;
|
pub struct PotatoBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for PotatoBlock {
|
impl BlockBehaviour for PotatoBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
<Self as CropBlockBase>::can_plant_on_top(self, args.block_accessor, &args.position.down())
|
Box::pin(async move {
|
||||||
|
<Self as CropBlockBase>::can_plant_on_top(
|
||||||
|
self,
|
||||||
|
args.block_accessor,
|
||||||
|
&args.position.down(),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
Box::pin(async move {
|
||||||
self,
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
args.world,
|
self,
|
||||||
args.position,
|
args.world,
|
||||||
args.state_id,
|
args.position,
|
||||||
)
|
args.state_id,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn random_tick(&self, args: RandomTickArgs<'_>) {
|
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
|
Box::pin(async move {
|
||||||
|
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::block_properties::{
|
use pumpkin_data::block_properties::{
|
||||||
BlockProperties, EnumVariants, Integer0To1, TorchflowerCropLikeProperties,
|
BlockProperties, EnumVariants, Integer0To1, TorchflowerCropLikeProperties,
|
||||||
@ -9,37 +8,48 @@ use rand::Rng;
|
|||||||
|
|
||||||
use crate::block::blocks::plant::PlantBlockBase;
|
use crate::block::blocks::plant::PlantBlockBase;
|
||||||
use crate::block::blocks::plant::crop::CropBlockBase;
|
use crate::block::blocks::plant::crop::CropBlockBase;
|
||||||
use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs};
|
use crate::block::{
|
||||||
|
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs,
|
||||||
|
};
|
||||||
|
|
||||||
type TorchFlowerProperties = TorchflowerCropLikeProperties;
|
type TorchFlowerProperties = TorchflowerCropLikeProperties;
|
||||||
|
|
||||||
#[pumpkin_block("minecraft:torchflower_crop")]
|
#[pumpkin_block("minecraft:torchflower_crop")]
|
||||||
pub struct TorchFlowerBlock;
|
pub struct TorchFlowerBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for TorchFlowerBlock {
|
impl BlockBehaviour for TorchFlowerBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
<Self as CropBlockBase>::can_plant_on_top(self, args.block_accessor, &args.position.down())
|
Box::pin(async move {
|
||||||
|
<Self as CropBlockBase>::can_plant_on_top(
|
||||||
|
self,
|
||||||
|
args.block_accessor,
|
||||||
|
&args.position.down(),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
Box::pin(async move {
|
||||||
self,
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
args.world,
|
self,
|
||||||
args.position,
|
args.world,
|
||||||
args.state_id,
|
args.position,
|
||||||
)
|
args.state_id,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn random_tick(&self, args: RandomTickArgs<'_>) {
|
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
if rand::rng().random_range(0..2) != 0 {
|
Box::pin(async move {
|
||||||
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
|
if rand::rng().random_range(0..2) != 0 {
|
||||||
}
|
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,36 +1,46 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
|
|
||||||
use crate::block::blocks::plant::PlantBlockBase;
|
use crate::block::blocks::plant::PlantBlockBase;
|
||||||
use crate::block::blocks::plant::crop::CropBlockBase;
|
use crate::block::blocks::plant::crop::CropBlockBase;
|
||||||
use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs};
|
use crate::block::{
|
||||||
|
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs,
|
||||||
|
};
|
||||||
|
|
||||||
#[pumpkin_block("minecraft:wheat")]
|
#[pumpkin_block("minecraft:wheat")]
|
||||||
pub struct WheatBlock;
|
pub struct WheatBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for WheatBlock {
|
impl BlockBehaviour for WheatBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
<Self as CropBlockBase>::can_plant_on_top(self, args.block_accessor, &args.position.down())
|
Box::pin(async move {
|
||||||
|
<Self as CropBlockBase>::can_plant_on_top(
|
||||||
|
self,
|
||||||
|
args.block_accessor,
|
||||||
|
&args.position.down(),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
Box::pin(async move {
|
||||||
self,
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
args.world,
|
self,
|
||||||
args.position,
|
args.world,
|
||||||
args.state_id,
|
args.position,
|
||||||
)
|
args.state_id,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn random_tick(&self, args: RandomTickArgs<'_>) {
|
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
|
Box::pin(async move {
|
||||||
|
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::tag;
|
use pumpkin_data::tag;
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_world::{BlockStateId, world::BlockAccessor};
|
use pumpkin_world::{BlockStateId, world::BlockAccessor};
|
||||||
|
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
||||||
blocks::plant::PlantBlockBase,
|
blocks::plant::PlantBlockBase,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -21,23 +20,26 @@ impl BlockMetadata for DryVegetationBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for DryVegetationBlock {
|
impl BlockBehaviour for DryVegetationBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
Box::pin(async move {
|
||||||
|
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
Box::pin(async move {
|
||||||
self,
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
args.world,
|
self,
|
||||||
args.position,
|
args.world,
|
||||||
args.state_id,
|
args.position,
|
||||||
)
|
args.state_id,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
||||||
use pumpkin_registry::VanillaDimensionType;
|
use pumpkin_registry::VanillaDimensionType;
|
||||||
@ -6,7 +5,9 @@ use pumpkin_world::BlockStateId;
|
|||||||
use pumpkin_world::world::BlockFlags;
|
use pumpkin_world::world::BlockFlags;
|
||||||
|
|
||||||
use crate::block::blocks::plant::PlantBlockBase;
|
use crate::block::blocks::plant::PlantBlockBase;
|
||||||
use crate::block::{BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs};
|
use crate::block::{
|
||||||
|
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::block::RandomTickArgs;
|
use crate::block::RandomTickArgs;
|
||||||
|
|
||||||
@ -22,57 +23,62 @@ impl BlockMetadata for FlowerBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for FlowerBlock {
|
impl BlockBehaviour for FlowerBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
Box::pin(async move {
|
||||||
|
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
Box::pin(async move {
|
||||||
self,
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
args.world,
|
self,
|
||||||
args.position,
|
args.world,
|
||||||
args.state_id,
|
args.position,
|
||||||
)
|
args.state_id,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn random_tick(&self, args: RandomTickArgs<'_>) {
|
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
//TODO add trail particule
|
Box::pin(async move {
|
||||||
if (args
|
//TODO add trail particule
|
||||||
.world
|
if (args
|
||||||
.dimension_type
|
|
||||||
.eq(&VanillaDimensionType::Overworld)
|
|
||||||
|| args
|
|
||||||
.world
|
.world
|
||||||
.dimension_type
|
.dimension_type
|
||||||
.eq(&VanillaDimensionType::OverworldCaves))
|
.eq(&VanillaDimensionType::Overworld)
|
||||||
&& args.block.eq(&Block::CLOSED_EYEBLOSSOM)
|
|| args
|
||||||
&& args.world.level_time.lock().await.time_of_day % 24000 > 14500
|
.world
|
||||||
{
|
.dimension_type
|
||||||
args.world
|
.eq(&VanillaDimensionType::OverworldCaves))
|
||||||
.set_block_state(
|
&& args.block.eq(&Block::CLOSED_EYEBLOSSOM)
|
||||||
args.position,
|
&& args.world.level_time.lock().await.time_of_day % 24000 > 14500
|
||||||
Block::OPEN_EYEBLOSSOM.default_state.id,
|
{
|
||||||
BlockFlags::NOTIFY_ALL,
|
args.world
|
||||||
)
|
.set_block_state(
|
||||||
.await;
|
args.position,
|
||||||
}
|
Block::OPEN_EYEBLOSSOM.default_state.id,
|
||||||
if args.block.eq(&Block::OPEN_EYEBLOSSOM)
|
BlockFlags::NOTIFY_ALL,
|
||||||
&& args.world.level_time.lock().await.time_of_day % 24000 <= 14500
|
)
|
||||||
{
|
.await;
|
||||||
args.world
|
}
|
||||||
.set_block_state(
|
if args.block.eq(&Block::OPEN_EYEBLOSSOM)
|
||||||
args.position,
|
&& args.world.level_time.lock().await.time_of_day % 24000 <= 14500
|
||||||
Block::CLOSED_EYEBLOSSOM.default_state.id,
|
{
|
||||||
BlockFlags::NOTIFY_ALL,
|
args.world
|
||||||
)
|
.set_block_state(
|
||||||
.await;
|
args.position,
|
||||||
}
|
Block::CLOSED_EYEBLOSSOM.default_state.id,
|
||||||
|
BlockFlags::NOTIFY_ALL,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
use pumpkin_data::{Block, tag};
|
use pumpkin_data::{Block, tag};
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
@ -6,8 +5,8 @@ use pumpkin_world::BlockStateId;
|
|||||||
use crate::block::blocks::plant::PlantBlockBase;
|
use crate::block::blocks::plant::PlantBlockBase;
|
||||||
|
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs,
|
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, CanUpdateAtArgs,
|
||||||
OnPlaceArgs,
|
GetStateForNeighborUpdateArgs, OnPlaceArgs,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::segmented::Segmented;
|
use super::segmented::Segmented;
|
||||||
@ -26,32 +25,35 @@ impl BlockMetadata for FlowerbedBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for FlowerbedBlock {
|
impl BlockBehaviour for FlowerbedBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
let block_below = args.block_accessor.get_block(&args.position.down()).await;
|
Box::pin(async move {
|
||||||
block_below.has_tag(&tag::Block::MINECRAFT_DIRT) || block_below == &Block::FARMLAND
|
let block_below = args.block_accessor.get_block(&args.position.down()).await;
|
||||||
|
block_below.has_tag(&tag::Block::MINECRAFT_DIRT) || block_below == &Block::FARMLAND
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn can_update_at(&self, args: CanUpdateAtArgs<'_>) -> bool {
|
fn can_update_at<'a>(&'a self, args: CanUpdateAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
Segmented::can_update_at(self, args).await
|
Box::pin(async move { Segmented::can_update_at(self, args).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
Segmented::on_place(self, args).await
|
Box::pin(async move { Segmented::on_place(self, args).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
Box::pin(async move {
|
||||||
self,
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
args.world,
|
self,
|
||||||
args.position,
|
args.world,
|
||||||
args.state_id,
|
args.position,
|
||||||
)
|
args.state_id,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::{Block, BlockDirection};
|
use pumpkin_data::{Block, BlockDirection};
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
|
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs,
|
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, CanUpdateAtArgs,
|
||||||
OnPlaceArgs,
|
GetStateForNeighborUpdateArgs, OnPlaceArgs,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::segmented::Segmented;
|
use super::segmented::Segmented;
|
||||||
@ -23,35 +22,38 @@ impl BlockMetadata for LeafLitterBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for LeafLitterBlock {
|
impl BlockBehaviour for LeafLitterBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
let block_below = args
|
Box::pin(async move {
|
||||||
.block_accessor
|
let block_below = args
|
||||||
.get_block_state(&args.position.down())
|
.block_accessor
|
||||||
.await;
|
.get_block_state(&args.position.down())
|
||||||
block_below.is_side_solid(BlockDirection::Up)
|
.await;
|
||||||
|
block_below.is_side_solid(BlockDirection::Up)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn can_update_at(&self, args: CanUpdateAtArgs<'_>) -> bool {
|
fn can_update_at<'a>(&'a self, args: CanUpdateAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
Segmented::can_update_at(self, args).await
|
Box::pin(async move { Segmented::can_update_at(self, args).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
Segmented::on_place(self, args).await
|
Box::pin(async move { Segmented::on_place(self, args).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
if args.direction == BlockDirection::Down {
|
Box::pin(async move {
|
||||||
let block_below_state = args.world.get_block_state(&args.position.down()).await;
|
if args.direction == BlockDirection::Down {
|
||||||
if !block_below_state.is_side_solid(BlockDirection::Up) {
|
let block_below_state = args.world.get_block_state(&args.position.down()).await;
|
||||||
return Block::AIR.default_state.id;
|
if !block_below_state.is_side_solid(BlockDirection::Up) {
|
||||||
|
return Block::AIR.default_state.id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
args.state_id
|
||||||
args.state_id
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
@ -7,45 +6,50 @@ use pumpkin_world::{
|
|||||||
world::{BlockAccessor, BlockFlags},
|
world::{BlockAccessor, BlockFlags},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::block::{GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase};
|
use crate::block::{BlockFuture, GetStateForNeighborUpdateArgs, blocks::plant::PlantBlockBase};
|
||||||
|
|
||||||
use crate::block::{BlockBehaviour, CanPlaceAtArgs, OnEntityCollisionArgs};
|
use crate::block::{BlockBehaviour, CanPlaceAtArgs, OnEntityCollisionArgs};
|
||||||
|
|
||||||
#[pumpkin_block("minecraft:lily_pad")]
|
#[pumpkin_block("minecraft:lily_pad")]
|
||||||
pub struct LilyPadBlock;
|
pub struct LilyPadBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for LilyPadBlock {
|
impl BlockBehaviour for LilyPadBlock {
|
||||||
async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) {
|
fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
// Proberbly not the best solution, but works
|
Box::pin(async move {
|
||||||
if args
|
// Proberbly not the best solution, but works
|
||||||
.entity
|
if args
|
||||||
.get_entity()
|
.entity
|
||||||
.entity_type
|
.get_entity()
|
||||||
.resource_name
|
.entity_type
|
||||||
.ends_with("_boat")
|
.resource_name
|
||||||
{
|
.ends_with("_boat")
|
||||||
args.world
|
{
|
||||||
.break_block(args.position, None, BlockFlags::empty())
|
args.world
|
||||||
.await;
|
.break_block(args.position, None, BlockFlags::empty())
|
||||||
}
|
.await;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
Box::pin(async move {
|
||||||
|
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
Box::pin(async move {
|
||||||
self,
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
args.world,
|
self,
|
||||||
args.position,
|
args.world,
|
||||||
args.state_id,
|
args.position,
|
||||||
)
|
args.state_id,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::tag;
|
use pumpkin_data::tag;
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_world::{BlockStateId, world::BlockAccessor};
|
use pumpkin_world::{BlockStateId, world::BlockAccessor};
|
||||||
|
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
BlockBehaviour, BlockFuture, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
|
||||||
blocks::plant::PlantBlockBase,
|
blocks::plant::PlantBlockBase,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -21,23 +20,26 @@ impl BlockMetadata for MushroomPlantBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for MushroomPlantBlock {
|
impl BlockBehaviour for MushroomPlantBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
Box::pin(async move {
|
||||||
|
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
Box::pin(async move {
|
||||||
self,
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
args.world,
|
self,
|
||||||
args.position,
|
args.world,
|
||||||
args.state_id,
|
args.position,
|
||||||
)
|
args.state_id,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
@ -6,28 +5,31 @@ use pumpkin_world::BlockStateId;
|
|||||||
use pumpkin_world::world::BlockAccessor;
|
use pumpkin_world::world::BlockAccessor;
|
||||||
|
|
||||||
use crate::block::blocks::plant::PlantBlockBase;
|
use crate::block::blocks::plant::PlantBlockBase;
|
||||||
use crate::block::{BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs};
|
use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs};
|
||||||
|
|
||||||
#[pumpkin_block("minecraft:nether_wart")]
|
#[pumpkin_block("minecraft:nether_wart")]
|
||||||
pub struct NetherWartBlock;
|
pub struct NetherWartBlock;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl BlockBehaviour for NetherWartBlock {
|
impl BlockBehaviour for NetherWartBlock {
|
||||||
async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
Box::pin(async move {
|
||||||
|
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_for_neighbor_update(
|
fn get_state_for_neighbor_update<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
args: GetStateForNeighborUpdateArgs<'_>,
|
args: GetStateForNeighborUpdateArgs<'a>,
|
||||||
) -> BlockStateId {
|
) -> BlockFuture<'a, BlockStateId> {
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
Box::pin(async move {
|
||||||
self,
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
args.world,
|
self,
|
||||||
args.position,
|
args.world,
|
||||||
args.state_id,
|
args.position,
|
||||||
)
|
args.state_id,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user