Style change

This commit is contained in:
themode 2024-04-11 21:31:40 +02:00 committed by mworzala
parent 84ead9b25c
commit a0c915a5c9
No known key found for this signature in database
GPG Key ID: B148F922E64797C7
3 changed files with 76 additions and 75 deletions

View File

@ -22,19 +22,35 @@ public final class Click {
* The inventory used should be known from context.
*/
public sealed interface Info {
record Left(int slot) implements Info {}
record Right(int slot) implements Info {}
record Middle(int slot) implements Info {} // Creative only
record Middle(int slot) implements Info {
// Creative only
}
record LeftShift(int slot) implements Info {}
record RightShift(int slot) implements Info {}
record Double(int slot) implements Info {}
record LeftDrag(List<Integer> slots) implements Info {}
record RightDrag(List<Integer> slots) implements Info {}
record MiddleDrag(List<Integer> slots) implements Info {} // Creative only
record LeftDrag(List<Integer> slots) implements Info {
public LeftDrag {
slots = List.copyOf(slots);
}
}
record RightDrag(List<Integer> slots) implements Info {
public RightDrag {
slots = List.copyOf(slots);
}
}
record MiddleDrag(List<Integer> slots) implements Info {
// Creative only
public MiddleDrag {
slots = List.copyOf(slots);
}
}
record LeftDropCursor() implements Info {}
record RightDropCursor() implements Info {}
@ -47,14 +63,12 @@ public final class Click {
record CreativeSetItem(int slot, @NotNull ItemStack item) implements Info {}
record CreativeDropItem(@NotNull ItemStack item) implements Info {}
}
/**
* Preprocesses click packets, turning them into {@link Info} instances for further processing.
*/
public static final class Preprocessor {
private final List<Integer> leftDrag = new ArrayList<>();
private final List<Integer> rightDrag = new ArrayList<>();
private final List<Integer> middleDrag = new ArrayList<>();
@ -69,8 +83,8 @@ public final class Click {
* Processes the provided click packet, turning it into a {@link Info}.
* This will do simple verification of the packet before sending it to {@link #process(ClientClickWindowPacket.ClickType, int, byte, boolean)}.
*
* @param packet the raw click packet
* @param isCreative whether or not the player is in creative mode (used for ignoring some actions)
* @param packet the raw click packet
* @param isCreative whether the player is in creative mode (used for ignoring some actions)
* @return the information about the click, or nothing if there was no immediately usable information
*/
public @Nullable Click.Info process(@NotNull ClientClickWindowPacket packet, @NotNull Inventory inventory, boolean isCreative) {
@ -81,25 +95,24 @@ public final class Click {
int slot = inventory instanceof PlayerInventory ? PlayerInventoryUtils.protocolToMinestom(originalSlot) : originalSlot;
if (originalSlot == -999) slot = -999;
boolean creativeRequired = switch (type) {
final boolean creativeRequired = switch (type) {
case CLONE -> true;
case QUICK_CRAFT -> button == 8 || button == 9 || button == 10;
default -> false;
};
if (creativeRequired && !isCreative) return null;
int maxSize = inventory.getSize() + (inventory instanceof PlayerInventory ? 0 : PlayerInventoryUtils.INNER_SIZE);
final int maxSize = inventory.getSize() + (inventory instanceof PlayerInventory ? 0 : PlayerInventoryUtils.INNER_SIZE);
return process(type, slot, button, slot >= 0 && slot < maxSize);
}
/**
* Processes a packet into click info.
*
* @param type the type of the click
* @param slot the clicked slot
* @param button the sent button
* @param valid whether or not {@code slot} fits within the inventory (may be unused, depending on click)
* @param type the type of the click
* @param slot the clicked slot
* @param button the sent button
* @param valid whether {@code slot} fits within the inventory (may be unused, depending on click)
* @return the information about the click, or nothing if there was no immediately usable information
*/
public @Nullable Click.Info process(@NotNull ClientClickWindowPacket.ClickType type,
@ -182,7 +195,6 @@ public final class Click {
public record Getter(@NotNull IntFunction<ItemStack> main, @NotNull IntFunction<ItemStack> player,
@NotNull ItemStack cursor, int mainSize) {
public @NotNull ItemStack get(int slot) {
if (slot < mainSize()) {
return main.apply(slot);
@ -196,8 +208,7 @@ public final class Click {
}
}
public static class Setter {
public static final class Setter {
private final Map<Integer, ItemStack> main = new HashMap<>();
private final Map<Integer, ItemStack> player = new HashMap<>();
private @Nullable ItemStack cursor;
@ -237,37 +248,32 @@ public final class Click {
public @NotNull Click.Result build() {
return new Result(main, player, cursor, sideEffect);
}
}
/**
* Stores changes that occurred or will occur as the result of a click.
* @param changes the map of changes that will occur to the inventory
*
* @param changes the map of changes that will occur to the inventory
* @param playerInventoryChanges the map of changes that will occur to the player inventory
* @param newCursorItem the player's cursor item after this click. Null indicates no change
* @param sideEffects the side effects of this click
* @param newCursorItem the player's cursor item after this click. Null indicates no change
* @param sideEffects the side effects of this click
*/
public record Result(@NotNull Map<Integer, ItemStack> changes, @NotNull Map<Integer, ItemStack> playerInventoryChanges,
public record Result(@NotNull Map<Integer, ItemStack> changes,
@NotNull Map<Integer, ItemStack> playerInventoryChanges,
@Nullable ItemStack newCursorItem, @Nullable Click.SideEffect sideEffects) {
public static @NotNull Result nothing() {
return new Result(Map.of(), Map.of(), null, null);
}
public static final Result NOTHING = new Result(Map.of(), Map.of(), null, null);
public Result {
changes = Map.copyOf(changes);
playerInventoryChanges = Map.copyOf(playerInventoryChanges);
}
}
/**
* Represents side effects that may occur as the result of an inventory click.
*/
public sealed interface SideEffect {
record DropFromPlayer(@NotNull List<ItemStack> items) implements SideEffect {
record DropFromPlayer(@NotNull List<@NotNull ItemStack> items) implements SideEffect {
public DropFromPlayer {
items = List.copyOf(items);
}

View File

@ -18,12 +18,11 @@ import java.util.stream.IntStream;
* Provides standard implementations of most click functions.
*/
public final class ClickProcessors {
private static final @NotNull StackingRule RULE = StackingRule.get();
public static @NotNull Click.Result leftClick(int slot, @NotNull Click.Getter getter) {
ItemStack cursor = getter.cursor();
ItemStack clickedItem = getter.get(slot);
final ItemStack cursor = getter.cursor();
final ItemStack clickedItem = getter.get(slot);
Pair<ItemStack, ItemStack> pair = TransactionOperator.STACK_LEFT.apply(clickedItem, cursor);
if (pair != null) { // Stackable items, combine their counts
@ -31,24 +30,23 @@ public final class ClickProcessors {
} else if (!RULE.canBeStacked(cursor, clickedItem)) { // If they're unstackable, switch them
return getter.setter().set(slot, cursor).cursor(clickedItem).build();
} else {
return Click.Result.nothing();
return Click.Result.NOTHING;
}
}
public static @NotNull Click.Result rightClick(int slot, @NotNull Click.Getter getter) {
ItemStack cursor = getter.cursor();
ItemStack clickedItem = getter.get(slot);
if (cursor.isAir() && clickedItem.isAir()) return Click.Result.nothing(); // Both are air, no changes
final ItemStack cursor = getter.cursor();
final ItemStack clickedItem = getter.get(slot);
if (cursor.isAir() && clickedItem.isAir()) return Click.Result.NOTHING; // Both are air, no changes
if (cursor.isAir()) { // Take half (rounded up) of the clicked item
int newAmount = (int) Math.ceil(RULE.getAmount(clickedItem) / 2d);
Pair<ItemStack, ItemStack> cursorSlot = TransactionOperator.stackLeftN(newAmount).apply(cursor, clickedItem);
return cursorSlot == null ? Click.Result.nothing() :
return cursorSlot == null ? Click.Result.NOTHING :
getter.setter().cursor(cursorSlot.left()).set(slot, cursorSlot.right()).build();
} else if (clickedItem.isAir() || RULE.canBeStacked(clickedItem, cursor)) { // Can add, transfer one over
Pair<ItemStack, ItemStack> slotCursor = TransactionOperator.stackLeftN(1).apply(clickedItem, cursor);
return slotCursor == null ? Click.Result.nothing() :
return slotCursor == null ? Click.Result.NOTHING :
getter.setter().set(slot, slotCursor.left()).cursor(slotCursor.right()).build();
} else { // Two existing of items of different types, so switch
return getter.setter().cursor(clickedItem).set(slot, cursor).build();
@ -60,12 +58,12 @@ public final class ClickProcessors {
if (getter.cursor().isAir() && !item.isAir()) {
return getter.setter().cursor(RULE.apply(item, RULE.getMaxSize(item))).build();
} else {
return Click.Result.nothing();
return Click.Result.NOTHING;
}
}
public static @NotNull Click.Result shiftClick(int slot, @NotNull List<Integer> slots, @NotNull Click.Getter getter) {
ItemStack clicked = getter.get(slot);
final ItemStack clicked = getter.get(slot);
slots = new ArrayList<>(slots);
slots.removeIf(i -> i == slot);
@ -80,8 +78,8 @@ public final class ClickProcessors {
}
public static @NotNull Click.Result doubleClick(@NotNull List<Integer> slots, @NotNull Click.Getter getter) {
var cursor = getter.cursor();
if (cursor.isAir()) Click.Result.nothing();
final ItemStack cursor = getter.cursor();
if (cursor.isAir()) return Click.Result.NOTHING;
var unstacked = TransactionType.general(TransactionOperator.filter(TransactionOperator.STACK_RIGHT, (left, right) -> RULE.getAmount(left) < RULE.getMaxSize(left)), slots);
var stacked = TransactionType.general(TransactionOperator.filter(TransactionOperator.STACK_RIGHT, (left, right) -> RULE.getAmount(left) == RULE.getMaxSize(left)), slots);
@ -96,8 +94,8 @@ public final class ClickProcessors {
}
public static @NotNull Click.Result dragClick(int countPerSlot, @NotNull List<Integer> slots, @NotNull Click.Getter getter) {
var cursor = getter.cursor();
if (cursor.isAir()) return Click.Result.nothing();
final ItemStack cursor = getter.cursor();
if (cursor.isAir()) return Click.Result.NOTHING;
Pair<ItemStack, Map<Integer, ItemStack>> result = TransactionType.general(TransactionOperator.stackLeftN(countPerSlot), slots).process(cursor, getter::get);
Click.Setter setter = getter.setter();
@ -109,25 +107,22 @@ public final class ClickProcessors {
}
public static @NotNull Click.Result middleDragClick(@NotNull List<Integer> slots, @NotNull Click.Getter getter) {
var cursor = getter.cursor();
final ItemStack cursor = getter.cursor();
Click.Setter setter = getter.setter();
for (int slot : slots) {
if (getter.get(slot).isAir()) {
setter.set(slot, cursor);
}
}
return setter.build();
}
public static @NotNull Click.Result dropFromCursor(int amount, @NotNull Click.Getter getter) {
var cursor = getter.cursor();
if (cursor.isAir()) Click.Result.nothing(); // Do nothing
final ItemStack cursor = getter.cursor();
if (cursor.isAir()) return Click.Result.NOTHING; // Do nothing
var pair = TransactionOperator.stackLeftN(amount).apply(ItemStack.AIR, cursor);
if (pair == null) return Click.Result.nothing();
if (pair == null) return Click.Result.NOTHING;
return getter.setter().cursor(pair.right())
.sideEffects(new Click.SideEffect.DropFromPlayer(pair.left()))
@ -135,11 +130,11 @@ public final class ClickProcessors {
}
public static @NotNull Click.Result dropFromSlot(int slot, int amount, @NotNull Click.Getter getter) {
var item = getter.get(slot);
if (item.isAir()) return Click.Result.nothing(); // Do nothing
final ItemStack item = getter.get(slot);
if (item.isAir()) return Click.Result.NOTHING; // Do nothing
var pair = TransactionOperator.stackLeftN(amount).apply(ItemStack.AIR, item);
if (pair == null) return Click.Result.nothing();
if (pair == null) return Click.Result.NOTHING;
return getter.setter().set(slot, pair.right())
.sideEffects(new Click.SideEffect.DropFromPlayer(pair.left()))
@ -150,9 +145,10 @@ public final class ClickProcessors {
* Handles clicks, given a shift click provider and a double click provider.<br>
* When shift clicks or double clicks need to be handled, the slots provided from the relevant handler will be
* checked in their given order.<br>
* For example, double clicking will collect items of the same type as the cursor; the slots provided by the double
* For example, double-clicking will collect items of the same type as the cursor; the slots provided by the double
* click slot provider will be checked sequentially and used if they have the same type as
* @param shiftClickSlots the shift click slot supplier
*
* @param shiftClickSlots the shift click slot supplier
* @param doubleClickSlots the double click slot supplier
*/
public static @NotNull BiFunction<Click.@NotNull Info, Click.@NotNull Getter, Click.@NotNull Result> standard(@NotNull SlotSuggestor shiftClickSlots, @NotNull SlotSuggestor doubleClickSlots) {
@ -173,18 +169,18 @@ public final class ClickProcessors {
case Click.Info.DropSlot(int slot, boolean all) -> dropFromSlot(slot, all ? RULE.getAmount(getter.get(slot)) : 1, getter);
case Click.Info.LeftDropCursor() -> dropFromCursor(getter.cursor().amount(), getter);
case Click.Info.RightDropCursor() -> dropFromCursor(1, getter);
case Click.Info.MiddleDropCursor() -> Click.Result.nothing();
case Click.Info.MiddleDropCursor() -> Click.Result.NOTHING;
case Click.Info.HotbarSwap(int hotbarSlot, int clickedSlot) -> {
var hotbarItem = getter.player().apply(hotbarSlot);
var selectedItem = getter.get(clickedSlot);
if (hotbarItem.equals(selectedItem)) yield Click.Result.nothing();
if (hotbarItem.equals(selectedItem)) yield Click.Result.NOTHING;
yield getter.setter().setPlayer(hotbarSlot, selectedItem).set(clickedSlot, hotbarItem).build();
}
case Click.Info.OffhandSwap(int slot) -> {
var offhandItem = getter.player().apply(PlayerInventoryUtils.OFF_HAND_SLOT);
var selectedItem = getter.get(slot);
if (offhandItem.equals(selectedItem)) yield Click.Result.nothing();
if (offhandItem.equals(selectedItem)) yield Click.Result.NOTHING;
yield getter.setter().setPlayer(PlayerInventoryUtils.OFF_HAND_SLOT, selectedItem).set(slot, offhandItem).build();
}
@ -204,16 +200,17 @@ public final class ClickProcessors {
/**
* Suggests slots to be used for this operation.
*
* @param builder the result builder
* @param item the item clicked
* @param slot the slot of the clicked item
* @param item the item clicked
* @param slot the slot of the clicked item
* @return the list of slots, in order of priority, to be used for this operation
*/
@NotNull IntStream get(@NotNull Click.Getter builder, @NotNull ItemStack item, int slot);
@NotNull
IntStream get(@NotNull Click.Getter builder, @NotNull ItemStack item, int slot);
default @NotNull List<Integer> getList(@NotNull Click.Getter builder, @NotNull ItemStack item, int slot) {
return get(builder, item, slot).boxed().toList();
}
}
}

View File

@ -5,11 +5,10 @@ package net.minestom.server.utils.inventory;
* Minestom uses different slot IDs for player inventories as the Minecraft protocol uses a strange system (e.g. the
* crafting result is the first slot).<br>
* These can be mapped 1:1 to and from protocol slots using {@link #minestomToProtocol(int)} and {@link #protocolToMinestom(int)}.<br>
*
* <p>
* Read about protocol slot IDs <a href="https://wiki.vg/Inventory">here</a>.
*/
public final class PlayerInventoryUtils {
public static final int INVENTORY_SIZE = 46;
public static final int INNER_SIZE = 36;
@ -28,12 +27,12 @@ public final class PlayerInventoryUtils {
public static final int OFF_HAND_SLOT = 45;
private PlayerInventoryUtils() {
}
/**
* Converts a Minestom slot ID to a Minecraft protocol slot ID.<br>
* This is the inverse of {@link #protocolToMinestom(int)}.
*
* @param slot the internal slot ID to convert
* @return the protocol slot ID, or -1 if the given slot could not be converted
*/
@ -64,6 +63,7 @@ public final class PlayerInventoryUtils {
/**
* Converts a Minecraft protocol slot ID to a Minestom slot ID.<br>
* This is the inverse of {@link #minestomToProtocol(int)}.
*
* @param slot the protocol slot ID to convert
* @return the Minestom slot ID, or -1 if the given slot could not be converted
*/
@ -97,7 +97,7 @@ public final class PlayerInventoryUtils {
* open.<br>
* This is the inverse of {@link #protocolToMinestom(int, int)}.
*
* @param slot the player slot that was interacted with
* @param slot the player slot that was interacted with
* @param openInventorySize the size of the inventory opened by the player (not the player's inventory)
* @return the protocol slot ID
*/
@ -111,14 +111,12 @@ public final class PlayerInventoryUtils {
* open.<br>
* This is the inverse of {@link #minestomToProtocol(int, int)}.
*
* @param slot the protocol slot ID, situated directly after the slot IDs for the open inventory
* @param slot the protocol slot ID, situated directly after the slot IDs for the open inventory
* @param openInventorySize the size of the inventory opened by the player (not the player's inventory)
* @return the player slot ID
*/
public static int protocolToMinestom(int slot, int openInventorySize) {
if (slot < openInventorySize) return -1;
return PlayerInventoryUtils.protocolToMinestom(slot - openInventorySize + PROTOCOL_OFFSET);
}
}
}