Simplify shift click handling, fix click processor ignoring non-air slot

This commit is contained in:
TheMode 2021-04-20 06:41:55 +02:00
parent 1daaeda63f
commit e9b5779b24
4 changed files with 90 additions and 122 deletions

View File

@ -338,51 +338,21 @@ public class Inventory extends AbstractInventory implements Viewable {
final ItemStack clicked = isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot);
final ItemStack cursor = getCursorItem(player); // Isn't used in the algorithm
final InventoryClickResult clickResult;
if (isInWindow) {
clickResult = clickProcessor.shiftClick(this, player, slot, clicked, cursor,
// Player inventory loop
new InventoryClickLoopHandler(offset, PlayerInventory.INVENTORY_SIZE + offset, 1,
PlayerInventoryUtils::convertToPacketSlot,
index -> isClickInWindow(index) ?
getItemStack(index) :
playerInventory.getItemStack(PlayerInventoryUtils.convertSlot(index, offset)),
(index, itemStack) -> {
if (isClickInWindow(index)) {
setItemStack(index, itemStack);
} else {
playerInventory.setItemStack(PlayerInventoryUtils.convertSlot(index, offset), itemStack);
}
}));
} else {
clickResult = clickProcessor.shiftClick(null, player, slot, clicked, cursor,
// Window loop
new InventoryClickLoopHandler(0, getSize(), 1,
i -> i,
index -> isClickInWindow(index) ?
getItemStack(index) :
playerInventory.getItemStack(PlayerInventoryUtils.convertSlot(index, offset)),
(index, itemStack) -> {
if (isClickInWindow(index)) {
setItemStack(index, itemStack);
} else {
playerInventory.setItemStack(PlayerInventoryUtils.convertSlot(index, offset), itemStack);
}
}));
}
final InventoryClickResult clickResult = clickProcessor.shiftClick(
isInWindow ? playerInventory : this,
isInWindow ? this : null,
player, slot, clicked, cursor);
if (clickResult == null)
return false;
if (clickResult.doRefresh()) {
updateFromClick(clickResult, player);
if (isInWindow) {
setItemStack(slot, clickResult.getClicked());
} else {
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
}
refreshPlayerCursorItem(player, clickResult.getCursor());
playerInventory.update();
update();
return !clickResult.isCancel();
}

View File

