remove async-trait entirely

This commit is contained in:
Alexander Medvedev 2025-12-04 23:50:00 +01:00
parent ae751f3a83
commit 70b75a0ac5
38 changed files with 1282 additions and 1017 deletions

7
Cargo.lock generated
View File

@ -1733,9 +1733,9 @@ checksum = "c505b3e17ed6b70a7ed2e67fbb2c560ee327353556120d6e72f5232b6880d536"
[[package]]
name = "mio"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
dependencies = [
"libc",
"log",
@ -2167,7 +2167,6 @@ dependencies = [
name = "pumpkin"
version = "0.1.0-dev+1.21.10"
dependencies = [
"async-trait",
"base64 0.22.1",
"bytes",
"console-subscriber",
@ -2252,7 +2251,6 @@ dependencies = [
name = "pumpkin-inventory"
version = "0.1.0-dev+1.21.10"
dependencies = [
"async-trait",
"crossbeam-utils",
"log",
"pumpkin-data",
@ -2347,7 +2345,6 @@ dependencies = [
name = "pumpkin-world"
version = "0.1.0-dev+1.21.10"
dependencies = [
"async-trait",
"bitflags",
"bytes",
"criterion",

View File

@ -47,7 +47,5 @@ uuid = { version = "1.19", features = ["serde", "v3", "v4"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
async-trait = "0.1"
tempfile = { version = "3.23.0", default-features = false }
proc-macro-error2 = "2"

View File

@ -68,7 +68,6 @@ pub fn plugin_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
#input_struct
#[async_trait::async_trait]
impl pumpkin::plugin::Plugin for #struct_ident {
#(#methods)*
}

View File

@ -14,6 +14,5 @@ pumpkin-util = { path = "../pumpkin-util" }
log.workspace = true
tokio.workspace = true
thiserror.workspace = true
async-trait.workspace = true
crossbeam-utils = "0.8.21"

View File

@ -1,11 +1,10 @@
use std::sync::Arc;
use std::{any::Any, pin::Pin};
use async_trait::async_trait;
use pumpkin_world::{inventory::split_stack, item::ItemStack};
use tokio::sync::Mutex;
use pumpkin_world::inventory::{Clearable, Inventory};
use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture};
use super::recipes::RecipeInputInventory;
@ -32,13 +31,13 @@ impl CraftingInventory {
}
}
#[async_trait]
impl Inventory for CraftingInventory {
fn size(&self) -> usize {
self.items.len()
}
async fn is_empty(&self) -> bool {
fn is_empty(&self) -> InventoryFuture<'_, bool> {
Box::pin(async move {
for slot in self.items.iter() {
if !slot.lock().await.is_empty() {
return false;
@ -46,25 +45,30 @@ impl Inventory for CraftingInventory {
}
true
})
}
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> {
self.items[slot].clone()
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move { self.items[slot].clone() })
}
async fn remove_stack(&self, slot: usize) -> ItemStack {
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
let mut removed = ItemStack::EMPTY.clone();
let mut guard = self.items[slot].lock().await;
std::mem::swap(&mut removed, &mut *guard);
removed
})
}
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack {
split_stack(&self.items, slot, amount).await
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move { split_stack(&self.items, slot, amount).await })
}
async fn set_stack(&self, slot: usize, stack: ItemStack) {
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
Box::pin(async move {
*self.items[slot].lock().await = stack;
})
}
fn as_any(&self) -> &dyn Any {

View File

@ -10,8 +10,8 @@ use crate::screen_handler::{
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture,
ScreenHandlerListener,
};
use crate::slot::{NormalSlot, Slot};
use async_trait::async_trait;
use crate::slot::{BoxFuture, NormalSlot, Slot};
use crossbeam_utils::atomic::AtomicCell;
use pumpkin_data::recipes::{CraftingRecipeTypes, RECIPES_CRAFTING, RecipeResultStruct};
use pumpkin_data::screen::WindowType;
@ -324,7 +324,6 @@ impl ResultSlot {
}
}
#[async_trait]
impl Slot for ResultSlot {
fn get_inventory(&self) -> Arc<dyn Inventory> {
self.inventory.clone()
@ -338,12 +337,23 @@ impl Slot for ResultSlot {
self.id.store(id as u8, Ordering::Relaxed);
}
async fn on_quick_move_crafted(&self, _stack: ItemStack, _stack_prev: ItemStack) {
fn on_quick_move_crafted(
&self,
_stack: ItemStack,
_stack_prev: ItemStack,
) -> BoxFuture<'_, ()> {
Box::pin(async move {
// refill the result slot with the recipe result
self.refill_output().await;
})
}
async fn on_take_item(&self, player: &dyn InventoryPlayer, stack: &ItemStack) {
fn on_take_item<'a>(
&'a self,
player: &'a dyn InventoryPlayer,
stack: &'a ItemStack,
) -> BoxFuture<'a, ()> {
Box::pin(async move {
for i in 0..self.inventory.size() {
let slot = self.inventory.get_stack(i).await;
let mut stack = slot.lock().await;
@ -354,37 +364,45 @@ impl Slot for ResultSlot {
}
self.stat_crafted(stack.item_count, player);
self.mark_dirty().await;
})
}
async fn can_insert(&self, _stack: &ItemStack) -> bool {
false
fn can_insert(&self, _stack: &ItemStack) -> BoxFuture<'_, bool> {
Box::pin(async move { false })
}
async fn get_stack(&self) -> Arc<Mutex<ItemStack>> {
self.result.clone()
fn get_stack(&self) -> BoxFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move { self.result.clone() })
}
async fn get_cloned_stack(&self) -> ItemStack {
self.result.lock().await.clone()
fn get_cloned_stack(&self) -> BoxFuture<'_, ItemStack> {
Box::pin(async move { self.result.lock().await.clone() })
}
async fn has_stack(&self) -> bool {
!self.result.lock().await.is_empty()
fn has_stack(&self) -> BoxFuture<'_, bool> {
Box::pin(async move { !self.result.lock().await.is_empty() })
}
async fn set_stack(&self, _stack: ItemStack) {
fn set_stack(&self, _stack: ItemStack) -> BoxFuture<'_, ()> {
Box::pin(async move {
self.refill_output().await;
})
}
async fn set_stack_prev(&self, _stack: ItemStack, _previous_stack: ItemStack) {
fn set_stack_prev(&self, _stack: ItemStack, _previous_stack: ItemStack) -> BoxFuture<'_, ()> {
Box::pin(async move {
self.refill_output().await;
})
}
async fn mark_dirty(&self) {
fn mark_dirty(&self) -> BoxFuture<'_, ()> {
Box::pin(async move {
self.inventory.mark_dirty();
})
}
async fn get_max_item_count(&self) -> u8 {
fn get_max_item_count(&self) -> BoxFuture<'_, u8> {
Box::pin(async move {
let mut count = u8::MAX;
for i in 0..self.inventory.size() {
let slot = self.inventory.get_stack(i).await;
@ -394,9 +412,11 @@ impl Slot for ResultSlot {
}
}
count
})
}
async fn take_stack(&self, _amount: u8) -> ItemStack {
fn take_stack(&self, _amount: u8) -> BoxFuture<'_, ItemStack> {
Box::pin(async move {
if self.has_stack().await {
let stack = self.result.lock().await;
// Vanilla: net.minecraft.world.inventory.ResultContainer#removeItem
@ -405,6 +425,7 @@ impl Slot for ResultSlot {
} else {
ItemStack::EMPTY.clone()
}
})
}
}

View File

@ -1,8 +1,7 @@
use std::{any::Any, pin::Pin, sync::Arc};
use async_trait::async_trait;
use pumpkin_world::{
inventory::{Clearable, Inventory},
inventory::{Clearable, Inventory, InventoryFuture},
item::ItemStack,
};
use tokio::sync::Mutex;
@ -19,33 +18,37 @@ impl DoubleInventory {
}
}
#[async_trait]
impl Inventory for DoubleInventory {
fn size(&self) -> usize {
self.first.size() + self.second.size()
}
async fn is_empty(&self) -> bool {
self.first.is_empty().await && self.second.is_empty().await
fn is_empty(&self) -> InventoryFuture<'_, bool> {
Box::pin(async move { self.first.is_empty().await && self.second.is_empty().await })
}
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> {
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move {
if slot >= self.first.size() {
self.second.get_stack(slot - self.first.size()).await
} else {
self.first.get_stack(slot).await
}
})
}
async fn remove_stack(&self, slot: usize) -> ItemStack {
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
if slot >= self.first.size() {
self.second.remove_stack(slot - self.first.size()).await
} else {
self.first.remove_stack(slot).await
}
})
}
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack {
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
if slot >= self.first.size() {
self.second
.remove_stack_specific(slot - self.first.size(), amount)
@ -53,18 +56,21 @@ impl Inventory for DoubleInventory {
} else {
self.first.remove_stack_specific(slot, amount).await
}
})
}
fn get_max_count_per_stack(&self) -> u8 {
self.first.get_max_count_per_stack()
}
async fn set_stack(&self, slot: usize, stack: ItemStack) {
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
Box::pin(async move {
if slot >= self.first.size() {
self.second.set_stack(slot - self.first.size(), stack).await
} else {
self.first.set_stack(slot, stack).await
}
})
}
fn mark_dirty(&self) {
@ -72,14 +78,18 @@ impl Inventory for DoubleInventory {
self.second.mark_dirty();
}
async fn on_open(&self) {
fn on_open(&self) -> InventoryFuture<'_, ()> {
Box::pin(async move {
self.first.on_open().await;
self.second.on_open().await;
})
}
async fn on_close(&self) {
fn on_close(&self) -> InventoryFuture<'_, ()> {
Box::pin(async move {
self.first.on_close().await;
self.second.on_close().await;
})
}
fn is_valid_slot_for(&self, slot: usize, stack: &ItemStack) -> bool {

View File

@ -1,10 +1,9 @@
use std::sync::{Arc, atomic::AtomicU8};
use async_trait::async_trait;
use pumpkin_data::{fuels::is_fuel, item::Item};
use pumpkin_world::inventory::Inventory;
use crate::slot::Slot;
use crate::slot::{BoxFuture, Slot};
#[derive(Debug, Clone, Copy)]
pub enum FurnaceSlotType {
@ -31,7 +30,6 @@ impl FurnaceSlot {
}
}
}
#[async_trait]
impl Slot for FurnaceSlot {
fn get_inventory(&self) -> Arc<dyn Inventory> {
self.inventory.clone()
@ -46,15 +44,21 @@ impl Slot for FurnaceSlot {
.store(id as u8, std::sync::atomic::Ordering::Relaxed);
}
async fn mark_dirty(&self) {
fn mark_dirty(&self) -> BoxFuture<'_, ()> {
Box::pin(async move {
self.inventory.mark_dirty();
})
}
async fn can_insert(&self, stack: &pumpkin_world::item::ItemStack) -> bool {
fn can_insert<'a>(&'a self, stack: &'a pumpkin_world::item::ItemStack) -> BoxFuture<'a, bool> {
Box::pin(async move {
match self.slot_type {
FurnaceSlotType::Top => true,
FurnaceSlotType::Bottom => is_fuel(stack.item.id) || stack.item.id == Item::BUCKET.id,
FurnaceSlotType::Bottom => {
is_fuel(stack.item.id) || stack.item.id == Item::BUCKET.id
}
FurnaceSlotType::Side => false,
}
})
}
}

View File

@ -1,9 +1,8 @@
use std::{any::Any, array::from_fn, pin::Pin, sync::Arc};
use async_trait::async_trait;
use pumpkin_world::{
block::viewer::ViewerCountTracker,
inventory::{Clearable, Inventory, split_stack},
inventory::{Clearable, Inventory, InventoryFuture, split_stack},
item::ItemStack,
};
use tokio::sync::Mutex;
@ -46,13 +45,13 @@ impl EnderChestInventory {
}
}
#[async_trait]
impl Inventory for EnderChestInventory {
fn size(&self) -> usize {
self.items.len()
}
async fn is_empty(&self) -> bool {
fn is_empty(&self) -> InventoryFuture<'_, bool> {
Box::pin(async move {
for slot in self.items.iter() {
if !slot.lock().await.is_empty() {
return false;
@ -60,37 +59,46 @@ impl Inventory for EnderChestInventory {
}
true
})
}
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> {
self.items[slot].clone()
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move { self.items[slot].clone() })
}
async fn remove_stack(&self, slot: usize) -> ItemStack {
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
let mut removed = ItemStack::EMPTY.clone();
let mut guard = self.items[slot].lock().await;
std::mem::swap(&mut removed, &mut *guard);
removed
})
}
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack {
split_stack(&self.items, slot, amount).await
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move { split_stack(&self.items, slot, amount).await })
}
async fn set_stack(&self, slot: usize, stack: ItemStack) {
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
Box::pin(async move {
*self.items[slot].lock().await = stack;
})
}
async fn on_open(&self) {
fn on_open(&self) -> InventoryFuture<'_, ()> {
Box::pin(async move {
if let Some(tracker) = self.tracker.lock().await.as_ref() {
tracker.open_container();
}
})
}
async fn on_close(&self) {
fn on_close(&self) -> InventoryFuture<'_, ()> {
Box::pin(async move {
if let Some(tracker) = self.tracker.lock().await.as_ref() {
tracker.close_container();
}
})
}
fn mark_dirty(&self) {}

View File

@ -1,11 +1,11 @@
use crate::entity_equipment::EntityEquipment;
use crate::screen_handler::InventoryPlayer;
use async_trait::async_trait;
use pumpkin_data::data_component_impl::EquipmentSlot;
use pumpkin_protocol::java::client::play::CSetPlayerInventory;
use pumpkin_util::Hand;
use pumpkin_world::inventory::split_stack;
use pumpkin_world::inventory::{Clearable, Inventory};
use pumpkin_world::inventory::{InventoryFuture, split_stack};
use pumpkin_world::item::ItemStack;
use std::any::Any;
use std::array::from_fn;
@ -320,13 +320,13 @@ impl Clearable for PlayerInventory {
}
}
#[async_trait]
impl Inventory for PlayerInventory {
fn size(&self) -> usize {
self.main_inventory.len() + self.equipment_slots.len()
}
async fn is_empty(&self) -> bool {
fn is_empty(&self) -> InventoryFuture<'_, bool> {
Box::pin(async move {
for item in self.main_inventory.iter() {
if !item.lock().await.is_empty() {
return false;
@ -348,18 +348,22 @@ impl Inventory for PlayerInventory {
}
true
})
}
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> {
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move {
if slot < self.main_inventory.len() {
self.main_inventory[slot].clone()
} else {
let slot = self.equipment_slots.get(&slot).unwrap();
self.entity_equipment.lock().await.get(slot)
}
})
}
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack {
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
if slot < self.main_inventory.len() {
split_stack(&self.main_inventory, slot, amount).await
} else {
@ -374,9 +378,11 @@ impl Inventory for PlayerInventory {
ItemStack::EMPTY.clone()
}
})
}
async fn remove_stack(&self, slot: usize) -> ItemStack {
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
if slot < self.main_inventory.len() {
let mut removed = ItemStack::EMPTY.clone();
let mut guard = self.main_inventory[slot].lock().await;
@ -390,9 +396,11 @@ impl Inventory for PlayerInventory {
.put(slot, ItemStack::EMPTY.clone())
.await
}
})
}
async fn set_stack(&self, slot: usize, stack: ItemStack) {
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
Box::pin(async move {
if slot < self.main_inventory.len() {
*self.main_inventory[slot].lock().await = stack;
} else {
@ -403,6 +411,7 @@ impl Inventory for PlayerInventory {
None => log::warn!("Failed to get Equipment Slot at {slot}"),
}
}
})
}
fn mark_dirty(&self) {}

View File

@ -4,7 +4,6 @@ use crate::{
slot::{NormalSlot, Slot},
sync_handler::{SyncHandler, TrackedStack},
};
use async_trait::async_trait;
use log::warn;
use pumpkin_data::{
data_component_impl::{EquipmentSlot, EquipmentType, EquippableImpl},
@ -1038,14 +1037,17 @@ pub trait ScreenHandlerListener: Send + Sync {
}
}
#[async_trait]
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub type SharedScreenHandler = Arc<Mutex<dyn ScreenHandler>>;
pub trait ScreenHandlerFactory: Send + Sync {
async fn create_screen_handler(
&self,
fn create_screen_handler<'a>(
&'a self,
sync_id: u8,
player_inventory: &Arc<PlayerInventory>,
player: &dyn InventoryPlayer,
) -> Option<Arc<Mutex<dyn ScreenHandler>>>;
player_inventory: &'a Arc<PlayerInventory>,
player: &'a dyn InventoryPlayer,
) -> BoxFuture<'a, Option<SharedScreenHandler>>;
fn get_display_name(&self) -> TextComponent;
}

View File

@ -1,6 +1,7 @@
#![warn(unused)]
use std::{
fmt::Debug,
pin::Pin,
sync::{
Arc,
atomic::{AtomicU8, Ordering},
@ -9,16 +10,17 @@ use std::{
};
use crate::screen_handler::InventoryPlayer;
use async_trait::async_trait;
use pumpkin_data::data_component_impl::EquipmentSlot;
use pumpkin_data::item::Item;
use pumpkin_world::inventory::Inventory;
use pumpkin_world::item::ItemStack;
use tokio::{sync::Mutex, time::timeout};
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
// Slot.java
// This is a trait due to crafting slots being a thing
#[async_trait]
pub trait Slot: Send + Sync + Debug {
fn get_inventory(&self) -> Arc<dyn Inventory>;
@ -31,102 +33,148 @@ pub trait Slot: Send + Sync + Debug {
/// locks to the stack to avoid deadlocks.
///
/// Also see: `ScreenHandler::quick_move`
async fn on_quick_move_crafted(&self, _stack: ItemStack, _stack_prev: ItemStack) {}
fn on_quick_move_crafted(
&self,
_stack: ItemStack,
_stack_prev: ItemStack,
) -> BoxFuture<'_, ()> {
Box::pin(async move {}) // Default implementation
}
/// Callback for when an item is taken from the slot.
///
/// Also see: `safe_take`
async fn on_take_item(&self, _player: &dyn InventoryPlayer, _stack: &ItemStack) {
fn on_take_item<'a>(
&'a self,
_player: &'a dyn InventoryPlayer,
_stack: &'a ItemStack,
) -> BoxFuture<'a, ()> {
// Default implementation logic:
Box::pin(async move {
self.mark_dirty().await;
})
}
// Used for plugins
async fn on_click(&self, _player: &dyn InventoryPlayer) {}
async fn can_insert(&self, _stack: &ItemStack) -> bool {
true
fn on_click(&self, _player: &dyn InventoryPlayer) -> BoxFuture<'_, ()> {
Box::pin(async move {}) // Default implementation
}
async fn get_stack(&self) -> Arc<Mutex<ItemStack>> {
self.get_inventory().get_stack(self.get_index()).await
fn can_insert<'a>(&'a self, _stack: &'a ItemStack) -> BoxFuture<'a, bool> {
// Default implementation logic:
Box::pin(async move { true })
}
async fn get_cloned_stack(&self) -> ItemStack {
fn get_stack(&self) -> BoxFuture<'_, Arc<Mutex<ItemStack>>> {
// Default implementation logic:
Box::pin(async move { self.get_inventory().get_stack(self.get_index()).await })
}
fn get_cloned_stack(&self) -> BoxFuture<'_, ItemStack> {
// Default implementation logic:
Box::pin(async move {
let stack = self.get_stack().await;
let lock = timeout(Duration::from_secs(5), stack.lock())
.await
.expect("Timed out while trying to acquire lock");
lock.clone()
})
}
async fn has_stack(&self) -> bool {
fn has_stack(&self) -> BoxFuture<'_, bool> {
// Default implementation logic:
Box::pin(async move {
let inv = self.get_inventory();
!inv.get_stack(self.get_index())
.await
.lock()
.await
.is_empty()
})
}
/// Make sure to drop any locks to the slot stack before calling this
async fn set_stack(&self, stack: ItemStack) {
fn set_stack(&self, stack: ItemStack) -> BoxFuture<'_, ()> {
// Default implementation logic:
Box::pin(async move {
self.set_stack_no_callbacks(stack).await;
})
}
/// Changes the stack in the slot with the given `stack`.
async fn set_stack_prev(&self, stack: ItemStack, _previous_stack: ItemStack) {
fn set_stack_prev(&self, stack: ItemStack, _previous_stack: ItemStack) -> BoxFuture<'_, ()> {
// Default implementation logic:
Box::pin(async move {
self.set_stack_no_callbacks(stack).await;
})
}
async fn set_stack_no_callbacks(&self, stack: ItemStack) {
fn set_stack_no_callbacks(&self, stack: ItemStack) -> BoxFuture<'_, ()> {
// Default implementation logic:
Box::pin(async move {
let inv = self.get_inventory();
inv.set_stack(self.get_index(), stack).await;
self.mark_dirty().await;
})
}
async fn mark_dirty(&self);
fn mark_dirty(&self) -> BoxFuture<'_, ()>; // This method must be implemented by concrete types
async fn get_max_item_count(&self) -> u8 {
self.get_inventory().get_max_count_per_stack()
fn get_max_item_count(&self) -> BoxFuture<'_, u8> {
// Default implementation logic:
Box::pin(async move { self.get_inventory().get_max_count_per_stack() })
}
async fn get_max_item_count_for_stack(&self, stack: &ItemStack) -> u8 {
fn get_max_item_count_for_stack<'a>(&'a self, stack: &'a ItemStack) -> BoxFuture<'a, u8> {
// Default implementation logic:
Box::pin(async move {
self.get_max_item_count()
.await
.min(stack.get_max_stack_size())
})
}
/// Removes a specific amount of items from the slot.
///
/// Mojang name: `remove`
async fn take_stack(&self, amount: u8) -> ItemStack {
fn take_stack(&self, amount: u8) -> BoxFuture<'_, ItemStack> {
// Default implementation logic:
Box::pin(async move {
let inv = self.get_inventory();
inv.remove_stack_specific(self.get_index(), amount).await
})
}
/// Mojang name: `mayPickup`
async fn can_take_items(&self, _player: &dyn InventoryPlayer) -> bool {
true
fn can_take_items(&self, _player: &dyn InventoryPlayer) -> BoxFuture<'_, bool> {
// Default implementation logic:
Box::pin(async move { true })
}
/// Mojang name: `allowModification`
async fn allow_modification(&self, player: &dyn InventoryPlayer) -> bool {
self.can_insert(&self.get_cloned_stack().await).await && self.can_take_items(player).await
fn allow_modification<'a>(&'a self, player: &'a dyn InventoryPlayer) -> BoxFuture<'a, bool> {
// Default implementation logic:
Box::pin(async move {
self.can_insert(&self.get_cloned_stack().await).await
&& self.can_take_items(player).await
})
}
/// Mojang name: `tryRemove`
async fn try_take_stack_range(
&self,
fn try_take_stack_range<'a>(
&'a self,
min: u8,
max: u8,
player: &dyn InventoryPlayer,
) -> Option<ItemStack> {
player: &'a dyn InventoryPlayer,
) -> BoxFuture<'a, Option<ItemStack>> {
// Default implementation logic:
Box::pin(async move {
if !self.can_take_items(player).await {
return None;
}
if !self.allow_modification(player).await && self.get_cloned_stack().await.item_count > max
if !self.allow_modification(player).await
&& self.get_cloned_stack().await.item_count > max
{
// If the slot is not allowed to be modified, we cannot take a partial stack from it.
return None;
@ -144,13 +192,20 @@ pub trait Slot: Send + Sync + Debug {
Some(stack)
}
})
}
/// Safely tries to take a stack of items from the slot, returning `None` if the stack is empty.
/// Considering such as result slots, as their stacks cannot split.
///
/// Mojang name: `safeTake`
async fn safe_take(&self, min: u8, max: u8, player: &dyn InventoryPlayer) -> ItemStack {
fn safe_take<'a>(
&'a self,
min: u8,
max: u8,
player: &'a dyn InventoryPlayer,
) -> BoxFuture<'a, ItemStack> {
Box::pin(async move {
let stack = self.try_take_stack_range(min, max, player).await;
if let Some(stack) = &stack {
@ -158,14 +213,20 @@ pub trait Slot: Send + Sync + Debug {
}
stack.unwrap_or(ItemStack::EMPTY.clone())
})
}
async fn insert_stack(&self, stack: ItemStack) -> ItemStack {
fn insert_stack(&self, stack: ItemStack) -> BoxFuture<'_, ItemStack> {
// Default implementation logic:
Box::pin(async move {
let stack_item_count = stack.item_count;
self.insert_stack_count(stack, stack_item_count).await
})
}
async fn insert_stack_count(&self, mut stack: ItemStack, count: u8) -> ItemStack {
fn insert_stack_count(&self, mut stack: ItemStack, count: u8) -> BoxFuture<'_, ItemStack> {
// Default implementation logic:
Box::pin(async move {
if !stack.is_empty() && self.can_insert(&stack).await {
let stack_mutex = self.get_stack().await;
let mut stack_self = stack_mutex.lock().await;
@ -174,7 +235,7 @@ pub trait Slot: Send + Sync + Debug {
.min(self.get_max_item_count_for_stack(&stack).await - stack_self.item_count);
if min_count == 0 {
return stack;
stack
} else {
if stack_self.is_empty() {
drop(stack_self);
@ -187,11 +248,12 @@ pub trait Slot: Send + Sync + Debug {
self.set_stack(cloned_stack).await;
}
return stack;
stack
}
} else {
stack
}
})
}
}
@ -212,7 +274,6 @@ impl NormalSlot {
}
}
}
#[async_trait]
impl Slot for NormalSlot {
fn get_inventory(&self) -> Arc<dyn Inventory> {
self.inventory.clone()
@ -226,8 +287,10 @@ impl Slot for NormalSlot {
self.id.store(id as u8, Ordering::Relaxed);
}
async fn mark_dirty(&self) {
fn mark_dirty(&self) -> BoxFuture<'_, ()> {
Box::pin(async move {
self.inventory.mark_dirty();
})
}
}
@ -251,7 +314,6 @@ impl ArmorSlot {
}
}
#[async_trait]
impl Slot for ArmorSlot {
fn get_inventory(&self) -> Arc<dyn Inventory> {
self.inventory.clone()
@ -265,16 +327,19 @@ impl Slot for ArmorSlot {
self.id.store(id as u8, Ordering::Relaxed);
}
async fn get_max_item_count(&self) -> u8 {
1
fn get_max_item_count(&self) -> BoxFuture<'_, u8> {
Box::pin(async move { 1 })
}
async fn set_stack_prev(&self, stack: ItemStack, _previous_stack: ItemStack) {
fn set_stack_prev(&self, stack: ItemStack, _previous_stack: ItemStack) -> BoxFuture<'_, ()> {
Box::pin(async move {
//TODO: this.entity.onEquipStack(this.equipmentSlot, previousStack, stack);
self.set_stack_no_callbacks(stack).await;
})
}
async fn can_insert(&self, stack: &ItemStack) -> bool {
fn can_insert<'a>(&'a self, stack: &'a ItemStack) -> BoxFuture<'a, bool> {
Box::pin(async move {
match self.equipment_slot {
EquipmentSlot::Head(_) => {
stack.is_helmet() || stack.is_skull() || stack.item == &Item::CARVED_PUMPKIN
@ -284,14 +349,19 @@ impl Slot for ArmorSlot {
EquipmentSlot::Feet(_) => stack.is_boots(),
_ => true,
}
})
}
async fn can_take_items(&self, _player: &dyn InventoryPlayer) -> bool {
fn can_take_items(&self, _player: &dyn InventoryPlayer) -> BoxFuture<'_, bool> {
Box::pin(async move {
// TODO: Check enchantments
true
})
}
async fn mark_dirty(&self) {
fn mark_dirty(&self) -> BoxFuture<'_, ()> {
Box::pin(async move {
self.inventory.mark_dirty();
})
}
}

View File

@ -10,7 +10,6 @@ pumpkin-util = { path = "../pumpkin-util" }
pumpkin-config = { path = "../pumpkin-config" }
pumpkin-data = { path = "../pumpkin-data" }
async-trait.workspace = true
futures.workspace = true
bytes.workspace = true

View File

@ -1,6 +1,6 @@
// use std::{fs, path::PathBuf, sync::Arc};
//
// use async_trait::async_trait;
//
// use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
// use pumpkin_data::BlockDirection;
// use pumpkin_util::math::{position::BlockPos, vector2::Vector2};
@ -79,7 +79,6 @@
//
// struct BlockRegistry;
//
// #[async_trait]
// impl BlockRegistryExt for BlockRegistry {
// fn can_place_at(
// &self,

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::block_properties::{BarrelLikeProperties, BlockProperties};
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::{Block, FacingExt};
@ -19,6 +18,7 @@ use std::{
use tokio::sync::Mutex;
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture};
use crate::inventory::InventoryFuture;
use crate::world::{BlockFlags, SimpleWorld};
use crate::{
inventory::{
@ -175,13 +175,13 @@ impl BarrelBlockEntity {
}
}
#[async_trait]
impl Inventory for BarrelBlockEntity {
fn size(&self) -> usize {
self.items.len()
}
async fn is_empty(&self) -> bool {
fn is_empty(&self) -> InventoryFuture<'_, bool> {
Box::pin(async move {
for slot in self.items.iter() {
if !slot.lock().await.is_empty() {
return false;
@ -189,33 +189,42 @@ impl Inventory for BarrelBlockEntity {
}
true
})
}
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> {
self.items[slot].clone()
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move { self.items[slot].clone() })
}
async fn remove_stack(&self, slot: usize) -> ItemStack {
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
let mut removed = ItemStack::EMPTY.clone();
let mut guard = self.items[slot].lock().await;
std::mem::swap(&mut removed, &mut *guard);
removed
})
}
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack {
split_stack(&self.items, slot, amount).await
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move { split_stack(&self.items, slot, amount).await })
}
async fn set_stack(&self, slot: usize, stack: ItemStack) {
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
Box::pin(async move {
*self.items[slot].lock().await = stack;
})
}
async fn on_open(&self) {
fn on_open(&self) -> InventoryFuture<'_, ()> {
Box::pin(async move {
self.viewers.open_container();
})
}
async fn on_close(&self) {
fn on_close(&self) -> InventoryFuture<'_, ()> {
Box::pin(async move {
self.viewers.close_container();
})
}
fn mark_dirty(&self) {

View File

@ -8,7 +8,6 @@ use std::{
},
};
use async_trait::async_trait;
use pumpkin_data::{
Block, HorizontalFacingExt,
block_properties::{BlockProperties, ChestLikeProperties, ChestType},
@ -23,7 +22,7 @@ use tokio::sync::Mutex;
use crate::{
block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture},
inventory::{Clearable, Inventory, split_stack},
inventory::{Clearable, Inventory, InventoryFuture, split_stack},
item::ItemStack,
world::SimpleWorld,
};
@ -183,13 +182,13 @@ impl ChestBlockEntity {
}
}
#[async_trait]
impl Inventory for ChestBlockEntity {
fn size(&self) -> usize {
self.items.len()
}
async fn is_empty(&self) -> bool {
fn is_empty(&self) -> InventoryFuture<'_, bool> {
Box::pin(async move {
for slot in self.items.iter() {
if !slot.lock().await.is_empty() {
return false;
@ -197,33 +196,42 @@ impl Inventory for ChestBlockEntity {
}
true
})
}
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> {
self.items[slot].clone()
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move { self.items[slot].clone() })
}
async fn remove_stack(&self, slot: usize) -> ItemStack {
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
let mut removed = ItemStack::EMPTY.clone();
let mut guard = self.items[slot].lock().await;
std::mem::swap(&mut removed, &mut *guard);
removed
})
}
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack {
split_stack(&self.items, slot, amount).await
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move { split_stack(&self.items, slot, amount).await })
}
async fn set_stack(&self, slot: usize, stack: ItemStack) {
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
Box::pin(async move {
*self.items[slot].lock().await = stack;
})
}
async fn on_open(&self) {
fn on_open(&self) -> InventoryFuture<'_, ()> {
Box::pin(async move {
self.viewers.open_container();
})
}
async fn on_close(&self) {
fn on_close(&self) -> InventoryFuture<'_, ()> {
Box::pin(async move {
self.viewers.close_container();
})
}
fn mark_dirty(&self) {

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use log::warn;
use pumpkin_data::Block;
use pumpkin_data::block_properties::{BlockProperties, ChiseledBookshelfLikeProperties};
@ -15,6 +14,7 @@ use std::{
};
use tokio::sync::Mutex;
use crate::inventory::InventoryFuture;
use crate::{
block::entities::BlockEntity,
inventory::{Clearable, Inventory, split_stack},
@ -130,13 +130,13 @@ impl ChiseledBookshelfBlockEntity {
}
}
#[async_trait]
impl Inventory for ChiseledBookshelfBlockEntity {
fn size(&self) -> usize {
self.items.len()
}
async fn is_empty(&self) -> bool {
fn is_empty(&self) -> InventoryFuture<'_, bool> {
Box::pin(async move {
for slot in self.items.iter() {
if !slot.lock().await.is_empty() {
return false;
@ -144,25 +144,30 @@ impl Inventory for ChiseledBookshelfBlockEntity {
}
true
})
}
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> {
self.items[slot].clone()
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move { self.items[slot].clone() })
}
async fn remove_stack(&self, slot: usize) -> ItemStack {
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
let mut removed = ItemStack::EMPTY.clone();
let mut guard = self.items[slot].lock().await;
std::mem::swap(&mut removed, &mut *guard);
removed
})
}
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack {
split_stack(&self.items, slot, amount).await
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move { split_stack(&self.items, slot, amount).await })
}
async fn set_stack(&self, slot: usize, stack: ItemStack) {
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
Box::pin(async move {
*self.items[slot].lock().await = stack;
})
}
fn mark_dirty(&self) {

View File

@ -1,7 +1,6 @@
use crate::block::entities::BlockEntity;
use crate::inventory::{Clearable, Inventory, split_stack};
use crate::inventory::{Clearable, Inventory, InventoryFuture, split_stack};
use crate::item::ItemStack;
use async_trait::async_trait;
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos;
use rand::{Rng, rng};
@ -95,13 +94,13 @@ impl DropperBlockEntity {
}
}
#[async_trait]
impl Inventory for DropperBlockEntity {
fn size(&self) -> usize {
self.items.len()
}
async fn is_empty(&self) -> bool {
fn is_empty(&self) -> InventoryFuture<'_, bool> {
Box::pin(async move {
for slot in self.items.iter() {
if !slot.lock().await.is_empty() {
return false;
@ -109,25 +108,30 @@ impl Inventory for DropperBlockEntity {
}
true
})
}
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> {
self.items[slot].clone()
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move { self.items[slot].clone() })
}
async fn remove_stack(&self, slot: usize) -> ItemStack {
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
let mut removed = ItemStack::EMPTY.clone();
let mut guard = self.items[slot].lock().await;
std::mem::swap(&mut removed, &mut *guard);
removed
})
}
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack {
split_stack(&self.items, slot, amount).await
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move { split_stack(&self.items, slot, amount).await })
}
async fn set_stack(&self, slot: usize, stack: ItemStack) {
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
Box::pin(async move {
*self.items[slot].lock().await = stack;
})
}
fn mark_dirty(&self) {

View File

@ -7,7 +7,6 @@ use std::{
},
};
use async_trait::async_trait;
use pumpkin_data::{
block_properties::{BlockProperties, FurnaceLikeProperties},
fuels::get_item_burn_ticks,
@ -20,7 +19,7 @@ use pumpkin_util::math::position::BlockPos;
use tokio::sync::Mutex;
use crate::{
inventory::{Clearable, Inventory, split_stack},
inventory::{Clearable, Inventory, InventoryFuture, split_stack},
item::ItemStack,
world::{BlockFlags, SimpleWorld},
};
@ -375,13 +374,13 @@ impl FurnaceBlockEntity {
}
}
#[async_trait]
impl Inventory for FurnaceBlockEntity {
fn size(&self) -> usize {
self.items.len()
}
async fn is_empty(&self) -> bool {
fn is_empty(&self) -> InventoryFuture<'_, bool> {
Box::pin(async move {
for slot in self.items.iter() {
if !slot.lock().await.is_empty() {
return false;
@ -389,29 +388,33 @@ impl Inventory for FurnaceBlockEntity {
}
true
})
}
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> {
self.items[slot].clone()
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move { self.items[slot].clone() })
}
async fn remove_stack(&self, slot: usize) -> ItemStack {
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
let mut removed = ItemStack::EMPTY.clone();
let mut guard = self.items[slot].lock().await;
std::mem::swap(&mut removed, &mut *guard);
removed
})
}
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack {
split_stack(&self.items, slot, amount).await
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move { split_stack(&self.items, slot, amount).await })
}
async fn set_stack(&self, slot: usize, stack: ItemStack) {
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
Box::pin(async move {
let furnace_stack = self.get_stack(slot).await;
let mut furnace_stack = furnace_stack.lock().await;
let is_same_item =
!stack.is_empty() && ItemStack::are_items_and_components_equal(&furnace_stack, &stack);
let is_same_item = !stack.is_empty()
&& ItemStack::are_items_and_components_equal(&furnace_stack, &stack);
*furnace_stack = stack.clone();
drop(furnace_stack);
@ -426,6 +429,7 @@ impl Inventory for FurnaceBlockEntity {
self.cooking_time_spent.store(0, Ordering::Relaxed);
self.mark_dirty();
}
})
}
fn mark_dirty(&self) {

View File

@ -1,9 +1,8 @@
use crate::BlockStateId;
use crate::block::entities::BlockEntity;
use crate::inventory::{Clearable, Inventory, split_stack};
use crate::inventory::{Clearable, Inventory, InventoryFuture, split_stack};
use crate::item::ItemStack;
use crate::world::SimpleWorld;
use async_trait::async_trait;
use pumpkin_data::block_properties::{BlockProperties, HopperFacing, HopperLikeProperties};
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, tag};
@ -294,13 +293,13 @@ impl HopperBlockEntity {
}
}
#[async_trait]
impl Inventory for HopperBlockEntity {
fn size(&self) -> usize {
self.items.len()
}
async fn is_empty(&self) -> bool {
fn is_empty(&self) -> InventoryFuture<'_, bool> {
Box::pin(async move {
for slot in self.items.iter() {
if !slot.lock().await.is_empty() {
return false;
@ -308,25 +307,30 @@ impl Inventory for HopperBlockEntity {
}
true
})
}
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> {
self.items[slot].clone()
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move { self.items[slot].clone() })
}
async fn remove_stack(&self, slot: usize) -> ItemStack {
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
let mut removed = ItemStack::EMPTY.clone();
let mut guard = self.items[slot].lock().await;
std::mem::swap(&mut removed, &mut *guard);
removed
})
}
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack {
split_stack(&self.items, slot, amount).await
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move { split_stack(&self.items, slot, amount).await })
}
async fn set_stack(&self, slot: usize, stack: ItemStack) {
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
Box::pin(async move {
*self.items[slot].lock().await = stack;
})
}
fn mark_dirty(&self) {

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos;
@ -6,6 +5,7 @@ use pumpkin_util::random::xoroshiro128::Xoroshiro;
use pumpkin_util::random::{RandomImpl, get_seed};
use std::any::Any;
use std::pin::Pin;
use std::sync::atomic::Ordering;
use std::{
array::from_fn,
sync::{Arc, atomic::AtomicBool},
@ -13,6 +13,7 @@ use std::{
use tokio::sync::Mutex;
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture};
use crate::inventory::InventoryFuture;
use crate::world::SimpleWorld;
use crate::{
inventory::{
@ -175,13 +176,13 @@ impl ShulkerBoxBlockEntity {
}
}
#[async_trait]
impl Inventory for ShulkerBoxBlockEntity {
fn size(&self) -> usize {
self.items.len()
}
async fn is_empty(&self) -> bool {
fn is_empty(&self) -> InventoryFuture<'_, bool> {
Box::pin(async move {
for slot in self.items.iter() {
if !slot.lock().await.is_empty() {
return false;
@ -189,37 +190,46 @@ impl Inventory for ShulkerBoxBlockEntity {
}
true
})
}
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> {
self.items[slot].clone()
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move { self.items[slot].clone() })
}
async fn remove_stack(&self, slot: usize) -> ItemStack {
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
let mut removed = ItemStack::EMPTY.clone();
let mut guard = self.items[slot].lock().await;
std::mem::swap(&mut removed, &mut *guard);
removed
})
}
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack {
split_stack(&self.items, slot, amount).await
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move { split_stack(&self.items, slot, amount).await })
}
async fn set_stack(&self, slot: usize, stack: ItemStack) {
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
Box::pin(async move {
*self.items[slot].lock().await = stack;
})
}
async fn on_open(&self) {
fn on_open(&self) -> InventoryFuture<'_, ()> {
Box::pin(async move {
self.viewers.open_container();
})
}
async fn on_close(&self) {
fn on_close(&self) -> InventoryFuture<'_, ()> {
Box::pin(async move {
self.viewers.close_container();
})
}
fn mark_dirty(&self) {
self.dirty.store(true, std::sync::atomic::Ordering::Relaxed);
self.dirty.store(true, Ordering::Relaxed);
}
fn as_any(&self) -> &dyn Any {

View File

@ -1,4 +1,3 @@
use async_trait::async_trait;
use bytes::*;
use flate2::read::{GzDecoder, GzEncoder, ZlibDecoder, ZlibEncoder};
use itertools::Itertools;
@ -526,7 +525,6 @@ pub trait SingleChunkDataSerializer: Send + Sync + Sized + Dirtiable {
fn position(&self) -> &Vector2<i32>;
}
#[async_trait]
impl<S: SingleChunkDataSerializer> ChunkSerializer for AnvilChunkFile<S> {
type Data = S;
type WriteBackend = PathBuf;
@ -827,7 +825,7 @@ impl<S: SingleChunkDataSerializer> ChunkSerializer for AnvilChunkFile<S> {
/*
#[cfg(test)]
mod tests {
use async_trait::async_trait;
use pumpkin_config::{AdvancedConfiguration, advanced_config, override_config_for_testing};
use pumpkin_data::BlockDirection;
use pumpkin_util::math::position::BlockPos;
@ -849,7 +847,6 @@ mod tests {
struct BlockRegistry;
#[async_trait]
impl BlockRegistryExt for BlockRegistry {
fn can_place_at(
&self,

View File

@ -6,7 +6,6 @@ use std::time::{SystemTime, UNIX_EPOCH};
use crate::chunk::format::anvil::{AnvilChunkFile, SingleChunkDataSerializer};
use crate::chunk::io::{ChunkSerializer, LoadedData};
use crate::chunk::{ChunkReadingError, ChunkWritingError};
use async_trait::async_trait;
use bytes::{Buf, BufMut, Bytes};
use log::error;
use pumpkin_util::math::vector2::Vector2;
@ -163,7 +162,6 @@ impl<S: SingleChunkDataSerializer> Default for LinearFile<S> {
}
}
#[async_trait]
impl<S: SingleChunkDataSerializer> ChunkSerializer for LinearFile<S> {
type Data = S;
type WriteBackend = PathBuf;
@ -372,7 +370,7 @@ impl<S: SingleChunkDataSerializer> ChunkSerializer for LinearFile<S> {
/*
#[cfg(test)]
mod tests {
use async_trait::async_trait;
use core::panic;
use pumpkin_data::BlockDirection;
use pumpkin_util::math::position::BlockPos;
@ -394,7 +392,6 @@ mod tests {
struct BlockRegistry;
#[async_trait]
impl BlockRegistryExt for BlockRegistry {
fn can_place_at(
&self,

View File

@ -6,7 +6,6 @@ use std::{
sync::Arc,
};
use async_trait::async_trait;
use futures::future::join_all;
use log::{error, trace};
use num_traits::Zero;
@ -18,7 +17,10 @@ use tokio::{
};
use crate::{
chunk::{ChunkReadingError, ChunkWritingError, io::Dirtiable},
chunk::{
ChunkReadingError, ChunkWritingError,
io::{BoxFuture, Dirtiable},
},
level::LevelFolder,
};
@ -149,7 +151,6 @@ impl<S: ChunkSerializer<WriteBackend = PathBuf>> ChunkFileManager<S> {
}
}
#[async_trait]
impl<P, S> FileIO for ChunkFileManager<S>
where
P: PathFromLevelFolder + Send + Sync + Sized + Dirtiable + 'static,
@ -157,7 +158,13 @@ where
{
type Data = Arc<RwLock<S::Data>>;
async fn watch_chunks(&self, folder: &LevelFolder, chunks: &[Vector2<i32>]) {
// Changed: Return BoxFuture<()>
fn watch_chunks<'a>(
&'a self,
folder: &'a LevelFolder,
chunks: &'a [Vector2<i32>],
) -> BoxFuture<'a, ()> {
Box::pin(async move {
// It is intentional that regions are watched multiple times (once per chunk)
let mut watchers = self.watchers.write().await;
for chunk in chunks {
@ -172,9 +179,16 @@ where
}
}
}
})
}
async fn unwatch_chunks(&self, folder: &LevelFolder, chunks: &[Vector2<i32>]) {
// Changed: Return BoxFuture<()>
fn unwatch_chunks<'a>(
&'a self,
folder: &'a LevelFolder,
chunks: &'a [Vector2<i32>],
) -> BoxFuture<'a, ()> {
Box::pin(async move {
let mut watchers = self.watchers.write().await;
for chunk in chunks {
let key = S::get_chunk_key(chunk);
@ -189,18 +203,24 @@ where
}
}
}
})
}
async fn clear_watched_chunks(&self) {
// Changed: Return BoxFuture<()>
fn clear_watched_chunks(&self) -> BoxFuture<'_, ()> {
Box::pin(async move {
self.watchers.write().await.clear();
})
}
async fn fetch_chunks(
&self,
folder: &LevelFolder,
chunk_coords: &[Vector2<i32>],
// Changed: Return BoxFuture<()>
fn fetch_chunks<'a>(
&'a self,
folder: &'a LevelFolder,
chunk_coords: &'a [Vector2<i32>],
stream: tokio::sync::mpsc::Sender<LoadedData<Self::Data, ChunkReadingError>>,
) {
) -> BoxFuture<'a, ()> {
Box::pin(async move {
let mut regions_chunks: BTreeMap<String, Vec<Vector2<i32>>> = BTreeMap::new();
for at in chunk_coords {
@ -214,7 +234,10 @@ where
// we use a Sync Closure with an Async Block to execute the tasks concurrently
// Also improves File Cache utilizations.
let region_read_tasks = regions_chunks.into_iter().map(async |(file_name, chunks)| {
let region_read_tasks = regions_chunks.into_iter().map(move |(file_name, chunks)| {
let task_stream = stream.clone();
async move {
let path = P::file_path(folder, &file_name);
let chunk_serializer = match self.get_serializer(&path).await {
Ok(chunk_serializer) => chunk_serializer,
@ -222,18 +245,20 @@ where
unreachable!("Default Serializer must be created")
}
Err(err) => {
let _ = stream.send(LoadedData::Error((chunks[0], err))).await;
let _ = task_stream.send(LoadedData::Error((chunks[0], err))).await;
return;
}
};
// Intermediate channel for wrapping the data with the Arc<RwLock>
let (send, mut recv) = mpsc::channel::<LoadedData<S::Data, ChunkReadingError>>(1);
let (send, mut recv) =
mpsc::channel::<LoadedData<S::Data, ChunkReadingError>>(1);
let intermediary = async {
let intermediary = async move {
// Note: 'task_stream' is captured and moved here
while let Some(data) = recv.recv().await {
let wrapped_data = data.map_loaded(|data| Arc::new(RwLock::new(data)));
if stream.send(wrapped_data).await.is_err() {
if task_stream.send(wrapped_data).await.is_err() {
// Stream is closed, so stop unneeded computation and io
return;
}
@ -248,16 +273,20 @@ where
};
join!(intermediary, reader);
}
});
let _ = join_all(region_read_tasks).await;
})
}
async fn save_chunks(
&self,
folder: &LevelFolder,
// Changed: Return BoxFuture<Result<(), ChunkWritingError>>
fn save_chunks<'a>(
&'a self,
folder: &'a LevelFolder,
chunks_data: Vec<(Vector2<i32>, Self::Data)>,
) -> Result<(), ChunkWritingError> {
) -> BoxFuture<'a, Result<(), ChunkWritingError>> {
Box::pin(async move {
let mut regions_chunks: BTreeMap<String, Vec<Self::Data>> = BTreeMap::new();
for (at, chunk) in chunks_data {
@ -277,7 +306,8 @@ where
// with out waiting the future. Also it improve we File Cache utilizations.
let tasks = regions_chunks
.into_iter()
.map(async |(file_name, chunk_locks)| {
// The closure now needs `move` to capture `self` and `folder` by value
.map(move |(file_name, chunk_locks)| async move {
let path = P::file_path(folder, &file_name);
log::trace!("Updating data for file {}", path.display());
@ -290,9 +320,7 @@ where
error!("Error reading the data before write: {err}");
Err(ChunkWritingError::IoError(err))
}
Err(_) => {
Err(ChunkWritingError::IoError(std::io::ErrorKind::Other))
}
Err(_) => Err(ChunkWritingError::IoError(std::io::ErrorKind::Other)),
}?;
// Create a task for each chunk_lock
@ -375,24 +403,31 @@ where
let _test: Vec<Result<(), ChunkWritingError>> = join_all(tasks).await;
Ok(())
})
}
async fn clean_up_log(&self) {
// Changed: Return BoxFuture<()>
fn clean_up_log(&self) -> BoxFuture<'_, ()> {
Box::pin(async move {
let locks = self.file_locks.read().await;
log::debug!("{} File locks remain in cache", locks.len());
})
}
async fn block_and_await_ongoing_tasks(&self) {
// Changed: Return BoxFuture<()>
fn block_and_await_ongoing_tasks(&self) -> BoxFuture<'_, ()> {
Box::pin(async move {
//we need to block any other operation
let serializer_cache = self.file_locks.write().await;
// Acquire a write lock on all entries to verify they are complete
let tasks = serializer_cache
.values()
.map(async |serializer| serializer.can_remove());
.map(|serializer| serializer.can_remove());
// We need to wait to ensure that all the locks are acquired
// so there is no **operation** ongoing
let _ = join_all(tasks).await;
})
}
}

View File

@ -1,6 +1,5 @@
use std::error;
use std::{error, pin::Pin};
use async_trait::async_trait;
use bytes::Bytes;
use pumpkin_util::math::vector2::Vector2;
@ -39,6 +38,8 @@ pub trait Dirtiable {
fn mark_dirty(&mut self, flag: bool);
}
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
/// Trait to handle the IO of chunks
/// for loading and saving chunks data
/// can be implemented for different types of IO
@ -46,7 +47,6 @@ pub trait Dirtiable {
///
/// The `R` type is the type of the data that will be loaded/saved
/// like ChunkData or EntityData
#[async_trait]
pub trait FileIO
where
Self: Send + Sync,
@ -54,40 +54,47 @@ where
type Data: Send + Sync + Sized;
/// Load the chunks data
async fn fetch_chunks(
&self,
folder: &LevelFolder,
chunk_coords: &[Vector2<i32>],
fn fetch_chunks<'a>(
&'a self,
folder: &'a LevelFolder,
chunk_coords: &'a [Vector2<i32>],
stream: tokio::sync::mpsc::Sender<LoadedData<Self::Data, ChunkReadingError>>,
);
) -> BoxFuture<'a, ()>; // Returns BoxFuture<()>
/// Persist the chunks data
async fn save_chunks(
&self,
folder: &LevelFolder,
fn save_chunks<'a>(
&'a self,
folder: &'a LevelFolder,
chunks_data: Vec<(Vector2<i32>, Self::Data)>,
) -> Result<(), ChunkWritingError>;
) -> BoxFuture<'a, Result<(), ChunkWritingError>>; // Returns BoxFuture<Result>
/// Tells the `ChunkIO` that these chunks are currently loaded in memory
async fn watch_chunks(&self, folder: &LevelFolder, chunks: &[Vector2<i32>]);
fn watch_chunks<'a>(
&'a self,
folder: &'a LevelFolder,
chunks: &'a [Vector2<i32>],
) -> BoxFuture<'a, ()>;
/// Tells the `ChunkIO` that these chunks are no longer loaded in memory
async fn unwatch_chunks(&self, folder: &LevelFolder, chunks: &[Vector2<i32>]);
fn unwatch_chunks<'a>(
&'a self,
folder: &'a LevelFolder,
chunks: &'a [Vector2<i32>],
) -> BoxFuture<'a, ()>;
/// Tells the `ChunkIO` that no more chunks are loaded in memory
async fn clear_watched_chunks(&self);
fn clear_watched_chunks(&self) -> BoxFuture<'_, ()>;
async fn clean_up_log(&self);
fn clean_up_log(&self) -> BoxFuture<'_, ()>;
/// Ensure that all ongoing operations are finished
async fn block_and_await_ongoing_tasks(&self);
fn block_and_await_ongoing_tasks(&self) -> BoxFuture<'_, ()>;
}
/// Trait to serialize and deserialize the chunk data to and from bytes.
///
/// The `Data` type is the type of the data that will be updated or serialized/deserialized
/// like ChunkData or EntityData
#[async_trait]
pub trait ChunkSerializer: Send + Sync + Default {
type Data: Send + Sync + Sized + Dirtiable;
type WriteBackend;
@ -98,18 +105,24 @@ pub trait ChunkSerializer: Send + Sync + Default {
fn should_write(&self, is_watched: bool) -> bool;
/// Serialize the data to bytes.
async fn write(&self, backend: Self::WriteBackend) -> Result<(), std::io::Error>;
fn write(
&self,
backend: Self::WriteBackend,
) -> impl Future<Output = Result<(), std::io::Error>> + Send;
/// Create a new instance from bytes
fn read(r: Bytes) -> Result<Self, ChunkReadingError>;
/// Add the chunk data to the serializer
async fn update_chunk(&mut self, chunk_data: &Self::Data) -> Result<(), ChunkWritingError>;
fn update_chunk(
&mut self,
chunk_data: &Self::Data,
) -> impl Future<Output = Result<(), ChunkWritingError>> + Send;
/// Get the chunks data from the serializer
async fn get_chunks(
fn get_chunks(
&self,
chunks: &[Vector2<i32>],
stream: tokio::sync::mpsc::Sender<LoadedData<Self::Data, ChunkReadingError>>,
);
) -> impl Future<Output = ()> + Send;
}

View File

@ -1,5 +1,4 @@
use crate::item::ItemStack;
use async_trait::async_trait;
use pumpkin_data::item::Item;
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
use std::any::Any;
@ -11,34 +10,86 @@ use std::{
};
use tokio::sync::{Mutex, OwnedMutexGuard};
// Inventory.java
#[async_trait]
pub type InventoryFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait Inventory: Send + Sync + Debug + Clearable {
fn size(&self) -> usize;
async fn is_empty(&self) -> bool;
// --- Asynchronous Methods (Using BlockFuture) ---
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>>;
fn is_empty(&self) -> InventoryFuture<'_, bool>;
async fn remove_stack(&self, slot: usize) -> ItemStack;
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>>;
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack;
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack>;
fn get_max_count_per_stack(&self) -> u8 {
99
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack>;
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()>;
fn on_open(&self) -> InventoryFuture<'_, ()> {
Box::pin(async {})
}
fn on_close(&self) -> InventoryFuture<'_, ()> {
Box::pin(async {})
}
async fn set_stack(&self, slot: usize, stack: ItemStack);
fn count<'a>(&'a self, item: &'a Item) -> InventoryFuture<'a, u8> {
Box::pin(async move {
let mut count = 0;
fn mark_dirty(&self) {}
for i in 0..self.size() {
let slot = self.get_stack(i).await;
let stack = slot.lock().await;
if stack.get_item().id == item.id {
count += stack.item_count;
}
}
async fn write_data(
count
})
}
fn contains_any_predicate<'a>(
&'a self,
predicate: &'a (dyn Fn(OwnedMutexGuard<ItemStack>) -> bool + Sync),
) -> InventoryFuture<'a, bool> {
Box::pin(async move {
for i in 0..self.size() {
let slot = self.get_stack(i).await;
let stack = slot.lock_owned().await;
if predicate(stack) {
return true;
}
}
false
})
}
fn contains_any<'a>(&'a self, items: &'a [Item]) -> InventoryFuture<'a, bool> {
Box::pin(async move {
self.contains_any_predicate(&|stack| {
!stack.is_empty() && items.contains(stack.get_item())
})
.await
})
}
// --- Default Implementation: write_data (Using BlockFuture) ---
fn write_data(
&self,
nbt: &mut pumpkin_nbt::compound::NbtCompound,
nbt: &mut NbtCompound,
stacks: &[Arc<Mutex<ItemStack>>],
include_empty: bool,
) {
) -> InventoryFuture<'_, ()> {
// Clone for the move block (requires `nbt` and `stacks` to be cloneable/to_owned)
let nbt = nbt.to_owned();
let stacks = stacks.to_owned();
Box::pin(async move {
let mut slots = Vec::new();
let mut nbt = nbt;
for (i, item) in stacks.iter().enumerate() {
let stack = item.lock().await;
@ -55,13 +106,18 @@ pub trait Inventory: Send + Sync + Debug + Clearable {
}
nbt.put("Items", NbtTag::List(slots));
})
}
fn read_data(
&self,
nbt: &pumpkin_nbt::compound::NbtCompound,
stacks: &[Arc<Mutex<ItemStack>>],
) {
// --- Synchronous Methods (No Change) ---
fn get_max_count_per_stack(&self) -> u8 {
99
}
fn mark_dirty(&self) {}
fn read_data(&self, nbt: &NbtCompound, stacks: &[Arc<Mutex<ItemStack>>]) {
if let Some(inventory_list) = nbt.get_list("Items") {
for tag in inventory_list {
if let Some(item_compound) = tag.extract_compound()
@ -71,7 +127,6 @@ pub trait Inventory: Send + Sync + Debug + Clearable {
if slot < stacks.len()
&& let Some(item_stack) = ItemStack::read_item_stack(item_compound)
{
// This won't error cause it's only called on initialization
*stacks[slot].try_lock().unwrap() = item_stack;
}
}
@ -79,15 +134,6 @@ pub trait Inventory: Send + Sync + Debug + Clearable {
}
}
/*
boolean canPlayerUse(PlayerEntity player);
*/
// TODO: Add (PlayerEntity player)
async fn on_open(&self) {}
async fn on_close(&self) {}
/// isValid is source
fn is_valid_slot_for(&self, _slot: usize, _stack: &ItemStack) -> bool {
true
}
@ -101,42 +147,6 @@ pub trait Inventory: Send + Sync + Debug + Clearable {
true
}
async fn count(&self, item: &Item) -> u8 {
let mut count = 0;
for i in 0..self.size() {
let slot = self.get_stack(i).await;
let stack = slot.lock().await;
if stack.get_item().id == item.id {
count += stack.item_count;
}
}
count
}
async fn contains_any_predicate(
&self,
predicate: &(dyn Fn(OwnedMutexGuard<ItemStack>) -> bool + Sync),
) -> bool {
for i in 0..self.size() {
let slot = self.get_stack(i).await;
let stack = slot.lock_owned().await;
if predicate(stack) {
return true;
}
}
false
}
async fn contains_any(&self, items: &[Item]) -> bool {
self.contains_any_predicate(&|stack| !stack.is_empty() && items.contains(stack.get_item()))
.await
}
// TODO: canPlayerUse
fn as_any(&self) -> &dyn Any;
}

View File

@ -43,7 +43,6 @@ tokio = { workspace = true, features = [
] }
rayon.workspace = true
thiserror.workspace = true
async-trait.workspace = true
futures.workspace = true
# config

View File

@ -5,11 +5,13 @@ use crate::block::{
registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs},
};
use async_trait::async_trait;
use pumpkin_data::block_properties::{BarrelLikeProperties, BlockProperties};
use pumpkin_inventory::generic_container_screen_handler::create_generic_9x3;
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerFactory};
use pumpkin_inventory::screen_handler::{
BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::text::TextComponent;
use pumpkin_world::BlockStateId;
@ -19,17 +21,19 @@ use tokio::sync::Mutex;
struct BarrelScreenFactory(Arc<dyn Inventory>);
#[async_trait]
impl ScreenHandlerFactory for BarrelScreenFactory {
async fn create_screen_handler(
&self,
fn create_screen_handler<'a>(
&'a self,
sync_id: u8,
player_inventory: &Arc<PlayerInventory>,
_player: &dyn InventoryPlayer,
) -> Option<Arc<Mutex<dyn ScreenHandler>>> {
Some(Arc::new(Mutex::new(
create_generic_9x3(sync_id, player_inventory, self.0.clone()).await,
)))
player_inventory: &'a Arc<PlayerInventory>,
_player: &'a dyn InventoryPlayer,
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
Box::pin(async move {
let handler = create_generic_9x3(sync_id, player_inventory, self.0.clone()).await;
let concrete_arc = Arc::new(Mutex::new(handler));
Some(concrete_arc as SharedScreenHandler)
})
}
fn get_display_name(&self) -> TextComponent {

View File

@ -1,6 +1,5 @@
use std::sync::Arc;
use async_trait::async_trait;
use futures::future::join;
use pumpkin_data::block_properties::{
BlockProperties, ChestLikeProperties, ChestType, HorizontalFacing,
@ -11,7 +10,9 @@ use pumpkin_data::{Block, BlockDirection};
use pumpkin_inventory::double::DoubleInventory;
use pumpkin_inventory::generic_container_screen_handler::{create_generic_9x3, create_generic_9x6};
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerFactory};
use pumpkin_inventory::screen_handler::{
BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
};
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::text::TextComponent;
use pumpkin_world::BlockStateId;
@ -34,19 +35,24 @@ use crate::{
struct ChestScreenFactory(Arc<dyn Inventory>);
#[async_trait]
impl ScreenHandlerFactory for ChestScreenFactory {
async fn create_screen_handler(
&self,
fn create_screen_handler<'a>(
&'a self,
sync_id: u8,
player_inventory: &Arc<PlayerInventory>,
_player: &dyn InventoryPlayer,
) -> Option<Arc<Mutex<dyn ScreenHandler>>> {
Some(Arc::new(Mutex::new(if self.0.size() > 27 {
player_inventory: &'a Arc<PlayerInventory>,
_player: &'a dyn InventoryPlayer,
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
Box::pin(async move {
let concrete_handler = if self.0.size() > 27 {
create_generic_9x6(sync_id, player_inventory, self.0.clone()).await
} else {
create_generic_9x3(sync_id, player_inventory, self.0.clone()).await
})))
};
let concrete_arc = Arc::new(Mutex::new(concrete_handler));
Some(concrete_arc as SharedScreenHandler)
})
}
fn get_display_name(&self) -> TextComponent {

View File

@ -1,9 +1,11 @@
use crate::block::registry::BlockActionResult;
use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs};
use async_trait::async_trait;
use pumpkin_inventory::crafting::crafting_screen_handler::CraftingTableScreenHandler;
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerFactory};
use pumpkin_inventory::screen_handler::{
BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::text::TextComponent;
use std::sync::Arc;
@ -26,17 +28,19 @@ impl BlockBehaviour for CraftingTableBlock {
struct CraftingTableScreenFactory;
#[async_trait]
impl ScreenHandlerFactory for CraftingTableScreenFactory {
async fn create_screen_handler(
&self,
fn create_screen_handler<'a>(
&'a self,
sync_id: u8,
player_inventory: &Arc<PlayerInventory>,
_player: &dyn InventoryPlayer,
) -> Option<Arc<Mutex<dyn ScreenHandler>>> {
Some(Arc::new(Mutex::new(
CraftingTableScreenHandler::new(sync_id, player_inventory).await,
)))
player_inventory: &'a Arc<PlayerInventory>,
_player: &'a dyn InventoryPlayer,
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
Box::pin(async move {
let handler = CraftingTableScreenHandler::new(sync_id, player_inventory).await;
let concrete_arc = Arc::new(Mutex::new(handler));
Some(concrete_arc as SharedScreenHandler)
})
}
fn get_display_name(&self) -> TextComponent {

View File

@ -4,12 +4,11 @@ use crate::block::{
BlockBehaviour, BlockFuture, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs,
registry::BlockActionResult,
};
use async_trait::async_trait;
use pumpkin_data::block_properties::{BlockProperties, LadderLikeProperties};
use pumpkin_inventory::{
generic_container_screen_handler::create_generic_9x3,
player::player_inventory::PlayerInventory,
screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerFactory},
screen_handler::{BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler},
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::text::TextComponent;
@ -20,17 +19,19 @@ use tokio::sync::Mutex;
struct EnderChestScreenFactory(Arc<dyn Inventory>);
#[async_trait]
impl ScreenHandlerFactory for EnderChestScreenFactory {
async fn create_screen_handler(
&self,
fn create_screen_handler<'a>(
&'a self,
sync_id: u8,
player_inventory: &Arc<PlayerInventory>,
_player: &dyn InventoryPlayer,
) -> Option<Arc<Mutex<dyn ScreenHandler>>> {
Some(Arc::new(Mutex::new(
create_generic_9x3(sync_id, player_inventory, self.0.clone()).await,
)))
player_inventory: &'a Arc<PlayerInventory>,
_player: &'a dyn InventoryPlayer,
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
Box::pin(async move {
let handler = create_generic_9x3(sync_id, player_inventory, self.0.clone()).await;
let concrete_arc = Arc::new(Mutex::new(handler));
Some(concrete_arc as SharedScreenHandler)
})
}
fn get_display_name(&self) -> TextComponent {

View File

@ -1,9 +1,10 @@
use std::sync::Arc;
use async_trait::async_trait;
use pumpkin_data::block_properties::{BlockProperties, FurnaceLikeProperties};
use pumpkin_inventory::{
furnace::furnace_screen_handler::FurnaceScreenHandler, screen_handler::ScreenHandlerFactory,
furnace::furnace_screen_handler::FurnaceScreenHandler,
player::player_inventory::PlayerInventory,
screen_handler::{BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler},
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::text::TextComponent;
@ -33,22 +34,26 @@ impl FurnaceScreenFactory {
}
}
#[async_trait]
impl ScreenHandlerFactory for FurnaceScreenFactory {
async fn create_screen_handler(
&self,
fn create_screen_handler<'a>(
&'a self,
sync_id: u8,
player_inventory: &Arc<pumpkin_inventory::player::player_inventory::PlayerInventory>,
_player: &dyn pumpkin_inventory::screen_handler::InventoryPlayer,
) -> Option<Arc<Mutex<dyn pumpkin_inventory::screen_handler::ScreenHandler>>> {
let furnace_screen_handler = FurnaceScreenHandler::new(
player_inventory: &'a Arc<PlayerInventory>,
_player: &'a dyn InventoryPlayer,
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
Box::pin(async move {
let concrete_handler = FurnaceScreenHandler::new(
sync_id,
player_inventory,
self.inventory.clone(),
self.block_entity.clone(),
)
.await;
Some(Arc::new(Mutex::new(furnace_screen_handler)))
let concrete_arc = Arc::new(Mutex::new(concrete_handler));
Some(concrete_arc as SharedScreenHandler)
})
}
fn get_display_name(&self) -> pumpkin_util::text::TextComponent {

View File

@ -7,12 +7,14 @@ use crate::block::{
{BlockBehaviour, NormalUseArgs},
};
use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::block_properties::{BlockProperties, HopperFacing};
use pumpkin_data::{Block, BlockDirection};
use pumpkin_inventory::generic_container_screen_handler::create_hopper;
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerFactory};
use pumpkin_inventory::screen_handler::{
BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::text::TextComponent;
@ -24,17 +26,20 @@ use tokio::sync::Mutex;
struct HopperBlockScreenFactory(Arc<dyn Inventory>);
#[async_trait]
impl ScreenHandlerFactory for HopperBlockScreenFactory {
async fn create_screen_handler(
&self,
fn create_screen_handler<'a>(
&'a self,
sync_id: u8,
player_inventory: &Arc<PlayerInventory>,
_player: &dyn InventoryPlayer,
) -> Option<Arc<Mutex<dyn ScreenHandler>>> {
Some(Arc::new(Mutex::new(
create_hopper(sync_id, player_inventory, self.0.clone()).await,
)))
player_inventory: &'a Arc<PlayerInventory>,
_player: &'a dyn InventoryPlayer,
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
Box::pin(async move {
let concrete_handler = create_hopper(sync_id, player_inventory, self.0.clone()).await;
let concrete_arc = Arc::new(Mutex::new(concrete_handler));
Some(concrete_arc as SharedScreenHandler)
})
}
fn get_display_name(&self) -> TextComponent {

View File

@ -1,8 +1,7 @@
use async_trait::async_trait;
use pumpkin_data::block_properties::{BlockProperties, HorizontalFacing, Integer1To4};
use pumpkin_world::BlockStateId;
use crate::block::{BlockBehaviour, CanUpdateAtArgs};
use crate::block::{BlockBehaviour, BlockFuture, CanUpdateAtArgs};
use crate::block::{BlockIsReplacing, OnPlaceArgs};
pub trait SegmentProperties {
@ -43,7 +42,6 @@ impl_segment_properties!(
segment_amount
);
#[async_trait]
pub trait Segmented: BlockBehaviour {
type Properties: BlockProperties + SegmentProperties;
@ -80,30 +78,34 @@ pub trait Segmented: BlockBehaviour {
}
}
async fn can_update_at(&self, ctx: CanUpdateAtArgs<'_>) -> bool {
fn can_update_at<'a>(&'a self, ctx: CanUpdateAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
let current_props = Self::Properties::from_state_id(ctx.state_id, ctx.block);
self.can_add_segment(&current_props)
})
}
async fn on_place(&self, ctx: OnPlaceArgs<'_>) -> BlockStateId {
if let BlockIsReplacing::Itself(existing_state_id) = ctx.replacing {
let mut props = Self::Properties::from_state_id(existing_state_id, ctx.block);
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
if let BlockIsReplacing::Itself(existing_state_id) = args.replacing {
let mut props = Self::Properties::from_state_id(existing_state_id, args.block);
if self.can_add_segment(&props) {
let current_amount = props.get_segment_amount();
let next_amount = self.get_next_segment_amount(current_amount);
props.set_segment_amount(next_amount);
props.to_state_id(ctx.block)
props.to_state_id(args.block)
} else {
existing_state_id
}
} else {
// Set first segment orientation based on player direction
let player_facing = ctx.player.living_entity.entity.get_horizontal_facing();
let mut props = Self::Properties::default(ctx.block);
let player_facing = args.player.living_entity.entity.get_horizontal_facing();
let mut props = Self::Properties::default(args.block);
props.set_segment_amount(Integer1To4::L1);
props.set_facing(self.get_facing_for_segment(player_facing, Integer1To4::L1));
props.to_state_id(ctx.block)
}
props.to_state_id(args.block)
}
})
}
}

View File

@ -6,14 +6,16 @@ use crate::block::{
};
use crate::entity::Entity;
use crate::entity::item::ItemEntity;
use async_trait::async_trait;
use pumpkin_data::FacingExt;
use pumpkin_data::block_properties::{BlockProperties, Facing};
use pumpkin_data::entity::EntityType;
use pumpkin_data::world::WorldEvent;
use pumpkin_inventory::generic_container_screen_handler::create_generic_3x3;
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerFactory};
use pumpkin_inventory::screen_handler::{
BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::text::TextComponent;
@ -30,17 +32,19 @@ use uuid::Uuid;
struct DropperScreenFactory(Arc<dyn Inventory>);
#[async_trait]
impl ScreenHandlerFactory for DropperScreenFactory {
async fn create_screen_handler(
&self,
fn create_screen_handler<'a>(
&'a self,
sync_id: u8,
player_inventory: &Arc<PlayerInventory>,
_player: &dyn InventoryPlayer,
) -> Option<Arc<Mutex<dyn ScreenHandler>>> {
Some(Arc::new(Mutex::new(
create_generic_3x3(sync_id, player_inventory, self.0.clone()).await,
)))
player_inventory: &'a Arc<PlayerInventory>,
_player: &'a dyn InventoryPlayer,
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
Box::pin(async move {
let handler = create_generic_3x3(sync_id, player_inventory, self.0.clone()).await;
let screen_handler_arc = Arc::new(Mutex::new(handler));
Some(screen_handler_arc as SharedScreenHandler)
})
}
fn get_display_name(&self) -> TextComponent {

View File

@ -5,12 +5,14 @@ use crate::block::{
registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs},
};
use async_trait::async_trait;
use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::tag::{RegistryKey, get_tag_values};
use pumpkin_inventory::generic_container_screen_handler::create_generic_9x3;
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerFactory};
use pumpkin_inventory::screen_handler::{
BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
};
use pumpkin_util::text::TextComponent;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::entities::shulker_box::ShulkerBoxBlockEntity;
@ -19,17 +21,19 @@ use tokio::sync::Mutex;
struct ShulkerBoxScreenFactory(Arc<dyn Inventory>);
#[async_trait]
impl ScreenHandlerFactory for ShulkerBoxScreenFactory {
async fn create_screen_handler(
&self,
fn create_screen_handler<'a>(
&'a self,
sync_id: u8,
player_inventory: &Arc<PlayerInventory>,
_player: &dyn InventoryPlayer,
) -> Option<Arc<Mutex<dyn ScreenHandler>>> {
Some(Arc::new(Mutex::new(
create_generic_9x3(sync_id, player_inventory, self.0.clone()).await,
)))
player_inventory: &'a Arc<PlayerInventory>,
_player: &'a dyn InventoryPlayer,
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
Box::pin(async move {
let handler = create_generic_9x3(sync_id, player_inventory, self.0.clone()).await;
let screen_handler_arc = Arc::new(Mutex::new(handler));
Some(screen_handler_arc as SharedScreenHandler)
})
}
fn get_display_name(&self) -> TextComponent {

View File

@ -1,9 +1,8 @@
pub mod context;
pub mod events;
use std::sync::Arc;
use std::{pin::Pin, sync::Arc};
use async_trait::async_trait;
pub use context::*;
pub use events::*;
@ -24,12 +23,12 @@ pub struct PluginMetadata<'s> {
pub description: &'s str,
}
type PluginFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
/// Trait representing a plugin with asynchronous lifecycle methods.
///
/// This trait defines the required methods for a plugin, including hooks for when
/// the plugin is loaded and unloaded. It is marked with `async_trait` to allow
/// for asynchronous implementations.
#[async_trait]
/// the plugin is loaded and unloaded.
pub trait Plugin: Send + Sync + 'static {
/// Asynchronous method called when the plugin is loaded.
///
@ -40,8 +39,8 @@ pub trait Plugin: Send + Sync + 'static {
///
/// # Returns
/// - `Ok(())` on success, or `Err(String)` on failure.
async fn on_load(&mut self, _server: Arc<Context>) -> Result<(), String> {
Ok(())
fn on_load(&mut self, _server: Arc<Context>) -> PluginFuture<'_, Result<(), String>> {
Box::pin(async move { Ok(()) })
}
/// Asynchronous method called when the plugin is unloaded.
@ -53,7 +52,7 @@ pub trait Plugin: Send + Sync + 'static {
///
/// # Returns
/// - `Ok(())` on success, or `Err(String)` on failure.
async fn on_unload(&mut self, _server: Arc<Context>) -> Result<(), String> {
Ok(())
fn on_unload(&mut self, _server: Arc<Context>) -> PluginFuture<'_, Result<(), String>> {
Box::pin(async move { Ok(()) })
}
}

View File

@ -1,10 +1,10 @@
use async_trait::async_trait;
use futures::future::join_all;
use loader::{LoaderError, PluginLoader, native::NativePluginLoader};
use std::{
any::Any,
collections::{HashMap, HashSet},
path::{Path, PathBuf},
pin::Pin,
sync::Arc,
};
use thiserror::Error;
@ -16,26 +16,31 @@ pub mod loader;
use crate::{PERMISSION_MANAGER, server::Server};
pub use api::*;
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
/// A trait for handling events dynamically.
///
/// This trait allows for handling events of any type that implements the `Event` trait.
#[async_trait]
pub trait DynEventHandler: Send + Sync {
/// Asynchronously handles a dynamic event.
///
/// # Arguments
/// - `event`: A reference to the event to handle.
async fn handle_dyn(&self, _server: &Arc<Server>, event: &(dyn Payload + Send + Sync));
fn handle_dyn<'a>(
&'a self,
_server: &'a Arc<Server>,
event: &'a (dyn Payload + Send + Sync),
) -> BoxFuture<'a, ()>;
/// Asynchronously handles a blocking dynamic event.
///
/// # Arguments
/// - `event`: A mutable reference to the event to handle.
async fn handle_blocking_dyn(
&self,
_server: &Arc<Server>,
_event: &mut (dyn Payload + Send + Sync),
);
fn handle_blocking_dyn<'a>(
&'a self,
_server: &'a Arc<Server>,
_event: &'a mut (dyn Payload + Send + Sync),
) -> BoxFuture<'a, ()>;
/// Checks if the event handler is blocking.
///
@ -53,19 +58,22 @@ pub trait DynEventHandler: Send + Sync {
/// A trait for handling specific events.
///
/// This trait allows for handling events of a specific type that implements the `Event` trait.
#[async_trait]
pub trait EventHandler<E: Payload>: Send + Sync {
/// Asynchronously handles an event of type `E`.
///
/// # Arguments
/// - `event`: A reference to the event to handle.
async fn handle(&self, _server: &Arc<Server>, _event: &E) {}
fn handle(&self, _server: &Arc<Server>, _event: &E) -> BoxFuture<'_, ()> {
Box::pin(async {})
}
/// Asynchronously handles a blocking event of type `E`.
///
/// # Arguments
/// - `event`: A mutable reference to the event to handle.
async fn handle_blocking(&self, _server: &Arc<Server>, _event: &mut E) {}
fn handle_blocking(&self, _server: &Arc<Server>, _event: &mut E) -> BoxFuture<'_, ()> {
Box::pin(async move {})
}
}
/// A struct representing a typed event handler.
@ -82,28 +90,37 @@ where
_phantom: std::marker::PhantomData<E>,
}
#[async_trait]
impl<E, H> DynEventHandler for TypedEventHandler<E, H>
where
E: Payload + Send + Sync + 'static,
H: EventHandler<E> + Send + Sync,
{
/// Asynchronously handles a blocking dynamic event.
async fn handle_blocking_dyn(
&self,
server: &Arc<Server>,
event: &mut (dyn Payload + Send + Sync),
) {
fn handle_blocking_dyn<'a>(
&'a self,
server: &'a Arc<Server>,
event: &'a mut (dyn Payload + Send + Sync),
) -> BoxFuture<'a, ()> {
Box::pin(async move {
if let Some(typed_event) = <dyn Payload>::downcast_mut(event) {
// The handler.handle_blocking call now returns a Future, which we await.
self.handler.handle_blocking(server, typed_event).await;
}
})
}
/// Asynchronously handles a dynamic event.
async fn handle_dyn(&self, server: &Arc<Server>, event: &(dyn Payload + Send + Sync)) {
fn handle_dyn<'a>(
&'a self,
server: &'a Arc<Server>,
event: &'a (dyn Payload + Send + Sync),
) -> BoxFuture<'a, ()> {
Box::pin(async move {
if let Some(typed_event) = <dyn Payload>::downcast_ref(event) {
// The handler.handle call now returns a Future, which we await.
self.handler.handle(server, typed_event).await;
}
})
}
/// Checks if the handler is blocking.