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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,8 +10,8 @@ use crate::screen_handler::{
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture, InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture,
ScreenHandlerListener, ScreenHandlerListener,
}; };
use crate::slot::{NormalSlot, Slot}; use crate::slot::{BoxFuture, NormalSlot, Slot};
use async_trait::async_trait;
use crossbeam_utils::atomic::AtomicCell; use crossbeam_utils::atomic::AtomicCell;
use pumpkin_data::recipes::{CraftingRecipeTypes, RECIPES_CRAFTING, RecipeResultStruct}; use pumpkin_data::recipes::{CraftingRecipeTypes, RECIPES_CRAFTING, RecipeResultStruct};
use pumpkin_data::screen::WindowType; use pumpkin_data::screen::WindowType;
@ -324,7 +324,6 @@ impl ResultSlot {
} }
} }
#[async_trait]
impl Slot for ResultSlot { impl Slot for ResultSlot {
fn get_inventory(&self) -> Arc<dyn Inventory> { fn get_inventory(&self) -> Arc<dyn Inventory> {
self.inventory.clone() self.inventory.clone()
@ -338,73 +337,95 @@ impl Slot for ResultSlot {
self.id.store(id as u8, Ordering::Relaxed); 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(
// refill the result slot with the recipe result &self,
self.refill_output().await; _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>(
for i in 0..self.inventory.size() { &'a self,
let slot = self.inventory.get_stack(i).await; player: &'a dyn InventoryPlayer,
let mut stack = slot.lock().await; stack: &'a ItemStack,
if !stack.is_empty() { ) -> BoxFuture<'a, ()> {
//TODO: Handle remaining items. Box::pin(async move {
stack.item_count -= 1; for i in 0..self.inventory.size() {
let slot = self.inventory.get_stack(i).await;
let mut stack = slot.lock().await;
if !stack.is_empty() {
//TODO: Handle remaining items.
stack.item_count -= 1;
}
} }
} self.stat_crafted(stack.item_count, player);
self.stat_crafted(stack.item_count, player); self.mark_dirty().await;
self.mark_dirty().await; })
} }
async fn can_insert(&self, _stack: &ItemStack) -> bool { fn can_insert(&self, _stack: &ItemStack) -> BoxFuture<'_, bool> {
false Box::pin(async move { false })
} }
async fn get_stack(&self) -> Arc<Mutex<ItemStack>> { fn get_stack(&self) -> BoxFuture<'_, Arc<Mutex<ItemStack>>> {
self.result.clone() Box::pin(async move { self.result.clone() })
} }
async fn get_cloned_stack(&self) -> ItemStack { fn get_cloned_stack(&self) -> BoxFuture<'_, ItemStack> {
self.result.lock().await.clone() Box::pin(async move { self.result.lock().await.clone() })
} }
async fn has_stack(&self) -> bool { fn has_stack(&self) -> BoxFuture<'_, bool> {
!self.result.lock().await.is_empty() Box::pin(async move { !self.result.lock().await.is_empty() })
} }
async fn set_stack(&self, _stack: ItemStack) { fn set_stack(&self, _stack: ItemStack) -> BoxFuture<'_, ()> {
self.refill_output().await; 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<'_, ()> {
self.refill_output().await; Box::pin(async move {
self.refill_output().await;
})
} }
async fn mark_dirty(&self) { fn mark_dirty(&self) -> BoxFuture<'_, ()> {
self.inventory.mark_dirty(); Box::pin(async move {
self.inventory.mark_dirty();
})
} }
async fn get_max_item_count(&self) -> u8 { fn get_max_item_count(&self) -> BoxFuture<'_, u8> {
let mut count = u8::MAX; Box::pin(async move {
for i in 0..self.inventory.size() { let mut count = u8::MAX;
let slot = self.inventory.get_stack(i).await; for i in 0..self.inventory.size() {
let slot = slot.lock().await; let slot = self.inventory.get_stack(i).await;
if !slot.is_empty() { let slot = slot.lock().await;
count = count.min(slot.item_count); if !slot.is_empty() {
count = count.min(slot.item_count);
}
} }
} count
count })
} }
async fn take_stack(&self, _amount: u8) -> ItemStack { fn take_stack(&self, _amount: u8) -> BoxFuture<'_, ItemStack> {
if self.has_stack().await { Box::pin(async move {
let stack = self.result.lock().await; if self.has_stack().await {
// Vanilla: net.minecraft.world.inventory.ResultContainer#removeItem let stack = self.result.lock().await;
// Regardless of the amount, we always return the full stack // Vanilla: net.minecraft.world.inventory.ResultContainer#removeItem
stack.clone() // Regardless of the amount, we always return the full stack
} else { stack.clone()
ItemStack::EMPTY.clone() } else {
} ItemStack::EMPTY.clone()
}
})
} }
} }

View File

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

View File

