1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
//! Inventory data structure storing item stacks.
use std::ops::Range;
use crate::item::ItemStack;
use crate::item;
/// An inventory handle is used to assists item insertion into inventory. It also record
/// stack indices that have changed and therefore allows selective events.
pub struct InventoryHandle<'a> {
inv: &'a mut [ItemStack],
changes: u64,
}
impl<'a> InventoryHandle<'a> {
/// Construct a new inventory handle to a slice of item stacks. This functions panics
/// if the given slice is bigger than 64 stacks.
pub fn new(inv: &'a mut [ItemStack]) -> Self {
assert!(inv.len() <= 64);
Self {
inv,
changes: 0,
}
}
/// Get the item stack at the given index.
#[inline]
pub fn get(&self, index: usize) -> ItemStack {
self.inv[index]
}
/// Set the item stack at the given index.
#[inline]
pub fn set(&mut self, index: usize, stack: ItemStack) {
if self.inv[index] != stack {
self.inv[index] = stack;
self.changes |= 1 << index;
}
}
/// Add an item to the inventory, starting by the first slots.
///
/// The given item stack is modified according to the amount of items actually added
/// to the inventory, its size will be set to zero if fully consumed.
pub fn push_front(&mut self, stack: &mut ItemStack) {
self.push(stack, 0..self.inv.len(), false);
}
/// Add an item to the inventory, starting from the last slots.
///
/// The given item stack is modified according to the amount of items actually added
/// to the inventory, its size will be set to zero if fully consumed.
pub fn push_back(&mut self, stack: &mut ItemStack) {
self.push(stack, 0..self.inv.len(), true);
}
/// Same as [`push_front`](Self::push_front), but this work in a slice of inventory.
pub fn push_front_in(&mut self, stack: &mut ItemStack, range: Range<usize>) {
self.push(stack, range, false);
}
/// Same as [`push_back`](Self::push_back), but this work in a slice of inventory.
pub fn push_back_in(&mut self, stack: &mut ItemStack, range: Range<usize>) {
self.push(stack, range, true);
}
/// Add an item to the inventory. The given item stack is modified according to the
/// amount of items actually added to the inventory, its size will be set to zero if
/// fully consumed.
fn push(&mut self, stack: &mut ItemStack, range: Range<usize>, back: bool) {
// Do nothing if stack size is 0 or the item is air.
if stack.is_empty() {
return;
}
let item = item::from_id(stack.id);
// Only accumulate of stack size is greater than 1.
if item.max_stack_size > 1 {
let mut range = range.clone();
while let Some(index) = if back { range.next_back() } else { range.next() } {
let slot = &mut self.inv[index];
// If the slot is of the same item and has space left in the stack size.
if slot.size != 0 && slot.id == stack.id && slot.damage == stack.damage && slot.size < item.max_stack_size {
let available = item.max_stack_size - slot.size;
let to_add = available.min(stack.size);
slot.size += to_add;
stack.size -= to_add;
// NOTE: We requires that size must be less than 64, so the index fit
// in the 64 bits of changes integer.
self.changes |= 1 << index;
if stack.size == 0 {
return;
}
}
}
}
// If we land here, some items are remaining to insert in the empty slots.
// We can also land here if the item has damage value. We search empty slots.
let mut range = range.clone();
while let Some(index) = if back { range.next_back() } else { range.next() } {
let slot = &mut self.inv[index];
if slot.is_empty() {
// We found an empty slot, insert the whole remaining stack size.
*slot = *stack;
stack.size = 0;
self.changes |= 1 << index;
return;
}
}
}
/// Test if the given item can be pushed in this inventory. If true is returned, a
/// call to `push_*` function is guaranteed to fully consume the stack.
pub fn can_push(&self, mut stack: ItemStack) -> bool {
// Do nothing if stack size is 0 or the item is air.
if stack.is_empty() {
return true;
}
let item = item::from_id(stack.id);
for slot in &self.inv[..] {
if slot.is_empty() {
return true;
} else if slot.size != 0 && slot.id == stack.id && slot.damage == stack.damage && slot.size < item.max_stack_size {
let available = item.max_stack_size - slot.size;
let to_add = available.min(stack.size);
stack.size -= to_add;
if stack.size == 0 {
return true;
}
}
}
false
}
/// Consume the equivalent of the given item stack, returning true if successful.
pub fn consume(&mut self, stack: ItemStack) -> bool {
for (index, slot) in self.inv.iter_mut().enumerate() {
if slot.id == stack.id && slot.damage == stack.damage && slot.size >= stack.size {
slot.size -= stack.size;
self.changes |= 1 << index;
return true;
}
}
false
}
/// Get an iterator for changes that happened in this inventory.
pub fn iter_changes(&self) -> ChangesIter {
ChangesIter {
changes: self.changes,
count: 0,
}
}
}
/// An iterator of changes that happened to an inventory.
pub struct ChangesIter {
changes: u64,
count: u8,
}
impl Iterator for ChangesIter {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
while self.count < 64 {
let ret = ((self.changes & 1) != 0).then_some(self.count as usize);
self.changes >>= 1;
self.count += 1;
if let Some(ret) = ret {
return Some(ret);
}
}
None
}
}