diff --git a/src/main/java/net/minestom/server/inventory/AbstractInventory.java b/src/main/java/net/minestom/server/inventory/AbstractInventory.java
index 7885be34c..4f32dc1bf 100644
--- a/src/main/java/net/minestom/server/inventory/AbstractInventory.java
+++ b/src/main/java/net/minestom/server/inventory/AbstractInventory.java
@@ -52,13 +52,11 @@ public abstract class AbstractInventory implements InventoryClickHandler, DataCo
/**
* Adds an {@link ItemStack} to the inventory and sends relevant update to the viewer(s).
- *
- * Does nothing if the item cannot be fully added.
*
* @param itemStack the item to add
* @return true if the item has been successfully added, false otherwise
*/
- public synchronized boolean addItemStack(@NotNull ItemStack itemStack) {
+ public synchronized T addItemStack(@NotNull ItemStack itemStack, FillOption fillOption) {
Int2ObjectMap itemChangesMap = new Int2ObjectOpenHashMap<>();
final StackingRule stackingRule = itemStack.getStackingRule();
@@ -96,81 +94,35 @@ public abstract class AbstractInventory implements InventoryClickHandler, DataCo
break;
}
- if (itemStack.isAir()) {
- // Item can be fully placed inside the inventory, do so
- itemChangesMap.forEach(this::safeItemInsert);
- return true;
- } else {
- // Inventory cannot accept the item fully
- return false;
- }
+ return fillOption.fill(this, itemStack, itemChangesMap);
+ }
+
+ public synchronized boolean addItemStack(@NotNull ItemStack itemStack) {
+ return addItemStack(itemStack, FillOption.ALL_OR_NOTHING);
}
/**
* Adds {@link ItemStack}s to the inventory and sends relevant updates to the viewer(s).
- *
- * Does nothing if the item cannot be fully added.
*
* @param itemStacks items to add
- * @return list of items that could not be successfully added, empty list otherwise
+ * @return the operation results
*/
- public @NotNull List addItemStacks(@NotNull List itemStacks) {
- List result = new ArrayList<>();
+ public @NotNull List addItemStacks(@NotNull List itemStacks, @NotNull FillOption fillOption) {
+ List result = new ArrayList<>(itemStacks.size());
itemStacks.forEach(itemStack -> {
- if (!addItemStack(itemStack)) {
- result.add(itemStack);
- }
+ T fillResult = addItemStack(itemStack, fillOption);
+ result.add(fillResult);
});
return result;
}
- /**
- * Checks whether {@link ItemStack} can be fully added to the inventory.
- *
- * @param itemStack the item to be checked
- * @return true if the item can be fully added to the inventory, false otherwise
- */
- public boolean canAddItemStack(@NotNull ItemStack itemStack) {
- final StackingRule stackingRule = itemStack.getStackingRule();
- int amountLeft = stackingRule.getAmount(itemStack);
- for (int i = 0; i < getInnerSize(); i++) {
- ItemStack inventoryItem = getItemStack(i);
- if (stackingRule.canBeStacked(itemStack, inventoryItem)) {
- final int itemAmount = stackingRule.getAmount(inventoryItem);
- if (itemAmount == stackingRule.getMaxSize())
- continue;
- if (!stackingRule.canApply(itemStack, amountLeft + itemAmount)) {
- // Slot cannot accept the whole item, reduce amount to 'itemStack'
- amountLeft -= stackingRule.getMaxSize() - itemAmount;
- } else {
- return true;
- }
- } else if (inventoryItem.isAir()) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Checks whether {@link ItemStack}s can be fully added to the inventory.
- *
- * @param itemStacks items to be checked
- * @return true if all the items can be fully added to the inventory, false otherwise
- */
- public boolean canAddItemStacks(@NotNull List itemStacks) {
- return itemStacks.stream().allMatch(this::canAddItemStack);
- }
-
/**
* Takes an {@link ItemStack} from the inventory and sends relevant update to the viewer(s).
- *
- * Even the item cannot be fully taken, the amount of {@code itemStack} will be updated.
*
* @param itemStack the item to take
* @return true if the item has been successfully fully taken, false otherwise
*/
- public boolean takeItemStack(@NotNull ItemStack itemStack) {
+ public T takeItemStack(@NotNull ItemStack itemStack, @NotNull FillOption fillOption) {
Int2ObjectMap itemChangesMap = new Int2ObjectOpenHashMap<>();
final StackingRule stackingRule = itemStack.getStackingRule();
for (int i = 0; i < getInnerSize(); i++) {
@@ -195,68 +147,24 @@ public abstract class AbstractInventory implements InventoryClickHandler, DataCo
}
}
- if (itemStack.isAir()) {
- // Item can be fully taken from the inventory, do so
- itemChangesMap.forEach(this::safeItemInsert);
- return true;
- } else {
- return false;
- }
+ return fillOption.fill(this, itemStack, itemChangesMap);
}
/**
* Takes {@link ItemStack}s from the inventory and sends relevant updates to the viewer(s).
- *
- * Even items cannot be fully taken, the amount of {@code itemStack}s will be updated.
*
* @param itemStacks items to take
- * @return list of itemstacks that could not be successfully fully taken, empty list otherwise
+ * @return the operation results
*/
- public @NotNull List takeItemStacks(@NotNull List itemStacks) {
- List result = new ArrayList<>();
+ public @NotNull List takeItemStacks(@NotNull List itemStacks, @NotNull FillOption fillOption) {
+ List result = new ArrayList<>(itemStacks.size());
itemStacks.forEach(itemStack -> {
- if (!takeItemStack(itemStack)) {
- result.add(itemStack);
- }
+ T fillResult = takeItemStack(itemStack, fillOption);
+ result.add(fillResult);
});
return result;
}
- /**
- * Checks whether {@link ItemStack} can be fully taken from the inventory.
- *
- * @param itemStack the item to be checked
- * @return true if the item can be fully taken from the inventory, false otherwise
- */
- public boolean canTakeItemStack(@NotNull ItemStack itemStack) {
- final StackingRule stackingRule = itemStack.getStackingRule();
- int amountLeft = stackingRule.getAmount(itemStack);
- for (int i = 0; i < getInnerSize(); i++) {
- ItemStack inventoryItem = getItemStack(i);
- if (inventoryItem.isAir()) {
- continue;
- }
- if (stackingRule.canBeStacked(itemStack, inventoryItem)) {
- final int itemAmount = stackingRule.getAmount(inventoryItem);
- if (amountLeft <= itemAmount) {
- return true;
- }
- amountLeft -= itemAmount;
- }
- }
- return false;
- }
-
- /**
- * Checks whether {@link ItemStack}s can be fully taken from the inventory.
- *
- * @param itemStacks items to be checked
- * @return true if all the items can be fully taken from the inventory, false otherwise
- */
- public boolean canTakeItemStacks(@NotNull List itemStacks) {
- return itemStacks.stream().allMatch(this::canTakeItemStack);
- }
-
public synchronized void replaceItemStack(int slot, @NotNull UnaryOperator<@NotNull ItemStack> operator) {
var currentItem = getItemStack(slot);
setItemStack(slot, operator.apply(currentItem));
diff --git a/src/main/java/net/minestom/server/inventory/FillOption.java b/src/main/java/net/minestom/server/inventory/FillOption.java
new file mode 100644
index 000000000..3713c5f5e
--- /dev/null
+++ b/src/main/java/net/minestom/server/inventory/FillOption.java
@@ -0,0 +1,32 @@
+package net.minestom.server.inventory;
+
+import net.minestom.server.item.ItemStack;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Map;
+
+@FunctionalInterface
+public interface FillOption {
+
+ FillOption ALL = (inventory, result, itemChangesMap) -> {
+ itemChangesMap.forEach(inventory::safeItemInsert);
+ return result;
+ };
+
+ FillOption ALL_OR_NOTHING = (inventory, result, itemChangesMap) -> {
+ if (result.isAir()) {
+ // Item can be fully placed inside the inventory, do so
+ itemChangesMap.forEach(inventory::safeItemInsert);
+ return true;
+ } else {
+ // Inventory cannot accept the item fully
+ return false;
+ }
+ };
+
+ FillOption DRY_RUN = (inventory, result, itemChangesMap) -> !result.isAir();
+
+ T fill(@NotNull AbstractInventory inventory,
+ @NotNull ItemStack result,
+ @NotNull Map<@NotNull Integer, @NotNull ItemStack> itemChangesMap);
+}
diff --git a/src/test/java/demo/Main.java b/src/test/java/demo/Main.java
index c357c124d..e6886fcf3 100644
--- a/src/test/java/demo/Main.java
+++ b/src/test/java/demo/Main.java
@@ -49,6 +49,7 @@ public class Main {
commandManager.register(new EchoCommand());
commandManager.register(new SummonCommand());
commandManager.register(new RemoveCommand());
+ commandManager.register(new GiveCommand());
commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage(Component.text("Unknown command", NamedTextColor.RED)));
diff --git a/src/test/java/demo/commands/GiveCommand.java b/src/test/java/demo/commands/GiveCommand.java
new file mode 100644
index 000000000..73f747213
--- /dev/null
+++ b/src/test/java/demo/commands/GiveCommand.java
@@ -0,0 +1,56 @@
+package demo.commands;
+
+import net.kyori.adventure.text.Component;
+import net.minestom.server.command.builder.Command;
+import net.minestom.server.entity.Entity;
+import net.minestom.server.entity.Player;
+import net.minestom.server.inventory.FillOption;
+import net.minestom.server.item.ItemStack;
+import net.minestom.server.utils.entity.EntityFinder;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static net.minestom.server.command.builder.arguments.ArgumentType.Integer;
+import static net.minestom.server.command.builder.arguments.ArgumentType.*;
+
+public class GiveCommand extends Command {
+ public GiveCommand() {
+ super("give");
+
+ setDefaultExecutor((sender, context) ->
+ sender.sendMessage(Component.text("Usage: /give - []")));
+
+ addSyntax((sender, context) -> {
+ final EntityFinder entityFinder = context.get("target");
+ int count = context.get("count");
+ ItemStack itemStack = context.get("item");
+
+ List itemStacks;
+ if (count <= 64) {
+ itemStack = itemStack.withAmount(count);
+ itemStacks = Collections.singletonList(itemStack);
+ } else {
+ itemStacks = new ArrayList<>();
+ while (count > 64) {
+ itemStacks.add(itemStack.withAmount(64));
+ count -= 64;
+ }
+ itemStacks.add(itemStack.withAmount(count));
+ }
+
+ final List targets = entityFinder.find(sender);
+ for (Entity target : targets) {
+ if (target instanceof Player) {
+ Player player = (Player) target;
+ player.getInventory().addItemStacks(itemStacks, FillOption.ALL_OR_NOTHING);
+ }
+ }
+
+ sender.sendMessage(Component.text("Items have been given successfully!"));
+
+ }, Entity("target").onlyPlayers(true), ItemStack("item"), Integer("count").setDefaultValue(() -> 1));
+
+ }
+}