Skip to content

Commit

Permalink
Refactor global inventories system
Browse files Browse the repository at this point in the history
  • Loading branch information
Bryntet committed Dec 28, 2024
1 parent d6e1811 commit 6fa9cd9
Show file tree
Hide file tree
Showing 17 changed files with 309 additions and 353 deletions.
2 changes: 1 addition & 1 deletion pumpkin-core/src/math/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::math::vector2::Vector2;
use num_traits::Euclid;
use serde::{Deserialize, Serialize};

#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
/// Aka Block Position
pub struct WorldPosition(pub Vector3<i32>);

Expand Down
2 changes: 2 additions & 0 deletions pumpkin-inventory/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ thiserror.workspace = true

num-traits.workspace = true
num-derive.workspace = true
rand = "0.8.5"
uuid = { version = "1.11.0", features = ["v4"] }
53 changes: 49 additions & 4 deletions pumpkin-inventory/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,18 @@ pub trait Container: Sync + Send {

fn all_slots_ref(&self) -> Vec<Option<&ItemStack>>;

fn clear_all_slots(&mut self) {
let all_slots = self.all_slots();
for stack in all_slots {
*stack = None;
fn destroy_container(
&mut self,
player_inventory: &mut PlayerInventory,
carried_item: &mut Option<ItemStack>,
unique: bool,
) {
if unique {
self.combine_container_with_player_inventory(player_inventory)
}
// TODO: Add drop for these remaining things
self.all_slots().into_iter().for_each(|slot| *slot = None);
*carried_item = None;
}

fn all_combinable_slots(&self) -> Vec<Option<&ItemStack>> {
Expand Down Expand Up @@ -132,6 +139,44 @@ pub trait Container: Sync + Send {
}

fn recipe_used(&mut self) {}

fn combine_container_with_player_inventory(&mut self, player_inventory: &mut PlayerInventory) {
let slots = self
.all_slots()
.into_iter()
.filter_map(|slot| {
if let Some(stack) = slot {
let stack = *stack;
Some((slot, stack))
} else {
None
}
})
.collect::<Vec<_>>();
let mut receiving_slots = player_inventory
.slots_with_hotbar_first()
.collect::<Vec<_>>();

for (slot, stack) in slots {
let matches = receiving_slots.iter_mut().filter_map(|slot| {
if let Some(receiving_stack) = slot {
if receiving_stack.item_id == stack.item_id {
Some(receiving_stack)
} else {
None
}
} else {
None
}
});
for receiving_slot in matches {
if slot.is_none() {
break;
}
combine_stacks(slot, receiving_slot, MouseClick::Left);
}
}
}
}

pub struct EmptyContainer;
Expand Down
142 changes: 108 additions & 34 deletions pumpkin-inventory/src/open_container.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,111 @@
use crate::crafting::check_if_matches_crafting;
use crate::player::PlayerInventory;
use crate::{Container, WindowType};
use pumpkin_core::math::position::WorldPosition;
use pumpkin_world::block::block_registry::Block;
use pumpkin_world::item::ItemStack;
use rand::random;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;
use uuid::Uuid;

#[derive(Default)]
pub struct ContainerHolder {
pub containers_by_id: HashMap<usize, OpenContainer>,
pub location_to_container_id: HashMap<WorldPosition, usize>,
}

impl ContainerHolder {
pub async fn destroy(
&mut self,
id: usize,
player_inventory: &mut PlayerInventory,
carried_item: &mut Option<ItemStack>,
) -> Vec<Uuid> {
if let Some(container) = self.containers_by_id.remove(&id) {
let unique = container.unique;
let players = container.players;
let mut container = container.container.lock().await;
container.destroy_container(player_inventory, carried_item, unique);
players
} else {
vec![]
}
}

pub async fn destroy_by_location(
&mut self,
location: &WorldPosition,
player_inventory: &mut PlayerInventory,
carried_item: &mut Option<ItemStack>,
) -> Vec<Uuid> {
if let Some(id) = self.location_to_container_id.remove(location) {
self.destroy(id, player_inventory, carried_item).await
} else {
vec![]
}
}

pub fn get_by_location(&self, location: &WorldPosition) -> Option<&OpenContainer> {
self.containers_by_id
.get(self.location_to_container_id.get(location)?)
}

pub fn get_mut_by_location(&mut self, location: &WorldPosition) -> Option<&mut OpenContainer> {
self.containers_by_id
.get_mut(self.location_to_container_id.get(location)?)
}

pub fn new_by_location<C: Container + Default + 'static>(
&mut self,
location: WorldPosition,
block: Option<Block>,
) -> Option<&mut OpenContainer> {
if self.location_to_container_id.contains_key(&location) {
return None;
}
let id = self.new_container::<C>(block, false);
self.location_to_container_id.insert(location, id);
self.containers_by_id.get_mut(&id)
}

pub fn new_container<C: Container + Default + 'static>(
&mut self,
block: Option<Block>,
unique: bool,
) -> usize {
let mut id: usize = random();
let mut new_container = OpenContainer::new_empty_container::<C>(block, unique);
while let Some(container) = self.containers_by_id.insert(id, new_container) {
new_container = container;
id = random();
}
id
}

pub fn new_unique<C: Container + Default + 'static>(
&mut self,
block: Option<Block>,
player_id: Uuid,
) -> usize {
let id = self.new_container::<C>(block, true);
let container = self.containers_by_id.get_mut(&id).expect("just created it");
container.players.push(player_id);
id
}
}

pub struct OpenContainer {
// TODO: unique id should be here
// TODO: should this be uuid?
players: Vec<i32>,
container: Arc<Mutex<Box<dyn Container>>>,
location: Option<WorldPosition>,
pub unique: bool,
block: Option<Block>,
pub id: usize,
container: Arc<Mutex<Box<dyn Container>>>,
players: Vec<Uuid>,
}

