Simplify InventoryClickProcessor

This commit is contained in:
TheMode 2021-08-11 20:35:30 +02:00
parent 5607652378
commit be07fdb647

View File

@ -20,7 +20,6 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -28,155 +27,103 @@ import java.util.function.BiConsumer;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ApiStatus.Internal @ApiStatus.Internal
public class InventoryClickProcessor { public final class InventoryClickProcessor {
// Dragging maps // Dragging maps
private final Map<Player, IntSet> leftDraggingMap = new ConcurrentHashMap<>(); private final Map<Player, IntSet> leftDraggingMap = new ConcurrentHashMap<>();
private final Map<Player, IntSet> rightDraggingMap = new ConcurrentHashMap<>(); private final Map<Player, IntSet> rightDraggingMap = new ConcurrentHashMap<>();
@NotNull public @NotNull InventoryClickResult leftClick(@Nullable Inventory inventory, @NotNull Player player, int slot,
public InventoryClickResult leftClick(@Nullable Inventory inventory, @NotNull Player player, int slot, @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
@NotNull ItemStack clicked, @NotNull ItemStack cursor) { final var result = startCondition(inventory, player, slot, ClickType.LEFT_CLICK, clicked, cursor);
final InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.LEFT_CLICK, clicked, cursor); if (result.isCancel()) return result;
clicked = clickResult.getClicked(); clicked = result.getClicked();
cursor = clickResult.getCursor(); cursor = result.getCursor();
if (clickResult.isCancel()) {
return clickResult;
}
if (cursor.isAir() && clicked.isAir()) {
clickResult.setCancel(true);
return clickResult;
}
final StackingRule cursorRule = cursor.getStackingRule(); final StackingRule cursorRule = cursor.getStackingRule();
final StackingRule clickedRule = clicked.getStackingRule(); final StackingRule clickedRule = clicked.getStackingRule();
ItemStack resultCursor;
ItemStack resultClicked;
if (cursorRule.canBeStacked(cursor, clicked)) { if (cursorRule.canBeStacked(cursor, clicked)) {
// Try to stack items
final int totalAmount = cursorRule.getAmount(cursor) + clickedRule.getAmount(clicked); final int totalAmount = cursorRule.getAmount(cursor) + clickedRule.getAmount(clicked);
final int maxSize = cursorRule.getMaxSize(cursor); final int maxSize = cursorRule.getMaxSize(cursor);
if (!clickedRule.canApply(clicked, totalAmount)) { if (!clickedRule.canApply(clicked, totalAmount)) {
resultCursor = cursorRule.apply(cursor, totalAmount - maxSize); // Size is too big, stack as much as possible into clicked
resultClicked = clickedRule.apply(clicked, maxSize); result.setCursor(cursorRule.apply(cursor, totalAmount - maxSize));
result.setClicked(clickedRule.apply(clicked, maxSize));
} else { } else {
resultCursor = cursorRule.apply(cursor, 0); // Merge cursor item clicked
resultClicked = clickedRule.apply(clicked, totalAmount); result.setCursor(cursorRule.apply(cursor, 0));
result.setClicked(clickedRule.apply(clicked, totalAmount));
} }
} else { } else {
resultCursor = clicked; // Items are not compatible, swap them
resultClicked = cursor; result.setCursor(clicked);
result.setClicked(cursor);
} }
return result;
clickResult.setClicked(resultClicked);
clickResult.setCursor(resultCursor);
return clickResult;
} }
@NotNull public @NotNull InventoryClickResult rightClick(@Nullable Inventory inventory, @NotNull Player player,
public InventoryClickResult rightClick(@Nullable Inventory inventory, @NotNull Player player, int slot, int slot,
@NotNull ItemStack clicked, @NotNull ItemStack cursor) { @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
final InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.RIGHT_CLICK, clicked, cursor); final var result = startCondition(inventory, player, slot, ClickType.RIGHT_CLICK, clicked, cursor);
if (result.isCancel()) return result;
if (clickResult.isCancel()) { clicked = result.getClicked();
return clickResult; cursor = result.getCursor();
}
if (cursor.isAir() && clicked.isAir()) {
clickResult.setCancel(true);
return clickResult;
}
final StackingRule cursorRule = cursor.getStackingRule(); final StackingRule cursorRule = cursor.getStackingRule();
final StackingRule clickedRule = clicked.getStackingRule(); final StackingRule clickedRule = clicked.getStackingRule();
ItemStack resultCursor;
ItemStack resultClicked;
if (clickedRule.canBeStacked(clicked, cursor)) { if (clickedRule.canBeStacked(clicked, cursor)) {
// Items can be stacked
final int amount = clickedRule.getAmount(clicked) + 1; final int amount = clickedRule.getAmount(clicked) + 1;
if (!clickedRule.canApply(clicked, amount)) { if (!clickedRule.canApply(clicked, amount)) {
return clickResult; // Size too large, stop here
return result;
} else { } else {
resultCursor = cursorRule.apply(cursor, operand -> operand - 1); // Add 1 to clicked
resultClicked = clickedRule.apply(clicked, amount); result.setCursor(cursorRule.apply(cursor, operand -> operand - 1));
result.setClicked(clickedRule.apply(clicked, amount));
} }
} else { } else {
// Items cannot be stacked
if (cursor.isAir()) { if (cursor.isAir()) {
// Take half of clicked
final int amount = (int) Math.ceil((double) clickedRule.getAmount(clicked) / 2d); final int amount = (int) Math.ceil((double) clickedRule.getAmount(clicked) / 2d);
resultCursor = cursorRule.apply(clicked, amount); result.setCursor(cursorRule.apply(clicked, amount));
resultClicked = clickedRule.apply(clicked, operand -> operand / 2); result.setClicked(clickedRule.apply(clicked, operand -> operand / 2));
} else { } else {
if (clicked.isAir()) { if (clicked.isAir()) {
resultCursor = cursorRule.apply(cursor, operand -> operand - 1); // Put 1 to clicked
resultClicked = clickedRule.apply(cursor, 1); result.setCursor(cursorRule.apply(cursor, operand -> operand - 1));
result.setClicked(clickedRule.apply(cursor, 1));
} else { } else {
resultCursor = clicked; // Swap items
resultClicked = cursor; result.setCursor(clicked);
result.setClicked(cursor);
} }
} }
} }
return result;
clickResult.setClicked(resultClicked);
clickResult.setCursor(resultCursor);
return clickResult;
} }
@NotNull public @NotNull InventoryClickResult changeHeld(@Nullable Inventory inventory, @NotNull Player player,
public InventoryClickResult changeHeld(@Nullable Inventory inventory, @NotNull Player player, int slot, int key, int slot, int key,
@NotNull ItemStack clicked, @NotNull ItemStack cursor) { @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
// Verify the clicked item // Verify the clicked item
InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.CHANGE_HELD, clicked, cursor); InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.CHANGE_HELD, clicked, cursor);
if (clickResult.isCancel()) return clickResult; if (clickResult.isCancel()) return clickResult;
// Verify the destination (held bar) // Verify the destination (held bar)
clickResult = startCondition(null, player, key, ClickType.CHANGE_HELD, clicked, cursor); clickResult = startCondition(null, player, key, ClickType.CHANGE_HELD, clicked, cursor);
if (clickResult.isCancel()) return clickResult; if (clickResult.isCancel()) return clickResult;
// Swap items
if (cursor.isAir() && clicked.isAir()) { clickResult.setClicked(cursor);
clickResult.setCancel(true); clickResult.setCursor(clicked);
return clickResult;
}
ItemStack resultClicked;
ItemStack resultHeld;
if (clicked.isAir()) {
// Set held item [key] to slot
resultClicked = cursor;
resultHeld = ItemStack.AIR;
} else {
if (cursor.isAir()) {
// if held item [key] is air then set clicked to held
resultClicked = ItemStack.AIR;
} else {
// Otherwise, replace held item and held
resultClicked = cursor;
}
resultHeld = clicked;
}
clickResult.setClicked(resultClicked);
clickResult.setCursor(resultHeld);
return clickResult; return clickResult;
} }
public @Nullable InventoryClickResult shiftClick(AbstractInventory targetInventory, @Nullable Inventory inventory, @NotNull Player player, int slot, public @Nullable InventoryClickResult shiftClick(AbstractInventory targetInventory, @Nullable Inventory inventory, @NotNull Player player, int slot,
@NotNull ItemStack clicked, @NotNull ItemStack cursor) { @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.START_SHIFT_CLICK, clicked, cursor); InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.START_SHIFT_CLICK, clicked, cursor);
if (clickResult.isCancel()) return clickResult;
if (clickResult.isCancel()) { if (clicked.isAir()) return null;
return clickResult; final var pair = TransactionType.ADD.process(targetInventory, clicked, (index, itemStack) -> {
}
if (clicked.isAir())
return null;
var pair = TransactionType.ADD.process(targetInventory, clicked, (index, itemStack) -> {
InventoryClickResult result = startCondition(targetInventory, player, index, ClickType.SHIFT_CLICK, itemStack, cursor); InventoryClickResult result = startCondition(targetInventory, player, index, ClickType.SHIFT_CLICK, itemStack, cursor);
if (result.isCancel()) { if (result.isCancel()) {
clickResult.setRefresh(true); clickResult.setRefresh(true);
@ -184,7 +131,6 @@ public class InventoryClickProcessor {
} }
return true; return true;
}); });
ItemStack itemResult = TransactionOption.ALL.fill(targetInventory, pair.left(), pair.right()); ItemStack itemResult = TransactionOption.ALL.fill(targetInventory, pair.left(), pair.right());
clickResult.setClicked(itemResult); clickResult.setClicked(itemResult);
return clickResult; return clickResult;
@ -193,15 +139,9 @@ public class InventoryClickProcessor {
public @Nullable InventoryClickResult shiftClick(@NotNull AbstractInventory targetInventory, int start, int end, int step, @NotNull Player player, int slot, public @Nullable InventoryClickResult shiftClick(@NotNull AbstractInventory targetInventory, int start, int end, int step, @NotNull Player player, int slot,
@NotNull ItemStack clicked, @NotNull ItemStack cursor) { @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
InventoryClickResult clickResult = startCondition(null, player, slot, ClickType.START_SHIFT_CLICK, clicked, cursor); InventoryClickResult clickResult = startCondition(null, player, slot, ClickType.START_SHIFT_CLICK, clicked, cursor);
if (clickResult.isCancel()) return clickResult;
if (clickResult.isCancel()) { if (clicked.isAir()) return null;
return clickResult; final var pair = TransactionType.ADD.process(targetInventory, clicked, (index, itemStack) -> {
}
if (clicked.isAir())
return null;
var pair = TransactionType.ADD.process(targetInventory, clicked, (index, itemStack) -> {
if (index == slot) // Prevent item lose/duplication if (index == slot) // Prevent item lose/duplication
return false; return false;
InventoryClickResult result = startCondition(targetInventory, player, index, ClickType.SHIFT_CLICK, itemStack, cursor); InventoryClickResult result = startCondition(targetInventory, player, index, ClickType.SHIFT_CLICK, itemStack, cursor);
@ -211,18 +151,16 @@ public class InventoryClickProcessor {
} }
return true; return true;
}, start, end, step); }, start, end, step);
ItemStack itemResult = TransactionOption.ALL.fill(targetInventory, pair.left(), pair.right()); ItemStack itemResult = TransactionOption.ALL.fill(targetInventory, pair.left(), pair.right());
clickResult.setClicked(itemResult); clickResult.setClicked(itemResult);
return clickResult; return clickResult;
} }
@Nullable public @Nullable InventoryClickResult dragging(@Nullable Inventory inventory, @NotNull Player player,
public InventoryClickResult dragging(@Nullable Inventory inventory, @NotNull Player player, int slot, int button,
int slot, int button, @NotNull ItemStack clicked, @NotNull ItemStack cursor,
@NotNull ItemStack clicked, @NotNull ItemStack cursor, @NotNull Int2ObjectFunction<ItemStack> itemGetter,
@NotNull Int2ObjectFunction<ItemStack> itemGetter, @NotNull BiConsumer<Integer, ItemStack> itemSetter) {
@NotNull BiConsumer<Integer, ItemStack> itemSetter) {
InventoryClickResult clickResult = null; InventoryClickResult clickResult = null;
final StackingRule stackingRule = cursor.getStackingRule(); final StackingRule stackingRule = cursor.getStackingRule();
@ -232,13 +170,11 @@ public class InventoryClickProcessor {
if (button == 0) { if (button == 0) {
// Start left // Start left
clickResult = startCondition(inventory, player, slot, ClickType.START_LEFT_DRAGGING, clicked, cursor); clickResult = startCondition(inventory, player, slot, ClickType.START_LEFT_DRAGGING, clicked, cursor);
if (!clickResult.isCancel()) if (!clickResult.isCancel())
this.leftDraggingMap.put(player, new IntOpenHashSet()); this.leftDraggingMap.put(player, new IntOpenHashSet());
} else if (button == 4) { } else if (button == 4) {
// Start right // Start right
clickResult = startCondition(inventory, player, slot, ClickType.START_RIGHT_DRAGGING, clicked, cursor); clickResult = startCondition(inventory, player, slot, ClickType.START_RIGHT_DRAGGING, clicked, cursor);
if (!clickResult.isCancel()) if (!clickResult.isCancel())
this.rightDraggingMap.put(player, new IntOpenHashSet()); this.rightDraggingMap.put(player, new IntOpenHashSet());
} else if (button == 2) { } else if (button == 2) {
@ -255,12 +191,9 @@ public class InventoryClickProcessor {
for (int s : slots) { for (int s : slots) {
ItemStack slotItem = itemGetter.apply(s); ItemStack slotItem = itemGetter.apply(s);
clickResult = startCondition(inventory, player, s, ClickType.LEFT_DRAGGING, slotItem, cursor); clickResult = startCondition(inventory, player, s, ClickType.LEFT_DRAGGING, slotItem, cursor);
if (clickResult.isCancel()) { if (clickResult.isCancel()) {
cancel = true; cancel = true;
break; break;
} }
} }
@ -381,17 +314,11 @@ public class InventoryClickProcessor {
return clickResult; return clickResult;
} }
@Nullable public @Nullable InventoryClickResult doubleClick(@NotNull AbstractInventory clickedInventory, @Nullable Inventory inventory, @NotNull Player player, int slot,
public InventoryClickResult doubleClick(@NotNull AbstractInventory clickedInventory, @Nullable Inventory inventory, @NotNull Player player, int slot, @NotNull final ItemStack cursor) {
@NotNull final ItemStack cursor) {
InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.START_DOUBLE_CLICK, ItemStack.AIR, cursor); InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.START_DOUBLE_CLICK, ItemStack.AIR, cursor);
if (clickResult.isCancel()) return clickResult;
if (clickResult.isCancel()) { if (cursor.isAir()) return null;
return clickResult;
}
if (cursor.isAir())
return null;
final StackingRule cursorRule = cursor.getStackingRule(); final StackingRule cursorRule = cursor.getStackingRule();
final int amount = cursorRule.getAmount(cursor); final int amount = cursorRule.getAmount(cursor);
@ -418,7 +345,6 @@ public class InventoryClickProcessor {
}; };
var playerInventory = player.getInventory(); var playerInventory = player.getInventory();
if (Objects.equals(clickedInventory, inventory)) { if (Objects.equals(clickedInventory, inventory)) {
// Clicked inside inventory // Clicked inside inventory
remain = func.apply(inventory, remain); remain = func.apply(inventory, remain);
@ -449,10 +375,9 @@ public class InventoryClickProcessor {
return clickResult; return clickResult;
} }
@NotNull public @NotNull InventoryClickResult drop(@Nullable Inventory inventory, @NotNull Player player,
public InventoryClickResult drop(@Nullable Inventory inventory, @NotNull Player player, boolean all, int slot, int button,
boolean all, int slot, int button, @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
@NotNull ItemStack clicked, @NotNull ItemStack cursor) {
final InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.DROP, clicked, cursor); final InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.DROP, clicked, cursor);
if (clickResult.isCancel()) { if (clickResult.isCancel()) {
@ -465,7 +390,6 @@ public class InventoryClickProcessor {
ItemStack resultClicked = clicked; ItemStack resultClicked = clicked;
ItemStack resultCursor = cursor; ItemStack resultCursor = cursor;
if (slot == -999) { if (slot == -999) {
// Click outside // Click outside
if (button == 0) { if (button == 0) {
@ -518,9 +442,10 @@ public class InventoryClickProcessor {
return clickResult; return clickResult;
} }
@NotNull private @NotNull InventoryClickResult startCondition(@Nullable Inventory inventory,
private InventoryClickResult startCondition(@NotNull InventoryClickResult clickResult, @Nullable Inventory inventory, @NotNull Player player, int slot, @NotNull ClickType clickType,
@NotNull Player player, int slot, @NotNull ClickType clickType) { @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
final InventoryClickResult clickResult = new InventoryClickResult(clicked, cursor);
boolean isPlayerInventory = inventory == null; boolean isPlayerInventory = inventory == null;
final int inventorySlot = isPlayerInventory ? 0 : inventory.getSize(); final int inventorySlot = isPlayerInventory ? 0 : inventory.getSize();
isPlayerInventory = isPlayerInventory ? isPlayerInventory : slot >= inventorySlot; isPlayerInventory = isPlayerInventory ? isPlayerInventory : slot >= inventorySlot;
@ -531,81 +456,67 @@ public class InventoryClickProcessor {
slot = slot - inventorySlot + PlayerInventoryUtils.OFFSET; slot = slot - inventorySlot + PlayerInventoryUtils.OFFSET;
} }
// Call ItemStack#onInventoryClick
{
//clickResult.getClicked().onInventoryClick(player, clickType, slot, isPlayerInventory);
}
// Reset the didCloseInventory field // Reset the didCloseInventory field
// Wait for inventory conditions + events to possibly close the inventory // Wait for inventory conditions + events to possibly close the inventory
player.UNSAFE_changeDidCloseInventory(false); player.UNSAFE_changeDidCloseInventory(false);
// InventoryPreClickEvent
// PreClickEvent
{ {
InventoryPreClickEvent inventoryPreClickEvent = new InventoryPreClickEvent(inventory, player, slot, clickType, InventoryPreClickEvent inventoryPreClickEvent = new InventoryPreClickEvent(inventory, player, slot, clickType,
clickResult.getClicked(), clickResult.getCursor()); clickResult.getClicked(), clickResult.getCursor());
EventDispatcher.call(inventoryPreClickEvent); EventDispatcher.call(inventoryPreClickEvent);
clickResult.setCursor(inventoryPreClickEvent.getCursorItem()); clickResult.setCursor(inventoryPreClickEvent.getCursorItem());
clickResult.setClicked(inventoryPreClickEvent.getClickedItem()); clickResult.setClicked(inventoryPreClickEvent.getClickedItem());
if (inventoryPreClickEvent.isCancelled()) { if (inventoryPreClickEvent.isCancelled()) {
clickResult.setRefresh(true); clickResult.setRefresh(true);
clickResult.setCancel(true); clickResult.setCancel(true);
} }
} }
// Inventory conditions // Inventory conditions
final List<InventoryCondition> inventoryConditions = isPlayerInventory ? {
player.getInventory().getInventoryConditions() : inventory.getInventoryConditions(); final var inventoryConditions = isPlayerInventory ?
if (!inventoryConditions.isEmpty()) { player.getInventory().getInventoryConditions() : inventory.getInventoryConditions();
if (!inventoryConditions.isEmpty()) {
for (InventoryCondition inventoryCondition : inventoryConditions) {
var result = new InventoryConditionResult(clickResult.getClicked(), clickResult.getCursor());
inventoryCondition.accept(player, slot, clickType, result);
for (InventoryCondition inventoryCondition : inventoryConditions) { clickResult.setCursor(result.getCursorItem());
InventoryConditionResult result = new InventoryConditionResult(clickResult.getClicked(), clickResult.getCursor()); clickResult.setClicked(result.getClickedItem());
inventoryCondition.accept(player, slot, clickType, result); if (result.isCancel()) {
clickResult.setRefresh(true);
clickResult.setCursor(result.getCursorItem()); clickResult.setCancel(true);
clickResult.setClicked(result.getClickedItem()); }
}
if (result.isCancel()) { // Cancel the click if the inventory has been closed by Player#closeInventory within an inventory listener
clickResult.setRefresh(true); if (player.didCloseInventory()) {
clickResult.setCancel(true); clickResult.setCancel(true);
player.UNSAFE_changeDidCloseInventory(false);
} }
} }
}
// Cancel the click if the inventory has been closed by Player#closeInventory within an inventory listener // Sanity check
if (player.didCloseInventory()) { {
if (clickResult.getCursor().isAir() && clickResult.getClicked().isAir()) {
// One of the item must not be air
clickResult.setCancel(true); clickResult.setCancel(true);
player.UNSAFE_changeDidCloseInventory(false);
} }
} }
return clickResult; return clickResult;
} }
@NotNull private @NotNull InventoryClickResult startCondition(@Nullable AbstractInventory inventory, @NotNull Player player, int slot,
private InventoryClickResult startCondition(@Nullable Inventory inventory, @NotNull Player player, int slot, @NotNull ClickType clickType, @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
@NotNull ClickType clickType, @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
final InventoryClickResult clickResult = new InventoryClickResult(clicked, cursor);
return startCondition(clickResult, inventory, player, slot, clickType);
}
@NotNull
private InventoryClickResult startCondition(@Nullable AbstractInventory inventory, @NotNull Player player, int slot,
@NotNull ClickType clickType, @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
return startCondition(inventory instanceof Inventory ? (Inventory) inventory : null, return startCondition(inventory instanceof Inventory ? (Inventory) inventory : null,
player, slot, clickType, clicked, cursor); player, slot, clickType, clicked, cursor);
} }
private void callClickEvent(@NotNull Player player, @Nullable Inventory inventory, int slot, private void callClickEvent(@NotNull Player player, @Nullable Inventory inventory, int slot,
@NotNull ClickType clickType, @NotNull ItemStack clicked, @NotNull ItemStack cursor) { @NotNull ClickType clickType, @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
InventoryClickEvent inventoryClickEvent = new InventoryClickEvent(inventory, player, slot, clickType, clicked, cursor); EventDispatcher.call(new InventoryClickEvent(inventory, player, slot, clickType, clicked, cursor));
EventDispatcher.call(inventoryClickEvent);
} }
public void clearCache(@NotNull Player player) { public void clearCache(@NotNull Player player) {
this.leftDraggingMap.remove(player); this.leftDraggingMap.remove(player);
this.rightDraggingMap.remove(player); this.rightDraggingMap.remove(player);
} }
} }