steel_core/player/player_inventory/
mod.rs1use std::sync::Arc;
4
5use text_components::TextComponent;
6
7use crate::{inventory::menu::Menu, player::Player, world::World};
8
9mod container;
10mod core;
11mod equipment;
12mod player_handlers;
13
14pub use container::InvalidHotbarSlot;
15pub(crate) use container::armor_equipment;
16pub use core::PlayerInventory;
17pub use equipment::EquipmentSwapResult;
18
19pub struct MenuOpenContext<'a> {
21 pub container_id: u8,
23 pub player: &'a Player,
25 pub world: &'a Arc<World>,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum Prediction {
32 Predicted,
34 ServerOnly,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40#[must_use]
41pub enum MenuRemovalStatus {
42 Complete,
44 Pending,
47}
48
49#[derive(Clone, Copy, PartialEq, Eq)]
50pub(super) enum MenuItemDisposition {
51 ReturnToInventory,
52 Drop,
53}
54
55impl MenuItemDisposition {
56 const fn combine(self, other: Self) -> Self {
57 if matches!(self, Self::Drop) || matches!(other, Self::Drop) {
58 Self::Drop
59 } else {
60 Self::ReturnToInventory
61 }
62 }
63}
64
65pub(super) struct OpenMenuState {
66 menu: Option<Menu>,
67 dispatch: Option<OpenMenuDispatch>,
68 terminal_removal: Option<TerminalMenuRemoval>,
69 active_open_operations: usize,
70}
71
72pub(super) struct PlayerInventorySyncState {
73 pending_slots: [bool; PlayerInventory::CONTAINER_SIZE],
74}
75
76impl PlayerInventorySyncState {
77 pub(super) const fn new() -> Self {
78 Self {
79 pending_slots: [false; PlayerInventory::CONTAINER_SIZE],
80 }
81 }
82
83 fn request(&mut self, slots: impl IntoIterator<Item = usize>) {
84 for slot in slots {
85 assert!(
86 slot < PlayerInventory::CONTAINER_SIZE,
87 "logical player inventory slot {slot} is out of bounds"
88 );
89 self.pending_slots[slot] = true;
90 }
91 }
92
93 fn take_ready(&mut self, overrides_player_slots: bool) -> Vec<usize> {
94 self.pending_slots
95 .iter_mut()
96 .enumerate()
97 .filter_map(|(slot, pending)| {
98 if !*pending || (overrides_player_slots && slot < PlayerInventory::INVENTORY_SIZE) {
99 return None;
100 }
101 *pending = false;
102 Some(slot)
103 })
104 .collect()
105 }
106}
107
108struct OpenMenuDispatch {
109 container_id: u8,
110 overrides_player_slots: bool,
111 actions: Vec<DeferredMenuAction>,
112}
113
114struct TerminalMenuRemoval {
115 disposition: MenuItemDisposition,
116 main_cleanup_complete: bool,
117 pending_cleanup_in_progress: bool,
118 pending_menus: Vec<Menu>,
119}
120
121enum DeferredMenuAction {
122 Close { send_packet: bool },
123 Open(Box<PendingMenuOpen>),
124 Install(Box<PreparedMenu>),
125}
126
127type MenuFactory = Box<dyn for<'a> FnOnce(MenuOpenContext<'a>) -> Menu + Send + 'static>;
128
129struct PendingMenuOpen {
130 title: TextComponent,
131 create: MenuFactory,
132}
133
134struct PreparedMenu {
135 title: TextComponent,
136 menu: Menu,
137}
138
139enum OpenMenuUnavailable {
140 Closed,
141 Unavailable,
142}
143
144impl OpenMenuState {
145 pub(super) const fn new() -> Self {
146 Self {
147 menu: None,
148 dispatch: None,
149 terminal_removal: None,
150 active_open_operations: 0,
151 }
152 }
153}
154
155#[cfg(test)]
156mod tests;