WIP FillOption

This commit is contained in:
TheMode 2021-04-11 23:43:35 +02:00
parent c7cd8c332a
commit d86a733b79
4 changed files with 107 additions and 110 deletions

View File

@ -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).
* <p>
* 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> T addItemStack(@NotNull ItemStack itemStack, FillOption<T> fillOption) {
Int2ObjectMap<ItemStack> 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).
* <p>
* 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<ItemStack> addItemStacks(@NotNull List<ItemStack> itemStacks) {
List<ItemStack> result = new ArrayList<>();
public <T> @NotNull List<T> addItemStacks(@NotNull List<ItemStack> itemStacks, @NotNull FillOption<T> fillOption) {
List<T> 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<ItemStack> itemStacks) {
return itemStacks.stream().allMatch(this::canAddItemStack);
}
/**
* Takes an {@link ItemStack} from the inventory and sends relevant update to the viewer(s).
* <p>
* 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> T takeItemStack(@NotNull ItemStack itemStack, @NotNull FillOption<T> fillOption) {
Int2ObjectMap<ItemStack> 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).
* <p>
* 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<ItemStack> takeItemStacks(@NotNull List<ItemStack> itemStacks) {
List<ItemStack> result = new ArrayList<>();
public <T> @NotNull List<T> takeItemStacks(@NotNull List<ItemStack> itemStacks, @NotNull FillOption<T> fillOption) {
List<T> 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<ItemStack> 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));

View File

@ -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<T> {
FillOption<ItemStack> ALL = (inventory, result, itemChangesMap) -> {
itemChangesMap.forEach(inventory::safeItemInsert);
return result;
};
FillOption<Boolean> 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<Boolean> DRY_RUN = (inventory, result, itemChangesMap) -> !result.isAir();
T fill(@NotNull AbstractInventory inventory,
@NotNull ItemStack result,
@NotNull Map<@NotNull Integer, @NotNull ItemStack> itemChangesMap);
}

View File

@ -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)));

View File

@ -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 <target> <item> [<count>]")));
addSyntax((sender, context) -> {
final EntityFinder entityFinder = context.get("target");
int count = context.get("count");
ItemStack itemStack = context.get("item");
List<ItemStack> 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<Entity> 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));
}
}