impl OpenContainer {
pub fn try_open(&self, player_id: i32) -> Option<&Arc<Mutex<Box<dyn Container>>>> {
pub fn try_open(&self, player_id: Uuid) -> Option<&Arc<Mutex<Box<dyn Container>>>> {
if !self.players.contains(&player_id) {
log::debug!("couldn't open container");
return None;
Expand All @@ -25,13 +114,13 @@ impl OpenContainer {
Some(container)
}

pub fn add_player(&mut self, player_id: i32) {
pub fn add_player(&mut self, player_id: Uuid) {
if !self.players.contains(&player_id) {
self.players.push(player_id);
}
}

pub fn remove_player(&mut self, player_id: i32) {
pub fn remove_player(&mut self, player_id: Uuid) {
if let Some(index) = self.players.iter().enumerate().find_map(|(index, id)| {
if *id == player_id {
Some(index)
Expand All @@ -44,49 +133,34 @@ impl OpenContainer {
}

pub fn new_empty_container<C: Container + Default + 'static>(
player_id: i32,
location: Option<WorldPosition>,
block: Option<Block>,
unique: bool,
) -> Self {
Self {
players: vec![player_id],
unique,
players: vec![],
container: Arc::new(Mutex::new(Box::new(C::default()))),
location,
block,
id: 0,
}
}

pub fn is_location(&self, try_position: WorldPosition) -> bool {
if let Some(location) = self.location {
location == try_position
} else {
false
}
}

pub async fn clear_all_slots(&self) {
self.container.lock().await.clear_all_slots();
}

pub fn clear_all_players(&mut self) {
self.players = vec![];
}

pub fn all_player_ids(&self) -> Vec<i32> {
self.players.clone()
}

pub fn get_location(&self) -> Option<WorldPosition> {
self.location
}

pub async fn set_location(&mut self, location: Option<WorldPosition>) {
self.location = location;
pub fn all_player_ids(&self) -> &[Uuid] {
&self.players
}

pub fn get_block(&self) -> Option<Block> {
self.block.clone()
}

pub async fn window_type(&self) -> &'static WindowType {
let container = self.container.lock().await;
container.window_type()
}
}
#[derive(Default)]
pub struct Chest([Option<ItemStack>; 27]);
Expand Down
6 changes: 1 addition & 5 deletions pumpkin/src/block/block_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock};
use crate::entity::player::Player;
use crate::server::Server;
use pumpkin_core::math::position::WorldPosition;
use pumpkin_inventory::OpenContainer;
use pumpkin_world::block::block_registry::Block;
use pumpkin_world::item::item_registry::Item;
use std::collections::HashMap;
Expand Down Expand Up @@ -91,13 +90,10 @@ impl BlockManager {
player: &Player,
location: WorldPosition,
server: &Server,
container: &mut OpenContainer,
) {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
pumpkin_block
.on_close(block, player, location, server, container)
.await;
pumpkin_block.on_close(player, location, server).await;
}
}

Expand Down
53 changes: 14 additions & 39 deletions pumpkin/src/block/blocks/chest.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use async_trait::async_trait;
use pumpkin_core::math::position::WorldPosition;
use pumpkin_inventory::{Chest, OpenContainer, WindowType};
use pumpkin_inventory::Chest;
use pumpkin_macros::{pumpkin_block, sound};
use pumpkin_world::{block::block_registry::Block, item::item_registry::Item};

use crate::block::container::ContainerBlock;
use crate::{
block::{block_manager::BlockActionResult, pumpkin_block::PumpkinBlock},
entity::player::Player,
Expand All @@ -19,73 +20,47 @@ impl PumpkinBlock for ChestBlock {
&self,
block: &Block,
player: &Player,
_location: WorldPosition,
location: WorldPosition,
server: &Server,
) {
self.open_chest_block(block, player, _location, server)
.await;
self.open(block, player, location, server).await;
player
.world()
.play_block_sound(sound!("block.chest.open"), _location)
.play_block_sound(sound!("block.chest.open"), location)
.await;
}

async fn on_use_with_item<'a>(
&self,
block: &Block,
player: &Player,
_location: WorldPosition,
location: WorldPosition,
_item: &Item,
server: &Server,
) -> BlockActionResult {
self.open_chest_block(block, player, _location, server)
.await;
self.open(block, player, location, server).await;
BlockActionResult::Consume
}

async fn on_broken<'a>(
&self,
block: &Block,
_block: &Block,
player: &Player,
location: WorldPosition,
server: &Server,
) {
super::standard_on_broken_with_container(block, player, location, server).await;
self.destroy(location, server, player).await;
}

async fn on_close<'a>(
&self,
_block: &Block,
player: &Player,
_location: WorldPosition,
_server: &Server,
_container: &OpenContainer,
) {
async fn on_close<'a>(&self, player: &Player, location: WorldPosition, server: &Server) {
self.close(location, server, player).await;
player
.world()
.play_block_sound(sound!("block.chest.close"), _location)
.play_block_sound(sound!("block.chest.close"), location)
.await;
// TODO: send entity updates close
}
}

impl ChestBlock {
pub async fn open_chest_block(
&self,
block: &Block,
player: &Player,
location: WorldPosition,
server: &Server,
) {
// TODO: shouldn't Chest and window type be constrained together to avoid errors?
super::standard_open_container::<Chest>(
block,
player,
location,
server,
WindowType::Generic9x3,
)
.await;
// TODO: send entity updates open
}
impl ContainerBlock<Chest> for ChestBlock {
const UNIQUE: bool = false;
}
Loading

0 comments on commit 6fa9cd9

Please sign in to comment.