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)); + + } +}