@ -339,26 +339,17 @@ public class PlayerInventory extends AbstractInventory implements EquipmentHandl
public boolean shiftClick(@NotNull Player player, int slot) {
final ItemStack cursor = getCursorItem();
final ItemStack clicked = getItemStack(slot, OFFSET);
final boolean hotBarClick = convertToPacketSlot(slot) < 9;
final InventoryClickResult clickResult = clickProcessor.shiftClick(null, player, slot, clicked, cursor,
new InventoryClickLoopHandler(0, itemStacks.length, 1,
i -> {
if (hotBarClick) {
return i < 9 ? i + 9 : i - 9;
} else {
return convertPlayerInventorySlot(i, OFFSET);
}
},
index -> getItemStack(index, OFFSET),
(index, itemStack) -> setItemStack(index, OFFSET, itemStack)));
final boolean hotBarClick = convertSlot(slot, OFFSET) < 9;
final int start = hotBarClick ? 9 : 0;
final int end = hotBarClick ? getSize() - 9 : 8;
final InventoryClickResult clickResult = clickProcessor.shiftClick(this,
start, end, 1,
player, slot, clicked, cursor);
if (clickResult == null)
return false;
if (clickResult.doRefresh())
update();
setItemStack(slot, OFFSET, clickResult.getClicked());
setCursorItem(clickResult.getCursor());
return !clickResult.isCancel();

View File

@ -19,13 +19,13 @@ public interface TransactionType {
* Adds an item to the inventory.
* Can either take an air slot or be stacked.
*/
TransactionType ADD = (inventory, itemStack) -> {
TransactionType ADD = (inventory, itemStack, slotPredicate, start, end, step) -> {
Int2ObjectMap<ItemStack> itemChangesMap = new Int2ObjectOpenHashMap<>();
final StackingRule stackingRule = itemStack.getStackingRule();
// Check filled slot (not air)
for (int i = 0; i < inventory.getInnerSize(); i++) {
for (int i = start; i < end; i += step) {
ItemStack inventoryItem = inventory.getItemStack(i);
if (inventoryItem.isAir()) {
continue;
@ -34,6 +34,12 @@ public interface TransactionType {
final int itemAmount = stackingRule.getAmount(inventoryItem);
if (itemAmount == stackingRule.getMaxSize())
continue;
if (!slotPredicate.test(i, inventoryItem)) {
// Cancelled transaction
continue;
}
final int itemStackAmount = stackingRule.getAmount(itemStack);
final int totalAmount = itemStackAmount + itemAmount;
if (!stackingRule.canApply(itemStack, totalAmount)) {
@ -50,11 +56,17 @@ public interface TransactionType {
}
// Check air slot to fill
for (int i = 0; i < inventory.getInnerSize(); i++) {
for (int i = start; i < end; i += step) {
ItemStack inventoryItem = inventory.getItemStack(i);
if (!inventoryItem.isAir()) {
continue;
}
if (!slotPredicate.test(i, inventoryItem)) {
// Cancelled transaction
continue;
}
// Fill the slot
itemChangesMap.put(i, itemStack);
itemStack = stackingRule.apply(itemStack, 0);
@ -68,15 +80,20 @@ public interface TransactionType {
* Takes an item from the inventory.
* Can either transform items to air or reduce their amount.
*/
TransactionType TAKE = (inventory, itemStack) -> {
TransactionType TAKE = (inventory, itemStack, slotPredicate, start, end, step) -> {
Int2ObjectMap<ItemStack> itemChangesMap = new Int2ObjectOpenHashMap<>();
final StackingRule stackingRule = itemStack.getStackingRule();
for (int i = 0; i < inventory.getInnerSize(); i++) {
for (int i = start; i < end; i += step) {
ItemStack inventoryItem = inventory.getItemStack(i);
if (inventoryItem.isAir()) {
continue;
}
if (stackingRule.canBeStacked(itemStack, inventoryItem)) {
if (!slotPredicate.test(i, inventoryItem)) {
// Cancelled transaction
continue;
}
final int itemAmount = stackingRule.getAmount(inventoryItem);
final int itemStackAmount = stackingRule.getAmount(itemStack);
if (itemStackAmount < itemAmount) {
@ -97,6 +114,23 @@ public interface TransactionType {
};
@NotNull Pair<ItemStack, Map<Integer, ItemStack>> process(@NotNull AbstractInventory inventory,
@NotNull ItemStack itemStack);
@NotNull ItemStack itemStack,
@NotNull SlotPredicate slotPredicate,
int start, int end, int step);
default @NotNull Pair<ItemStack, Map<Integer, ItemStack>> process(@NotNull AbstractInventory inventory,
@NotNull ItemStack itemStack,
@NotNull SlotPredicate slotPredicate) {
return process(inventory, itemStack, slotPredicate, 0, inventory.getInnerSize(), 1);
}
default @NotNull Pair<ItemStack, Map<Integer, ItemStack>> process(@NotNull AbstractInventory inventory,
@NotNull ItemStack itemStack) {
return process(inventory, itemStack, (slot, itemStack1) -> true);
}
@FunctionalInterface
interface SlotPredicate {
boolean test(int slot, @NotNull ItemStack itemStack);
}
}

View File

@ -7,7 +7,9 @@ import it.unimi.dsi.fastutil.ints.IntSet;
import net.minestom.server.entity.Player;
import net.minestom.server.event.inventory.InventoryClickEvent;
import net.minestom.server.event.inventory.InventoryPreClickEvent;
import net.minestom.server.inventory.AbstractInventory;
import net.minestom.server.inventory.Inventory;
import net.minestom.server.inventory.TransactionType;
import net.minestom.server.inventory.condition.InventoryCondition;
import net.minestom.server.inventory.condition.InventoryConditionResult;
import net.minestom.server.item.ItemStack;
@ -167,9 +169,8 @@ public class InventoryClickProcessor {
return clickResult;
}
@Nullable
public InventoryClickResult shiftClick(@Nullable Inventory inventory, @NotNull Player player, int slot,
@NotNull ItemStack clicked, @NotNull ItemStack cursor, @NotNull InventoryClickLoopHandler... loopHandlers) {
public @Nullable InventoryClickResult shiftClick(AbstractInventory targetInventory, @Nullable Inventory inventory, @NotNull Player player, int slot,
@NotNull ItemStack clicked, @NotNull ItemStack cursor) {
InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.START_SHIFT_CLICK, clicked, cursor);
if (clickResult.isCancel()) {
@ -179,72 +180,44 @@ public class InventoryClickProcessor {
if (clicked.isAir())
return null;
final StackingRule clickedRule = clicked.getStackingRule();
var pair = TransactionType.ADD.process(targetInventory, clicked, (index, itemStack) -> {
InventoryClickResult result = startCondition(inventory, player, index, ClickType.SHIFT_CLICK, itemStack, cursor);
return !result.isCancel();
});
boolean filled = false;
ItemStack resultClicked = clicked;
var itemResult = pair.left();
var map = pair.right();
map.forEach(targetInventory::setItemStack);
for (InventoryClickLoopHandler loopHandler : loopHandlers) {
final Int2IntFunction indexModifier = loopHandler.getIndexModifier();
final Int2ObjectFunction<ItemStack> itemGetter = loopHandler.getItemGetter();
final BiConsumer<Integer, ItemStack> itemSetter = loopHandler.getItemSetter();
clickResult.setClicked(itemResult);
for (int i = loopHandler.getStart(); i < loopHandler.getEnd(); i += loopHandler.getStep()) {
final int index = indexModifier.apply(i);
if (index == slot)
continue;
return clickResult;
}
ItemStack item = itemGetter.apply(index);
final StackingRule itemRule = item.getStackingRule();
if (itemRule.canBeStacked(item, clicked)) {
public @Nullable InventoryClickResult shiftClick(@NotNull AbstractInventory targetInventory, int start, int end, int step, @NotNull Player player, int slot,
@NotNull ItemStack clicked, @NotNull ItemStack cursor) {
InventoryClickResult clickResult = startCondition(null, player, slot, ClickType.START_SHIFT_CLICK, clicked, cursor);
clickResult = startCondition(inventory, player, index, ClickType.SHIFT_CLICK, item, cursor);
if (clickResult.isCancel())
break;
final int amount = itemRule.getAmount(item);
if (!clickedRule.canApply(clicked, amount + 1))
continue;
final int totalAmount = clickedRule.getAmount(resultClicked) + amount;
if (!clickedRule.canApply(clicked, totalAmount)) {
item = itemRule.apply(item, itemRule.getMaxSize());
itemSetter.accept(index, item);
resultClicked = clickedRule.apply(resultClicked, totalAmount - clickedRule.getMaxSize());
filled = false;
callClickEvent(player, inventory, index, ClickType.SHIFT_CLICK, item, cursor);
continue;
} else {
resultClicked = clickedRule.apply(resultClicked, totalAmount);
itemSetter.accept(index, resultClicked);
item = itemRule.apply(item, 0);
itemSetter.accept(slot, item);
filled = true;
callClickEvent(player, inventory, index, ClickType.SHIFT_CLICK, item, cursor);
break;
}
} else if (item.isAir()) {
clickResult = startCondition(inventory, player, index, ClickType.SHIFT_CLICK, item, cursor);
if (clickResult.isCancel())
break;
// Switch
itemSetter.accept(index, resultClicked);
itemSetter.accept(slot, ItemStack.AIR);
filled = true;
break;
}
}
if (!filled) {
itemSetter.accept(slot, resultClicked);
}
if (clickResult.isCancel()) {
return clickResult;
}
if (clicked.isAir())
return null;
var pair = TransactionType.ADD.process(targetInventory, clicked, (index, itemStack) -> {
if (index == slot) // Prevent item lose/duplication
return false;
InventoryClickResult result = startCondition(null, player, index, ClickType.SHIFT_CLICK, itemStack, cursor);
return !result.isCancel();
}, start, end, step);
var itemResult = pair.left();
var map = pair.right();
map.forEach(targetInventory::setItemStack);
clickResult.setClicked(itemResult);
return clickResult;
}