DREAMFIRE Docs ← Back to site
Loading...
Searching...
No Matches
DreamInventory.java
Go to the documentation of this file.
1/*
2 * MIT License
3 *
4 * Copyright (c) 2025 Dreamfire Studio
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24package com.dreamfirestudios.dreamcore.DreamInventory;
25
26import net.kyori.adventure.text.Component;
27import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
28import org.bukkit.entity.LivingEntity;
29import org.bukkit.entity.Player;
30import org.bukkit.inventory.Inventory;
31import org.bukkit.inventory.ItemStack;
32import org.bukkit.inventory.PlayerInventory;
33import org.bukkit.inventory.meta.Damageable;
34import org.bukkit.inventory.meta.ItemMeta;
35import org.jetbrains.annotations.NotNull;
36import org.jetbrains.annotations.Nullable;
37
38import java.util.ArrayList;
39import java.util.List;
40import java.util.Objects;
41import java.util.function.Predicate;
42
65public final class DreamInventory {
66
67 private DreamInventory() {}
68
70 public static final int SLOT_MAIN_HAND = -1;
71
73 public static final int SLOT_OFF_HAND = -2;
74
75 // ---------------------------------------------------------------------
76 // Scanning
77 // ---------------------------------------------------------------------
78
85 public static @NotNull List<ItemRef> scan(@Nullable LivingEntity entity) {
86 final List<ItemRef> out = new ArrayList<>();
87 if (entity == null || entity.getEquipment() == null) return out;
88
89 // Armor
90 final ItemStack[] armor = entity.getEquipment().getArmorContents();
91 for (int i = 0; i < armor.length; i++) {
92 final ItemStack s = armor[i];
93 if (s != null) out.add(new ItemRef(ItemLocation.ENTITY_ARMOR, i, s));
94 }
95
96 // Hands
97 final ItemStack main = entity.getEquipment().getItemInMainHand();
98 final ItemStack off = entity.getEquipment().getItemInOffHand();
99 if (main != null) out.add(new ItemRef(ItemLocation.ENTITY_MAIN_HAND, SLOT_MAIN_HAND, main));
100 if (off != null) out.add(new ItemRef(ItemLocation.ENTITY_OFF_HAND, SLOT_OFF_HAND, off));
101
102 return out;
103 }
104
111 public static @NotNull List<ItemRef> scan(@Nullable PlayerInventory inv) {
112 final List<ItemRef> out = new ArrayList<>();
113 if (inv == null) return out;
114
115 // Backpack
116 final ItemStack[] contents = inv.getContents();
117 for (int slot = 0; slot < contents.length; slot++) {
118 final ItemStack s = contents[slot];
119 if (s != null) out.add(new ItemRef(ItemLocation.ENTITY_INVENTORY, slot, s));
120 }
121
122 // Armor
123 final ItemStack[] armor = inv.getArmorContents();
124 for (int i = 0; i < armor.length; i++) {
125 final ItemStack s = armor[i];
126 if (s != null) out.add(new ItemRef(ItemLocation.ENTITY_ARMOR, i, s));
127 }
128
129 // Hands
130 final ItemStack main = inv.getItemInMainHand();
131 final ItemStack off = inv.getItemInOffHand();
132 if (main != null) out.add(new ItemRef(ItemLocation.ENTITY_MAIN_HAND, SLOT_MAIN_HAND, main));
133 if (off != null) out.add(new ItemRef(ItemLocation.ENTITY_OFF_HAND, SLOT_OFF_HAND, off));
134
135 return out;
136 }
137
138 // ---------------------------------------------------------------------
139 // Queries
140 // ---------------------------------------------------------------------
141
148 public static int totalCount(@NotNull PlayerInventory inv, @NotNull ItemStack probe) {
149 Objects.requireNonNull(inv, "inventory");
150 Objects.requireNonNull(probe, "itemStack");
151 int total = 0;
152 for (ItemStack s : inv.getContents()) {
153 if (s != null && s.isSimilar(probe)) total += s.getAmount();
154 }
155 return total;
156 }
157
164 public static boolean isBroken(@Nullable ItemStack item) {
165 if (item == null) return false;
166 final int max = item.getType().getMaxDurability();
167 if (max <= 0) return false;
168 final ItemMeta meta = item.getItemMeta();
169 if (!(meta instanceof Damageable dmg)) return false;
170 return dmg.getDamage() >= max;
171 }
172
180 public static @Nullable ItemStack findByDisplayName(@NotNull PlayerInventory inv, @NotNull String plainName) {
181 Objects.requireNonNull(inv, "inventory");
182 Objects.requireNonNull(plainName, "plainName");
183 final String target = plainName.trim();
184 for (ItemStack s : inv.getContents()) {
185 if (s == null) continue;
186 final ItemMeta meta = s.getItemMeta();
187 if (meta == null || !meta.hasDisplayName()) continue;
188 final Component name = meta.displayName();
189 if (name == null) continue;
190 final String plain = PlainTextComponentSerializer.plainText().serialize(name);
191 if (plain.equalsIgnoreCase(target)) return s;
192 }
193 return null;
194 }
195
202 public static @Nullable ItemStack findByDisplayName(@NotNull PlayerInventory inv, @NotNull Component nameComponent) {
203 final String plain = PlainTextComponentSerializer.plainText().serialize(nameComponent);
204 return findByDisplayName(inv, plain);
205 }
206
214 public static boolean hasAtLeast(@NotNull PlayerInventory inv, @NotNull ItemStack probe, int amount) {
215 return totalCount(inv, probe) >= Math.max(0, amount);
216 }
217
218 // ---------------------------------------------------------------------
219 // Mutations
220 // ---------------------------------------------------------------------
221
229 public static int removeAmount(@NotNull PlayerInventory inv, @NotNull ItemStack probe, int amount) {
230 Objects.requireNonNull(inv, "inventory");
231 Objects.requireNonNull(probe, "itemStack");
232 int toRemove = Math.max(0, amount);
233 if (toRemove == 0) return 0;
234
235 ItemStack[] contents = inv.getContents();
236 for (int slot = 0; slot < contents.length && toRemove > 0; slot++) {
237 ItemStack s = contents[slot];
238 if (s == null || !s.isSimilar(probe)) continue;
239
240 int take = Math.min(s.getAmount(), toRemove);
241 s.setAmount(s.getAmount() - take);
242 toRemove -= take;
243
244 if (s.getAmount() <= 0) contents[slot] = null;
245 }
246 inv.setContents(contents);
247 return amount - toRemove;
248 }
249
256 public static int removeAllSimilar(@NotNull PlayerInventory inv, @NotNull ItemStack probe) {
257 Objects.requireNonNull(inv, "inventory");
258 Objects.requireNonNull(probe, "itemStack");
259 int removed = 0;
260 ItemStack[] contents = inv.getContents();
261 for (int slot = 0; slot < contents.length; slot++) {
262 ItemStack s = contents[slot];
263 if (s != null && s.isSimilar(probe)) {
264 removed += s.getAmount();
265 contents[slot] = null;
266 }
267 }
268 inv.setContents(contents);
269 return removed;
270 }
271
278 public static boolean addItem(@NotNull PlayerInventory inv, @NotNull ItemStack stack) {
279 Objects.requireNonNull(inv, "inventory");
280 Objects.requireNonNull(stack, "itemStack");
281 return inv.addItem(stack).isEmpty();
282 }
283
291 public static void swap(@NotNull Inventory inv, int slot1, int slot2) {
292 Objects.requireNonNull(inv, "inventory");
293 final int size = inv.getSize();
294 if (slot1 < 0 || slot1 >= size || slot2 < 0 || slot2 >= size) {
295 throw new IllegalArgumentException("Invalid slot(s): " + slot1 + ", " + slot2 + " (size=" + size + ")");
296 }
297 ItemStack a = inv.getItem(slot1);
298 ItemStack b = inv.getItem(slot2);
299 inv.setItem(slot1, b);
300 inv.setItem(slot2, a);
301 }
302
308 public static void ensureMainHand(@NotNull Player player, @NotNull ItemStack stack) {
309 Objects.requireNonNull(player, "player");
310 Objects.requireNonNull(stack, "itemStack");
311 ItemStack current = player.getInventory().getItemInMainHand();
312 if (current != null && current.isSimilar(stack)) return;
313 player.getInventory().setItemInMainHand(stack);
314 }
315
316 // ---------------------------------------------------------------------
317 // Search helpers
318 // ---------------------------------------------------------------------
319
332 public static @Nullable ItemStack findFirst(@NotNull PlayerInventory inv, @NotNull Predicate<ItemStack> predicate) {
333 Objects.requireNonNull(inv, "inventory");
334 Objects.requireNonNull(predicate, "predicate");
335 for (ItemStack s : inv.getContents()) {
336 if (s != null && predicate.test(s)) return s;
337 }
338 return null;
339 }
340}
Utility class providing safe and Paper-friendly inventory manipulation helpers.
static int totalCount(@NotNull PlayerInventory inv, @NotNull ItemStack probe)
Counts total items similar to the probe across a player’s inventory.
static void swap(@NotNull Inventory inv, int slot1, int slot2)
Swaps two slots in an inventory.
static int removeAmount(@NotNull PlayerInventory inv, @NotNull ItemStack probe, int amount)
Removes up to a certain amount of matching items from the inventory.
static int removeAllSimilar(@NotNull PlayerInventory inv, @NotNull ItemStack probe)
Removes all stacks similar to the probe.
static boolean hasAtLeast(@NotNull PlayerInventory inv, @NotNull ItemStack probe, int amount)
Checks if the inventory has at least amount of the probe item.
static List< ItemRef > scan(@Nullable LivingEntity entity)
Scans a living entity’s equipment (armor + hands) and returns logical item references.
static ItemStack findByDisplayName(@NotNull PlayerInventory inv, @NotNull String plainName)
Finds the first item whose display name matches the given plain text.
static boolean addItem(@NotNull PlayerInventory inv, @NotNull ItemStack stack)
Attempts to add an item to inventory.
static ItemStack findFirst(@NotNull PlayerInventory inv, @NotNull Predicate< ItemStack > predicate)
Finds the first item in an inventory matching a predicate.
static final int SLOT_MAIN_HAND
Reserved slot index for main hand references in ItemRef.
static boolean isBroken(@Nullable ItemStack item)
Determines whether the item is fully broken (at max durability).
static void ensureMainHand(@NotNull Player player, @NotNull ItemStack stack)
Ensures the player’s main hand contains the given stack (if not already similar).
static ItemStack findByDisplayName(@NotNull PlayerInventory inv, @NotNull Component nameComponent)
Finds the first item matching the given component display name.
static final int SLOT_OFF_HAND
Reserved slot index for off hand references in ItemRef.
static List< ItemRef > scan(@Nullable PlayerInventory inv)
Scans a player’s full inventory (backpack + armor + hands).
Logical locations for items when scanning entities or players.
ENTITY_OFF_HAND
Entity’s off hand (slot = DreamInventory.SLOT_OFF_HAND).
ENTITY_ARMOR
Any armor slot (see slot index for which).
ENTITY_MAIN_HAND
Entity’s main hand (slot = DreamInventory.SLOT_MAIN_HAND).
record ItemRef(ItemLocation location, int slot, ItemStack stack)
Immutable reference to an item found during an inventory scan.
Definition ItemRef.java:44