Skip to main content

steel_core/inventory/
ender_chest.rs

1//! Ender chest container implementation.
2
3use std::sync::{Arc, Weak};
4
5use steel_registry::item_stack::ItemStack;
6use steel_utils::locks::SyncMutex;
7use steel_utils::{DowncastType, DowncastTypeKey};
8
9use crate::block_entity::BlockEntity;
10use crate::inventory::container::Container;
11use crate::player::Player;
12
13/// Number of slots in an ender chest (3 rows of 9).
14pub const ENDER_CHEST_SLOTS: usize = 27;
15
16type WeakBlockEntity = Weak<dyn BlockEntity>;
17
18/// Thread-safe reference to a player's ender chest container.
19pub type SyncPlayerEnderChest = Arc<SyncMutex<PlayerEnderChestContainer>>;
20
21/// The player's ender chest inventory.
22pub struct PlayerEnderChestContainer {
23    items: Vec<ItemStack>,
24    active_chest: Option<WeakBlockEntity>,
25}
26
27// SAFETY: This key is owned by Steel and uniquely identifies `PlayerEnderChestContainer`.
28unsafe impl DowncastType for PlayerEnderChestContainer {
29    const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:inventory/player_ender_chest");
30}
31
32impl Default for PlayerEnderChestContainer {
33    fn default() -> Self {
34        Self::new()
35    }
36}
37
38impl PlayerEnderChestContainer {
39    /// Creates a new, empty ender chest inventory.
40    #[must_use]
41    pub fn new() -> Self {
42        Self {
43            items: vec![ItemStack::empty(); ENDER_CHEST_SLOTS],
44            active_chest: None,
45        }
46    }
47
48    /// Sets the block entity this container was most recently opened from.
49    pub fn set_active_chest(&mut self, active_chest: WeakBlockEntity) {
50        self.active_chest = Some(active_chest);
51    }
52
53    /// Clears the active block entity.
54    pub fn clear_active_chest(&mut self) {
55        self.active_chest = None;
56    }
57
58    /// Checks if the container is still valid for the given player.
59    #[must_use]
60    pub fn still_valid(&self, player: &Player) -> bool {
61        let Some(weak_chest) = &self.active_chest else {
62            return true;
63        };
64        // A dropped weak handle means the chest was destroyed while open.
65        let Some(chest) = weak_chest.upgrade() else {
66            return false;
67        };
68        chest.base().is_valid_container_for(player)
69    }
70}
71
72impl Container for PlayerEnderChestContainer {
73    fn items(&self) -> &[ItemStack] {
74        &self.items
75    }
76
77    fn items_mut(&mut self) -> &mut [ItemStack] {
78        &mut self.items
79    }
80
81    fn set_changed(&mut self) {
82        // Player data saving handles change tracking for this inventory.
83    }
84}