@ -1,10 +1,9 @@
use std::sync::{Arc, atomic::AtomicU8}; use std::sync::{Arc, atomic::AtomicU8};
use async_trait::async_trait;
use pumpkin_data::{fuels::is_fuel, item::Item}; use pumpkin_data::{fuels::is_fuel, item::Item};
use pumpkin_world::inventory::Inventory; use pumpkin_world::inventory::Inventory;
use crate::slot::Slot; use crate::slot::{BoxFuture, Slot};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum FurnaceSlotType { pub enum FurnaceSlotType {
@ -31,7 +30,6 @@ impl FurnaceSlot {
} }
} }
} }
#[async_trait]
impl Slot for FurnaceSlot { impl Slot for FurnaceSlot {
fn get_inventory(&self) -> Arc<dyn Inventory> { fn get_inventory(&self) -> Arc<dyn Inventory> {
self.inventory.clone() self.inventory.clone()
@ -46,15 +44,21 @@ impl Slot for FurnaceSlot {
.store(id as u8, std::sync::atomic::Ordering::Relaxed); .store(id as u8, std::sync::atomic::Ordering::Relaxed);
} }
async fn mark_dirty(&self) { fn mark_dirty(&self) -> BoxFuture<'_, ()> {
self.inventory.mark_dirty(); 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> {
match self.slot_type { Box::pin(async move {
FurnaceSlotType::Top => true, match self.slot_type {
FurnaceSlotType::Bottom => is_fuel(stack.item.id) || stack.item.id == Item::BUCKET.id, FurnaceSlotType::Top => true,
FurnaceSlotType::Side => false, 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 std::{any::Any, array::from_fn, pin::Pin, sync::Arc};
use async_trait::async_trait;
use pumpkin_world::{ use pumpkin_world::{
block::viewer::ViewerCountTracker, block::viewer::ViewerCountTracker,
inventory::{Clearable, Inventory, split_stack}, inventory::{Clearable, Inventory, InventoryFuture, split_stack},
item::ItemStack, item::ItemStack,
}; };
use tokio::sync::Mutex; use tokio::sync::Mutex;
@ -46,51 +45,60 @@ impl EnderChestInventory {
} }
} }
#[async_trait]
impl Inventory for EnderChestInventory { impl Inventory for EnderChestInventory {
fn size(&self) -> usize { fn size(&self) -> usize {
self.items.len() self.items.len()
} }
async fn is_empty(&self) -> bool { fn is_empty(&self) -> InventoryFuture<'_, bool> {
for slot in self.items.iter() { Box::pin(async move {
if !slot.lock().await.is_empty() { for slot in self.items.iter() {
return false; if !slot.lock().await.is_empty() {
return false;
}
} }
}
true true
})
} }
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> { fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
self.items[slot].clone() Box::pin(async move { self.items[slot].clone() })
} }
async fn remove_stack(&self, slot: usize) -> ItemStack { fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
let mut removed = ItemStack::EMPTY.clone(); Box::pin(async move {
let mut guard = self.items[slot].lock().await; let mut removed = ItemStack::EMPTY.clone();
std::mem::swap(&mut removed, &mut *guard); let mut guard = self.items[slot].lock().await;
removed std::mem::swap(&mut removed, &mut *guard);
removed
})
} }
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
split_stack(&self.items, slot, amount).await 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<'_, ()> {
*self.items[slot].lock().await = stack; Box::pin(async move {
*self.items[slot].lock().await = stack;
})
} }
async fn on_open(&self) { fn on_open(&self) -> InventoryFuture<'_, ()> {
if let Some(tracker) = self.tracker.lock().await.as_ref() { Box::pin(async move {
tracker.open_container(); if let Some(tracker) = self.tracker.lock().await.as_ref() {
} tracker.open_container();
}
})
} }
async fn on_close(&self) { fn on_close(&self) -> InventoryFuture<'_, ()> {
if let Some(tracker) = self.tracker.lock().await.as_ref() { Box::pin(async move {
tracker.close_container(); if let Some(tracker) = self.tracker.lock().await.as_ref() {
} tracker.close_container();
}
})
} }
fn mark_dirty(&self) {} fn mark_dirty(&self) {}

View File

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

View File

@ -4,7 +4,6 @@ use crate::{
slot::{NormalSlot, Slot}, slot::{NormalSlot, Slot},
sync_handler::{SyncHandler, TrackedStack}, sync_handler::{SyncHandler, TrackedStack},
}; };
use async_trait::async_trait;
use log::warn; use log::warn;
use pumpkin_data::{ use pumpkin_data::{
data_component_impl::{EquipmentSlot, EquipmentType, EquippableImpl}, 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 { pub trait ScreenHandlerFactory: Send + Sync {
async fn create_screen_handler( fn create_screen_handler<'a>(
&self, &'a self,
sync_id: u8, sync_id: u8,
player_inventory: &Arc<PlayerInventory>, player_inventory: &'a Arc<PlayerInventory>,
player: &dyn InventoryPlayer, player: &'a dyn InventoryPlayer,
) -> Option<Arc<Mutex<dyn ScreenHandler>>>; ) -> BoxFuture<'a, Option<SharedScreenHandler>>;
fn get_display_name(&self) -> TextComponent; fn get_display_name(&self) -> TextComponent;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,6 @@
use crate::block::entities::BlockEntity; 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::item::ItemStack;
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;
use rand::{Rng, rng}; use rand::{Rng, rng};
@ -95,39 +94,44 @@ impl DropperBlockEntity {
} }
} }
#[async_trait]
impl Inventory for DropperBlockEntity { impl Inventory for DropperBlockEntity {
fn size(&self) -> usize { fn size(&self) -> usize {
self.items.len() self.items.len()
} }
async fn is_empty(&self) -> bool { fn is_empty(&self) -> InventoryFuture<'_, bool> {
for slot in self.items.iter() { Box::pin(async move {
if !slot.lock().await.is_empty() { for slot in self.items.iter() {
return false; if !slot.lock().await.is_empty() {
return false;
}
} }
}
true true
})
} }
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> { fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
self.items[slot].clone() Box::pin(async move { self.items[slot].clone() })
} }
async fn remove_stack(&self, slot: usize) -> ItemStack { fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
let mut removed = ItemStack::EMPTY.clone(); Box::pin(async move {
let mut guard = self.items[slot].lock().await; let mut removed = ItemStack::EMPTY.clone();
std::mem::swap(&mut removed, &mut *guard); let mut guard = self.items[slot].lock().await;
removed std::mem::swap(&mut removed, &mut *guard);
removed
})
} }
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
split_stack(&self.items, slot, amount).await 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<'_, ()> {
*self.items[slot].lock().await = stack; Box::pin(async move {
*self.items[slot].lock().await = stack;
})
} }
fn mark_dirty(&self) { fn mark_dirty(&self) {

View File

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

View File

@ -1,9 +1,8 @@
use crate::BlockStateId; use crate::BlockStateId;
use crate::block::entities::BlockEntity; 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::item::ItemStack;
use crate::world::SimpleWorld; use crate::world::SimpleWorld;
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};
@ -294,39 +293,44 @@ impl HopperBlockEntity {
} }
} }
#[async_trait]
impl Inventory for HopperBlockEntity { impl Inventory for HopperBlockEntity {
fn size(&self) -> usize { fn size(&self) -> usize {
self.items.len() self.items.len()
} }
async fn is_empty(&self) -> bool { fn is_empty(&self) -> InventoryFuture<'_, bool> {
for slot in self.items.iter() { Box::pin(async move {
if !slot.lock().await.is_empty() { for slot in self.items.iter() {
return false; if !slot.lock().await.is_empty() {
return false;
}
} }
}
true true
})
} }
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> { fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
self.items[slot].clone() Box::pin(async move { self.items[slot].clone() })
} }
async fn remove_stack(&self, slot: usize) -> ItemStack { fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
let mut removed = ItemStack::EMPTY.clone(); Box::pin(async move {
let mut guard = self.items[slot].lock().await; let mut removed = ItemStack::EMPTY.clone();
std::mem::swap(&mut removed, &mut *guard); let mut guard = self.items[slot].lock().await;
removed std::mem::swap(&mut removed, &mut *guard);
removed
})
} }
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
split_stack(&self.items, slot, amount).await 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<'_, ()> {
*self.items[slot].lock().await = stack; Box::pin(async move {
*self.items[slot].lock().await = stack;
})
} }
fn mark_dirty(&self) { fn mark_dirty(&self) {

View File

@ -1,4 +1,3 @@
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;
@ -6,6 +5,7 @@ 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::pin::Pin;
use std::sync::atomic::Ordering;
use std::{ use std::{
array::from_fn, array::from_fn,
sync::{Arc, atomic::AtomicBool}, sync::{Arc, atomic::AtomicBool},
@ -13,6 +13,7 @@ use std::{
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture}; use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture};
use crate::inventory::InventoryFuture;
use crate::world::SimpleWorld; use crate::world::SimpleWorld;
use crate::{ use crate::{
inventory::{ inventory::{
@ -175,51 +176,60 @@ impl ShulkerBoxBlockEntity {
} }
} }
#[async_trait]
impl Inventory for ShulkerBoxBlockEntity { impl Inventory for ShulkerBoxBlockEntity {
fn size(&self) -> usize { fn size(&self) -> usize {
self.items.len() self.items.len()
} }
async fn is_empty(&self) -> bool { fn is_empty(&self) -> InventoryFuture<'_, bool> {
for slot in self.items.iter() { Box::pin(async move {
if !slot.lock().await.is_empty() { for slot in self.items.iter() {
return false; if !slot.lock().await.is_empty() {
return false;
}
} }
}
true true
})
} }
async fn get_stack(&self, slot: usize) -> Arc<Mutex<ItemStack>> { fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
self.items[slot].clone() Box::pin(async move { self.items[slot].clone() })
} }
async fn remove_stack(&self, slot: usize) -> ItemStack { fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
let mut removed = ItemStack::EMPTY.clone(); Box::pin(async move {
let mut guard = self.items[slot].lock().await; let mut removed = ItemStack::EMPTY.clone();
std::mem::swap(&mut removed, &mut *guard); let mut guard = self.items[slot].lock().await;
removed std::mem::swap(&mut removed, &mut *guard);
removed
})
} }
async fn remove_stack_specific(&self, slot: usize, amount: u8) -> ItemStack { fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
split_stack(&self.items, slot, amount).await 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<'_, ()> {
*self.items[slot].lock().await = stack; Box::pin(async move {
*self.items[slot].lock().await = stack;
})
} }
async fn on_open(&self) { fn on_open(&self) -> InventoryFuture<'_, ()> {
self.viewers.open_container(); Box::pin(async move {
self.viewers.open_container();
})
} }
async fn on_close(&self) { fn on_close(&self) -> InventoryFuture<'_, ()> {
self.viewers.close_container(); Box::pin(async move {
self.viewers.close_container();
})
} }
fn mark_dirty(&self) { 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 { fn as_any(&self) -> &dyn Any {

View File

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

View File

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

View File

@ -6,7 +6,6 @@ use std::{
sync::Arc, sync::Arc,
}; };
use async_trait::async_trait;
use futures::future::join_all; use futures::future::join_all;
use log::{error, trace}; use log::{error, trace};
use num_traits::Zero; use num_traits::Zero;
@ -18,7 +17,10 @@ use tokio::{
}; };
use crate::{ use crate::{
chunk::{ChunkReadingError, ChunkWritingError, io::Dirtiable}, chunk::{
ChunkReadingError, ChunkWritingError,
io::{BoxFuture, Dirtiable},
},
level::LevelFolder, level::LevelFolder,
}; };
@ -149,7 +151,6 @@ impl<S: ChunkSerializer<WriteBackend = PathBuf>> ChunkFileManager<S> {
} }
} }
#[async_trait]
impl<P, S> FileIO for ChunkFileManager<S> impl<P, S> FileIO for ChunkFileManager<S>
where where
P: PathFromLevelFolder + Send + Sync + Sized + Dirtiable + 'static, P: PathFromLevelFolder + Send + Sync + Sized + Dirtiable + 'static,
@ -157,242 +158,276 @@ where
{ {
type Data = Arc<RwLock<S::Data>>; type Data = Arc<RwLock<S::Data>>;
async fn watch_chunks(&self, folder: &LevelFolder, chunks: &[Vector2<i32>]) { // Changed: Return BoxFuture<()>
// It is intentional that regions are watched multiple times (once per chunk) fn watch_chunks<'a>(
let mut watchers = self.watchers.write().await; &'a self,
for chunk in chunks { folder: &'a LevelFolder,
let key = S::get_chunk_key(chunk); chunks: &'a [Vector2<i32>],
let map_key = P::file_path(folder, &key); ) -> BoxFuture<'a, ()> {
match watchers.entry(map_key) { Box::pin(async move {
std::collections::btree_map::Entry::Vacant(vacant) => { // It is intentional that regions are watched multiple times (once per chunk)
let _ = vacant.insert(1); let mut watchers = self.watchers.write().await;
} for chunk in chunks {
std::collections::btree_map::Entry::Occupied(mut occupied) => { let key = S::get_chunk_key(chunk);
occupied.get_mut().add_assign(1); let map_key = P::file_path(folder, &key);
} match watchers.entry(map_key) {
} std::collections::btree_map::Entry::Vacant(vacant) => {
} let _ = vacant.insert(1);
} }
std::collections::btree_map::Entry::Occupied(mut occupied) => {
async fn unwatch_chunks(&self, folder: &LevelFolder, chunks: &[Vector2<i32>]) { occupied.get_mut().add_assign(1);
let mut watchers = self.watchers.write().await;
for chunk in chunks {
let key = S::get_chunk_key(chunk);
let map_key = P::file_path(folder, &key);
match watchers.entry(map_key) {
std::collections::btree_map::Entry::Vacant(_vacant) => {}
std::collections::btree_map::Entry::Occupied(mut occupied) => {
occupied.get_mut().sub_assign(1);
if occupied.get().is_zero() {
occupied.remove_entry();
} }
} }
} }
} })
} }
async fn clear_watched_chunks(&self) { // Changed: Return BoxFuture<()>
self.watchers.write().await.clear(); fn unwatch_chunks<'a>(
} &'a self,
folder: &'a LevelFolder,
async fn fetch_chunks( chunks: &'a [Vector2<i32>],
&self, ) -> BoxFuture<'a, ()> {
folder: &LevelFolder, Box::pin(async move {
chunk_coords: &[Vector2<i32>], let mut watchers = self.watchers.write().await;
stream: tokio::sync::mpsc::Sender<LoadedData<Self::Data, ChunkReadingError>>, for chunk in chunks {
) { let key = S::get_chunk_key(chunk);
let mut regions_chunks: BTreeMap<String, Vec<Vector2<i32>>> = BTreeMap::new(); let map_key = P::file_path(folder, &key);
match watchers.entry(map_key) {
for at in chunk_coords { std::collections::btree_map::Entry::Vacant(_vacant) => {}
let key = S::get_chunk_key(at); std::collections::btree_map::Entry::Occupied(mut occupied) => {
occupied.get_mut().sub_assign(1);
regions_chunks if occupied.get().is_zero() {
.entry(key) occupied.remove_entry();
.and_modify(|chunks| chunks.push(*at))
.or_insert(vec![*at]);
}
// 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 path = P::file_path(folder, &file_name);
let chunk_serializer = match self.get_serializer(&path).await {
Ok(chunk_serializer) => chunk_serializer,
Err(ChunkReadingError::ChunkNotExist) => {
unreachable!("Default Serializer must be created")
}
Err(err) => {
let _ = 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 intermediary = async {
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() {
// Stream is closed, so stop unneeded computation and io
return;
}
}
};
// We need to hold the read lock only for the duration of get_chunks
// This minimizes the time we block other operations
let reader = async move {
let serializer = chunk_serializer.read().await;
serializer.get_chunks(&chunks, send).await;
};
join!(intermediary, reader);
});
let _ = join_all(region_read_tasks).await;
}
async fn save_chunks(
&self,
folder: &LevelFolder,
chunks_data: Vec<(Vector2<i32>, Self::Data)>,
) -> Result<(), ChunkWritingError> {
let mut regions_chunks: BTreeMap<String, Vec<Self::Data>> = BTreeMap::new();
for (at, chunk) in chunks_data {
let key = S::get_chunk_key(&at);
match regions_chunks.entry(key) {
std::collections::btree_map::Entry::Occupied(mut occupied) => {
occupied.get_mut().push(chunk);
}
std::collections::btree_map::Entry::Vacant(vacant) => {
vacant.insert(vec![chunk]);
}
}
}
// we use a Sync Closure with an Async Block to execute the tasks in parallel
// with out waiting the future. Also it improve we File Cache utilizations.
let tasks = regions_chunks
.into_iter()
.map(async |(file_name, chunk_locks)| {
let path = P::file_path(folder, &file_name);
log::trace!("Updating data for file {}", path.display());
let chunk_serializer = match self.get_serializer(&path).await {
Ok(file) => Ok(file),
Err(ChunkReadingError::ChunkNotExist) => {
unreachable!("Must be managed by the cache")
}
Err(ChunkReadingError::IoError(err)) => {
error!("Error reading the data before write: {err}");
Err(ChunkWritingError::IoError(err))
}
Err(_) => {
Err(ChunkWritingError::IoError(std::io::ErrorKind::Other))
}
}?;
// Create a task for each chunk_lock
let update_tasks = chunk_locks.into_iter().map(|chunk_lock| {
let chunk_serializer = chunk_serializer.clone();
async move {
let mut chunk = chunk_lock.write().await;
let chunk_is_dirty = chunk.is_dirty();
// Edge case: this chunk is loaded while we were saving, mark it as cleaned since we are
// updating what we will write here
chunk.mark_dirty(false);
// It is important that we keep the lock after we mark the chunk as clean so no one else
// can modify it
let chunk = chunk.downgrade();
// We only need to update the chunk if it is dirty
if chunk_is_dirty {
chunk_serializer.write().await.update_chunk(&*chunk).await?;
} }
Ok::<(), ChunkWritingError>(())
} }
}); }
// Run all update tasks concurrently and propagate any error }
futures::future::try_join_all(update_tasks).await?; })
log::trace!("Updated data for file {}", path.display()); }
let is_watched = self // Changed: Return BoxFuture<()>
.watchers fn clear_watched_chunks(&self) -> BoxFuture<'_, ()> {
.read() Box::pin(async move {
.await self.watchers.write().await.clear();
.get(&path) })
.is_some_and(|count| !count.is_zero()); }
if !is_watched { // Changed: Return BoxFuture<()>
// With the modification done, we can drop the write lock but keep the read lock fn fetch_chunks<'a>(
// to avoid other threads to write/modify the data, but allow other threads to read it &'a self,
let serializer = chunk_serializer.read().await; 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();
log::debug!("Writing file for {}", path.display()); for at in chunk_coords {
serializer let key = S::get_chunk_key(at);
.write(path.clone())
.await
.map_err(|err| ChunkWritingError::IoError(err.kind()))?;
// Remove lock regions_chunks
drop(serializer); .entry(key)
// Decrement strong count .and_modify(|chunks| chunks.push(*at))
drop(chunk_serializer); .or_insert(vec![*at]);
}
// If there are still no watchers, drop from the locks // we use a Sync Closure with an Async Block to execute the tasks concurrently
let mut locks = self.file_locks.write().await; // Also improves File Cache utilizations.
let region_read_tasks = regions_chunks.into_iter().map(move |(file_name, chunks)| {
let task_stream = stream.clone();
if self async move {
let path = P::file_path(folder, &file_name);
let chunk_serializer = match self.get_serializer(&path).await {
Ok(chunk_serializer) => chunk_serializer,
Err(ChunkReadingError::ChunkNotExist) => {
unreachable!("Default Serializer must be created")
}
Err(err) => {
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 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 task_stream.send(wrapped_data).await.is_err() {
// Stream is closed, so stop unneeded computation and io
return;
}
}
};
// We need to hold the read lock only for the duration of get_chunks
// This minimizes the time we block other operations
let reader = async move {
let serializer = chunk_serializer.read().await;
serializer.get_chunks(&chunks, send).await;
};
join!(intermediary, reader);
}
});
let _ = join_all(region_read_tasks).await;
})
}
// Changed: Return BoxFuture<Result<(), ChunkWritingError>>
fn save_chunks<'a>(
&'a self,
folder: &'a LevelFolder,
chunks_data: Vec<(Vector2<i32>, Self::Data)>,
) -> 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 {
let key = S::get_chunk_key(&at);
match regions_chunks.entry(key) {
std::collections::btree_map::Entry::Occupied(mut occupied) => {
occupied.get_mut().push(chunk);
}
std::collections::btree_map::Entry::Vacant(vacant) => {
vacant.insert(vec![chunk]);
}
}
}
// we use a Sync Closure with an Async Block to execute the tasks in parallel
// with out waiting the future. Also it improve we File Cache utilizations.
let tasks = regions_chunks
.into_iter()
// 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());
let chunk_serializer = match self.get_serializer(&path).await {
Ok(file) => Ok(file),
Err(ChunkReadingError::ChunkNotExist) => {
unreachable!("Must be managed by the cache")
}
Err(ChunkReadingError::IoError(err)) => {
error!("Error reading the data before write: {err}");
Err(ChunkWritingError::IoError(err))
}
Err(_) => Err(ChunkWritingError::IoError(std::io::ErrorKind::Other)),
}?;
// Create a task for each chunk_lock
let update_tasks = chunk_locks.into_iter().map(|chunk_lock| {
let chunk_serializer = chunk_serializer.clone();
async move {
let mut chunk = chunk_lock.write().await;
let chunk_is_dirty = chunk.is_dirty();
// Edge case: this chunk is loaded while we were saving, mark it as cleaned since we are
// updating what we will write here
chunk.mark_dirty(false);
// It is important that we keep the lock after we mark the chunk as clean so no one else
// can modify it
let chunk = chunk.downgrade();
// We only need to update the chunk if it is dirty
if chunk_is_dirty {
chunk_serializer.write().await.update_chunk(&*chunk).await?;
}
Ok::<(), ChunkWritingError>(())
}
});
// Run all update tasks concurrently and propagate any error
futures::future::try_join_all(update_tasks).await?;
log::trace!("Updated data for file {}", path.display());
let is_watched = self
.watchers .watchers
.read() .read()
.await .await
.get(&path) .get(&path)
.is_none_or(|count| count.is_zero()) .is_some_and(|count| !count.is_zero());
{
let can_remove = if let Some(loader) = locks.get(&path) {
loader.can_remove().await
} else {
true
};
if can_remove { if !is_watched {
locks.remove(&path); // With the modification done, we can drop the write lock but keep the read lock
log::trace!("Removed lockfile cache {}", path.display()); // to avoid other threads to write/modify the data, but allow other threads to read it
} else { let serializer = chunk_serializer.read().await;
log::trace!("Wanted to remove lockfile cache {} but someone still holds a reference to it!", path.display());
log::debug!("Writing file for {}", path.display());
serializer
.write(path.clone())
.await
.map_err(|err| ChunkWritingError::IoError(err.kind()))?;
// Remove lock
drop(serializer);
// Decrement strong count
drop(chunk_serializer);
// If there are still no watchers, drop from the locks
let mut locks = self.file_locks.write().await;
if self
.watchers
.read()
.await
.get(&path)
.is_none_or(|count| count.is_zero())
{
let can_remove = if let Some(loader) = locks.get(&path) {
loader.can_remove().await
} else {
true
};
if can_remove {
locks.remove(&path);
log::trace!("Removed lockfile cache {}", path.display());
} else {
log::trace!("Wanted to remove lockfile cache {} but someone still holds a reference to it!", path.display());
}
} }
} }
}
Ok(()) Ok(())
}); });
//TODO: we need to handle the errors and return the result //TODO: we need to handle the errors and return the result
// files to save // files to save
let _test: Vec<Result<(), ChunkWritingError>> = join_all(tasks).await; let _test: Vec<Result<(), ChunkWritingError>> = join_all(tasks).await;
Ok(()) Ok(())
})
} }
async fn clean_up_log(&self) { // Changed: Return BoxFuture<()>
let locks = self.file_locks.read().await; fn clean_up_log(&self) -> BoxFuture<'_, ()> {
log::debug!("{} File locks remain in cache", locks.len()); 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<()>
//we need to block any other operation fn block_and_await_ongoing_tasks(&self) -> BoxFuture<'_, ()> {
let serializer_cache = self.file_locks.write().await; 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 // Acquire a write lock on all entries to verify they are complete
let tasks = serializer_cache let tasks = serializer_cache
.values() .values()
.map(async |serializer| serializer.can_remove()); .map(|serializer| serializer.can_remove());
// We need to wait to ensure that all the locks are acquired // We need to wait to ensure that all the locks are acquired
// so there is no **operation** ongoing // so there is no **operation** ongoing
let _ = join_all(tasks).await; 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 bytes::Bytes;
use pumpkin_util::math::vector2::Vector2; use pumpkin_util::math::vector2::Vector2;
@ -39,6 +38,8 @@ pub trait Dirtiable {
fn mark_dirty(&mut self, flag: bool); 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 /// Trait to handle the IO of chunks
/// for loading and saving chunks data /// for loading and saving chunks data
/// can be implemented for different types of IO /// 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 /// The `R` type is the type of the data that will be loaded/saved
/// like ChunkData or EntityData /// like ChunkData or EntityData
#[async_trait]
pub trait FileIO pub trait FileIO
where where
Self: Send + Sync, Self: Send + Sync,
@ -54,40 +54,47 @@ where
type Data: Send + Sync + Sized; type Data: Send + Sync + Sized;
/// Load the chunks data /// Load the chunks data
async fn fetch_chunks( fn fetch_chunks<'a>(
&self, &'a self,
folder: &LevelFolder, folder: &'a LevelFolder,
chunk_coords: &[Vector2<i32>], chunk_coords: &'a [Vector2<i32>],
stream: tokio::sync::mpsc::Sender<LoadedData<Self::Data, ChunkReadingError>>, stream: tokio::sync::mpsc::Sender<LoadedData<Self::Data, ChunkReadingError>>,
); ) -> BoxFuture<'a, ()>; // Returns BoxFuture<()>
/// Persist the chunks data /// Persist the chunks data
async fn save_chunks( fn save_chunks<'a>(
&self, &'a self,
folder: &LevelFolder, folder: &'a LevelFolder,
chunks_data: Vec<(Vector2<i32>, Self::Data)>, 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 /// 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 /// 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 /// 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 /// 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. /// 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 /// The `Data` type is the type of the data that will be updated or serialized/deserialized
/// like ChunkData or EntityData /// like ChunkData or EntityData
#[async_trait]
pub trait ChunkSerializer: Send + Sync + Default { pub trait ChunkSerializer: Send + Sync + Default {
type Data: Send + Sync + Sized + Dirtiable; type Data: Send + Sync + Sized + Dirtiable;
type WriteBackend; type WriteBackend;
@ -98,18 +105,24 @@ pub trait ChunkSerializer: Send + Sync + Default {
fn should_write(&self, is_watched: bool) -> bool; fn should_write(&self, is_watched: bool) -> bool;
/// Serialize the data to bytes. /// 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 /// Create a new instance from bytes
fn read(r: Bytes) -> Result<Self, ChunkReadingError>; fn read(r: Bytes) -> Result<Self, ChunkReadingError>;
/// Add the chunk data to the serializer /// 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 /// Get the chunks data from the serializer
async fn get_chunks( fn get_chunks(
&self, &self,
chunks: &[Vector2<i32>], chunks: &[Vector2<i32>],
stream: tokio::sync::mpsc::Sender<LoadedData<Self::Data, ChunkReadingError>>, stream: tokio::sync::mpsc::Sender<LoadedData<Self::Data, ChunkReadingError>>,
); ) -> impl Future<Output = ()> + Send;
} }

View File

@ -1,5 +1,4 @@
use crate::item::ItemStack; use crate::item::ItemStack;
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;
@ -11,57 +10,114 @@ use std::{
}; };
use tokio::sync::{Mutex, OwnedMutexGuard}; use tokio::sync::{Mutex, OwnedMutexGuard};
// Inventory.java pub type InventoryFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
#[async_trait]
pub trait Inventory: Send + Sync + Debug + Clearable { pub trait Inventory: Send + Sync + Debug + Clearable {
fn size(&self) -> usize; 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 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 {})
}
fn count<'a>(&'a self, item: &'a Item) -> InventoryFuture<'a, u8> {
Box::pin(async move {
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
})
}
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 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;
if !stack.is_empty() {
let mut item_compound = NbtCompound::new();
item_compound.put_byte("Slot", i as i8);
stack.write_item_stack(&mut item_compound);
slots.push(NbtTag::Compound(item_compound));
}
}
if !include_empty && slots.is_empty() {
return;
}
nbt.put("Items", NbtTag::List(slots));
})
}
// --- Synchronous Methods (No Change) ---
fn get_max_count_per_stack(&self) -> u8 { fn get_max_count_per_stack(&self) -> u8 {
99 99
} }
async fn set_stack(&self, slot: usize, stack: ItemStack);
fn mark_dirty(&self) {} fn mark_dirty(&self) {}
async fn write_data( fn read_data(&self, nbt: &NbtCompound, stacks: &[Arc<Mutex<ItemStack>>]) {
&self,
nbt: &mut pumpkin_nbt::compound::NbtCompound,
stacks: &[Arc<Mutex<ItemStack>>],
include_empty: bool,
) {
let mut slots = Vec::new();
for (i, item) in stacks.iter().enumerate() {
let stack = item.lock().await;
if !stack.is_empty() {
let mut item_compound = NbtCompound::new();
item_compound.put_byte("Slot", i as i8);
stack.write_item_stack(&mut item_compound);
slots.push(NbtTag::Compound(item_compound));
}
}
if !include_empty && slots.is_empty() {
return;
}
nbt.put("Items", NbtTag::List(slots));
}
fn read_data(
&self,
nbt: &pumpkin_nbt::compound::NbtCompound,
stacks: &[Arc<Mutex<ItemStack>>],
) {
if let Some(inventory_list) = nbt.get_list("Items") { if let Some(inventory_list) = nbt.get_list("Items") {
for tag in inventory_list { for tag in inventory_list {
if let Some(item_compound) = tag.extract_compound() if let Some(item_compound) = tag.extract_compound()
@ -71,7 +127,6 @@ pub trait Inventory: Send + Sync + Debug + Clearable {
if slot < stacks.len() if slot < stacks.len()
&& let Some(item_stack) = ItemStack::read_item_stack(item_compound) && 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; *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 { fn is_valid_slot_for(&self, _slot: usize, _stack: &ItemStack) -> bool {
true true
} }
@ -101,42 +147,6 @@ pub trait Inventory: Send + Sync + Debug + Clearable {
true 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; fn as_any(&self) -> &dyn Any;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,12 +7,14 @@ use crate::block::{
{BlockBehaviour, NormalUseArgs}, {BlockBehaviour, NormalUseArgs},
}; };
use crate::world::World; use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::block_properties::{BlockProperties, HopperFacing}; use pumpkin_data::block_properties::{BlockProperties, HopperFacing};
use pumpkin_data::{Block, BlockDirection}; use pumpkin_data::{Block, BlockDirection};
use pumpkin_inventory::generic_container_screen_handler::create_hopper; use pumpkin_inventory::generic_container_screen_handler::create_hopper;
use pumpkin_inventory::player::player_inventory::PlayerInventory; 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_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
@ -24,17 +26,20 @@ use tokio::sync::Mutex;
struct HopperBlockScreenFactory(Arc<dyn Inventory>); struct HopperBlockScreenFactory(Arc<dyn Inventory>);
#[async_trait]
impl ScreenHandlerFactory for HopperBlockScreenFactory { impl ScreenHandlerFactory for HopperBlockScreenFactory {
async fn create_screen_handler( fn create_screen_handler<'a>(
&self, &'a self,
sync_id: u8, sync_id: u8,
player_inventory: &Arc<PlayerInventory>, player_inventory: &'a Arc<PlayerInventory>,
_player: &dyn InventoryPlayer, _player: &'a dyn InventoryPlayer,
) -> Option<Arc<Mutex<dyn ScreenHandler>>> { ) -> BoxFuture<'a, Option<SharedScreenHandler>> {
Some(Arc::new(Mutex::new( Box::pin(async move {
create_hopper(sync_id, player_inventory, self.0.clone()).await, 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 { 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_data::block_properties::{BlockProperties, HorizontalFacing, Integer1To4};
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use crate::block::{BlockBehaviour, CanUpdateAtArgs}; use crate::block::{BlockBehaviour, BlockFuture, CanUpdateAtArgs};
use crate::block::{BlockIsReplacing, OnPlaceArgs}; use crate::block::{BlockIsReplacing, OnPlaceArgs};
pub trait SegmentProperties { pub trait SegmentProperties {
@ -43,7 +42,6 @@ impl_segment_properties!(
segment_amount segment_amount
); );
#[async_trait]
pub trait Segmented: BlockBehaviour { pub trait Segmented: BlockBehaviour {
type Properties: BlockProperties + SegmentProperties; 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> {
let current_props = Self::Properties::from_state_id(ctx.state_id, ctx.block); Box::pin(async move {
self.can_add_segment(&current_props) 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 { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
if let BlockIsReplacing::Itself(existing_state_id) = ctx.replacing { Box::pin(async move {
let mut props = Self::Properties::from_state_id(existing_state_id, ctx.block); 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) { if self.can_add_segment(&props) {
let current_amount = props.get_segment_amount(); let current_amount = props.get_segment_amount();
let next_amount = self.get_next_segment_amount(current_amount); let next_amount = self.get_next_segment_amount(current_amount);
props.set_segment_amount(next_amount); props.set_segment_amount(next_amount);
props.to_state_id(ctx.block) props.to_state_id(args.block)
} else {
existing_state_id
}
} else { } else {
existing_state_id // Set first segment orientation based on player direction
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(args.block)
} }
} 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);
props.set_segment_amount(Integer1To4::L1);
props.set_facing(self.get_facing_for_segment(player_facing, Integer1To4::L1));
props.to_state_id(ctx.block)
}
} }
} }

View File

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

View File

@ -5,12 +5,14 @@ use crate::block::{
registry::BlockActionResult, registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs}, {BlockBehaviour, NormalUseArgs},
}; };
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_inventory::generic_container_screen_handler::create_generic_9x3; use pumpkin_inventory::generic_container_screen_handler::create_generic_9x3;
use pumpkin_inventory::player::player_inventory::PlayerInventory; 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_util::text::TextComponent;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use pumpkin_world::block::entities::shulker_box::ShulkerBoxBlockEntity; use pumpkin_world::block::entities::shulker_box::ShulkerBoxBlockEntity;
@ -19,17 +21,19 @@ use tokio::sync::Mutex;
struct ShulkerBoxScreenFactory(Arc<dyn Inventory>); struct ShulkerBoxScreenFactory(Arc<dyn Inventory>);
#[async_trait]
impl ScreenHandlerFactory for ShulkerBoxScreenFactory { impl ScreenHandlerFactory for ShulkerBoxScreenFactory {
async fn create_screen_handler( fn create_screen_handler<'a>(
&self, &'a self,
sync_id: u8, sync_id: u8,
player_inventory: &Arc<PlayerInventory>, player_inventory: &'a Arc<PlayerInventory>,
_player: &dyn InventoryPlayer, _player: &'a dyn InventoryPlayer,
) -> Option<Arc<Mutex<dyn ScreenHandler>>> { ) -> BoxFuture<'a, Option<SharedScreenHandler>> {
Some(Arc::new(Mutex::new( Box::pin(async move {
create_generic_9x3(sync_id, player_inventory, self.0.clone()).await, 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 { fn get_display_name(&self) -> TextComponent {

View File

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

View File

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