Added InventoryUpdatedEvent

This commit is contained in:
NichtStudioCode 2022-01-07 20:35:30 +01:00
parent eae337ceb9
commit a025d06ac7
5 changed files with 251 additions and 136 deletions

@ -207,9 +207,11 @@ public abstract class BaseGUI implements GUI, Controllable {
protected void handleVIItemShift(InventoryClickEvent event, VirtualInventory inventory, int slot, Player player, ItemStack clicked) { protected void handleVIItemShift(InventoryClickEvent event, VirtualInventory inventory, int slot, Player player, ItemStack clicked) {
if (clicked == null) return; if (clicked == null) return;
ItemStack previousStack = clicked.clone();
UpdateReason updateReason = new PlayerUpdateReason(player, event); UpdateReason updateReason = new PlayerUpdateReason(player, event);
Window window = WindowManager.getInstance().findOpenWindow(player).orElse(null); Window window = WindowManager.getInstance().findOpenWindow(player).orElse(null);
ItemUpdateEvent updateEvent = inventory.callPreUpdateEvent(updateReason, slot, clicked, null); ItemUpdateEvent updateEvent = inventory.callPreUpdateEvent(updateReason, slot, previousStack, null);
if (!updateEvent.isCancelled()) { if (!updateEvent.isCancelled()) {
int leftOverAmount; int leftOverAmount;
@ -232,6 +234,8 @@ public abstract class BaseGUI implements GUI, Controllable {
clicked.setAmount(leftOverAmount); clicked.setAmount(leftOverAmount);
inventory.setItemStackSilently(slot, clicked); inventory.setItemStackSilently(slot, clicked);
inventory.callAfterUpdateEvent(updateReason, slot, previousStack, clicked);
} }
} }

@ -3,6 +3,7 @@ package de.studiocode.invui.virtualinventory;
import de.studiocode.invui.InvUI; import de.studiocode.invui.InvUI;
import de.studiocode.invui.gui.GUI; import de.studiocode.invui.gui.GUI;
import de.studiocode.invui.util.ArrayUtils; import de.studiocode.invui.util.ArrayUtils;
import de.studiocode.invui.virtualinventory.event.InventoryUpdatedEvent;
import de.studiocode.invui.virtualinventory.event.ItemUpdateEvent; import de.studiocode.invui.virtualinventory.event.ItemUpdateEvent;
import de.studiocode.invui.virtualinventory.event.UpdateReason; import de.studiocode.invui.virtualinventory.event.UpdateReason;
import de.studiocode.invui.window.Window; import de.studiocode.invui.window.Window;
@ -31,6 +32,7 @@ public class VirtualInventory implements ConfigurationSerializable {
private ItemStack[] items; private ItemStack[] items;
private int[] stackSizes; private int[] stackSizes;
private Consumer<ItemUpdateEvent> itemUpdateHandler; private Consumer<ItemUpdateEvent> itemUpdateHandler;
private Consumer<InventoryUpdatedEvent> inventoryUpdatedHandler;
private int guiShiftPriority = 0; private int guiShiftPriority = 0;
/** /**
@ -166,6 +168,15 @@ public class VirtualInventory implements ConfigurationSerializable {
this.itemUpdateHandler = itemUpdateHandler; this.itemUpdateHandler = itemUpdateHandler;
} }
/**
* Sets a handler which is called every time after something has been updated in the {@link VirtualInventory}.
*
* @param inventoryUpdatedHandler The new handler
*/
public void setInventoryUpdatedHandler(Consumer<InventoryUpdatedEvent> inventoryUpdatedHandler) {
this.inventoryUpdatedHandler = inventoryUpdatedHandler;
}
/** /**
* Gets the priority for shift-clicking {@link ItemStack ItemStacks} into a {@link GUI} * Gets the priority for shift-clicking {@link ItemStack ItemStacks} into a {@link GUI}
* *
@ -225,6 +236,16 @@ public class VirtualInventory implements ConfigurationSerializable {
return Arrays.stream(items).map(item -> item != null ? item.clone() : null).toArray(ItemStack[]::new); return Arrays.stream(items).map(item -> item != null ? item.clone() : null).toArray(ItemStack[]::new);
} }
/**
* Gets the same {@link ItemStack ItemStack[]} that backs this {@link VirtualInventory}.
* <br>
* As it is not clone, it should be handled carefully as changes done on that array will not call any
* Window updates (and create inconsistency between server and client).
*/
public ItemStack[] getUnsafeItems() {
return items;
}
/** /**
* Gets a clone of the {@link ItemStack} on that slot. * Gets a clone of the {@link ItemStack} on that slot.
* *
@ -302,7 +323,7 @@ public class VirtualInventory implements ConfigurationSerializable {
/** /**
* Gets the maximum stack size for a specific slot while ignoring the {@link ItemStack} on it * Gets the maximum stack size for a specific slot while ignoring the {@link ItemStack} on it
* and it's own maximum stack size. * and its own maximum stack size.
* *
* @param slot The slot * @param slot The slot
* @return The maximum stack size on that slo * @return The maximum stack size on that slo
@ -339,12 +360,27 @@ public class VirtualInventory implements ConfigurationSerializable {
* @param newItemStack The {@link ItemStack} that will be on that slot * @param newItemStack The {@link ItemStack} that will be on that slot
* @return The {@link ItemUpdateEvent} after it has been handled by the {@link #itemUpdateHandler} * @return The {@link ItemUpdateEvent} after it has been handled by the {@link #itemUpdateHandler}
*/ */
public ItemUpdateEvent callUpdateEvent(@Nullable UpdateReason updateReason, int slot, @Nullable ItemStack previousItemStack, @Nullable ItemStack newItemStack) { public ItemUpdateEvent callPreUpdateEvent(@Nullable UpdateReason updateReason, int slot, @Nullable ItemStack previousItemStack, @Nullable ItemStack newItemStack) {
ItemUpdateEvent event = new ItemUpdateEvent(this, slot, updateReason, previousItemStack, newItemStack); ItemUpdateEvent event = new ItemUpdateEvent(this, slot, updateReason, previousItemStack, newItemStack);
if (itemUpdateHandler != null) itemUpdateHandler.accept(event); if (itemUpdateHandler != null) itemUpdateHandler.accept(event);
return event; return event;
} }
/**
* Creates an {@link InventoryUpdatedEvent} and calls the {@link #inventoryUpdatedHandler} to handle it.
*
* @param updateReason The {@link UpdateReason}
* @param slot The slot of the affected {@link ItemStack}
* @param previousItemStack The {@link ItemStack} that was on that slot previously.
* @param newItemStack The {@link ItemStack} that is on that slot now.
* @return The {@link InventoryUpdatedEvent} after it has been handled by the {@link #inventoryUpdatedHandler}
*/
public InventoryUpdatedEvent callAfterUpdateEvent(@Nullable UpdateReason updateReason, int slot, @Nullable ItemStack previousItemStack, @Nullable ItemStack newItemStack) {
InventoryUpdatedEvent event = new InventoryUpdatedEvent(this, slot, updateReason, previousItemStack, newItemStack);
if (inventoryUpdatedHandler != null) inventoryUpdatedHandler.accept(event);
return event;
}
/** /**
* Checks if the {@link ItemStack} on that slot is the same * Checks if the {@link ItemStack} on that slot is the same
* as the assumed {@link ItemStack} provided as parameter. * as the assumed {@link ItemStack} provided as parameter.
@ -389,9 +425,12 @@ public class VirtualInventory implements ConfigurationSerializable {
* @return If the action was successful * @return If the action was successful
*/ */
public boolean forceSetItemStack(@Nullable UpdateReason updateReason, int slot, @Nullable ItemStack itemStack) { public boolean forceSetItemStack(@Nullable UpdateReason updateReason, int slot, @Nullable ItemStack itemStack) {
ItemUpdateEvent event = callUpdateEvent(updateReason, slot, items[slot], itemStack); ItemStack previousStack = items[slot];
ItemUpdateEvent event = callPreUpdateEvent(updateReason, slot, previousStack, itemStack);
if (!event.isCancelled()) { if (!event.isCancelled()) {
setItemStackSilently(slot, event.getNewItemStack()); ItemStack newStack = event.getNewItemStack();
setItemStackSilently(slot, newStack);
callAfterUpdateEvent(updateReason, slot, previousStack, newStack);
return true; return true;
} }
return false; return false;
@ -432,12 +471,14 @@ public class VirtualInventory implements ConfigurationSerializable {
ItemStack newItemStack = itemStack.clone(); ItemStack newItemStack = itemStack.clone();
newItemStack.setAmount(min(currentAmount + itemStack.getAmount(), maxStackSize)); newItemStack.setAmount(min(currentAmount + itemStack.getAmount(), maxStackSize));
ItemUpdateEvent event = callUpdateEvent(updateReason, slot, currentStack, newItemStack); ItemUpdateEvent event = callPreUpdateEvent(updateReason, slot, currentStack, newItemStack);
if (!event.isCancelled()) { if (!event.isCancelled()) {
newItemStack = event.getNewItemStack(); newItemStack = event.getNewItemStack();
items[slot] = newItemStack; items[slot] = newItemStack;
notifyWindows(); notifyWindows();
callAfterUpdateEvent(updateReason, slot, currentStack, newItemStack);
int newAmount = newItemStack != null ? newItemStack.getAmount() : 0; int newAmount = newItemStack != null ? newItemStack.getAmount() : 0;
return itemStack.getAmount() - (newAmount - currentAmount); return itemStack.getAmount() - (newAmount - currentAmount);
} }
@ -470,12 +511,14 @@ public class VirtualInventory implements ConfigurationSerializable {
newItemStack = null; newItemStack = null;
} }
ItemUpdateEvent event = callUpdateEvent(updateReason, slot, currentStack, newItemStack); ItemUpdateEvent event = callPreUpdateEvent(updateReason, slot, currentStack, newItemStack);
if (!event.isCancelled()) { if (!event.isCancelled()) {
newItemStack = event.getNewItemStack(); newItemStack = event.getNewItemStack();
items[slot] = newItemStack; items[slot] = newItemStack;
notifyWindows(); notifyWindows();
callAfterUpdateEvent(updateReason, slot, currentStack, newItemStack);
return newItemStack != null ? newItemStack.getAmount() : 0; return newItemStack != null ? newItemStack.getAmount() : 0;
} }
@ -718,10 +761,13 @@ public class VirtualInventory implements ConfigurationSerializable {
newStack.setAmount(amount - take); newStack.setAmount(amount - take);
} else newStack = null; } else newStack = null;
ItemUpdateEvent event = callUpdateEvent(updateReason, index, itemStack, newStack); ItemUpdateEvent event = callPreUpdateEvent(updateReason, index, itemStack, newStack);
if (!event.isCancelled()) { if (!event.isCancelled()) {
newStack = event.getNewItemStack();
items[index] = newStack; items[index] = newStack;
notifyWindows(); notifyWindows();
callAfterUpdateEvent(updateReason, index, itemStack, newStack);
return take; return take;
} }

@ -0,0 +1,34 @@
package de.studiocode.invui.virtualinventory.event;
import de.studiocode.invui.virtualinventory.VirtualInventory;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
// TODO: better solution?
/**
* An event that is called after the {@link VirtualInventory} has been updated.
*/
public class InventoryUpdatedEvent extends ItemUpdateEvent {
/**
* Creates a new {@link ItemUpdateEvent}.
*
* @param virtualInventory The {@link VirtualInventory} where this action takes place.
* @param updateReason The {@link UpdateReason} for the calling of this event.
* This will probably be a {@link PlayerUpdateReason} in most cases but can be a custom one
* if you called the methods in the {@link VirtualInventory} yourself.
* if it wasn't a {@link Player}
* @param slot The slot that is affected
* @param previousItemStack The {@link ItemStack} that was on that slot previously
* @param newItemStack The {@link ItemStack} that is on that slot now
*/
public InventoryUpdatedEvent(@NotNull VirtualInventory virtualInventory, int slot, @Nullable UpdateReason updateReason,
@Nullable ItemStack previousItemStack, @Nullable ItemStack newItemStack) {
super(virtualInventory, slot, updateReason, previousItemStack, newItemStack);
}
}

@ -9,14 +9,7 @@ import org.jetbrains.annotations.Nullable;
/** /**
* An event that is called whenever a slot inside a {@link VirtualInventory} gets updated. * An event that is called whenever a slot inside a {@link VirtualInventory} gets updated.
*/ */
public class ItemUpdateEvent { public class ItemUpdateEvent extends UpdateEvent {
private final VirtualInventory virtualInventory;
private final ItemStack previousItemStack;
private final UpdateReason updateReason;
private final int slot;
private ItemStack newItemStack;
private boolean cancelled;
/** /**
* Creates a new {@link ItemUpdateEvent}. * Creates a new {@link ItemUpdateEvent}.
@ -32,68 +25,11 @@ public class ItemUpdateEvent {
*/ */
public ItemUpdateEvent(@NotNull VirtualInventory virtualInventory, int slot, @Nullable UpdateReason updateReason, public ItemUpdateEvent(@NotNull VirtualInventory virtualInventory, int slot, @Nullable UpdateReason updateReason,
@Nullable ItemStack previousItemStack, @Nullable ItemStack newItemStack) { @Nullable ItemStack previousItemStack, @Nullable ItemStack newItemStack) {
this.virtualInventory = virtualInventory;
this.slot = slot; super(virtualInventory, slot, updateReason, previousItemStack, newItemStack);
this.updateReason = updateReason;
this.previousItemStack = previousItemStack;
this.newItemStack = newItemStack;
} }
/** private boolean cancelled;
* Gets the {@link VirtualInventory} where this action takes place.
*
* @return The {@link VirtualInventory}
*/
public VirtualInventory getVirtualInventory() {
return virtualInventory;
}
/**
* Gets the {@link UpdateReason} for the calling of this event.
*
* @return The reason why this event was called. Probably a {@link PlayerUpdateReason} in most cases.
*/
public UpdateReason getUpdateReason() {
return updateReason;
}
/**
* Gets the {@link ItemStack} that was there previously.
*
* @return The {@link ItemStack}
*/
public ItemStack getPreviousItemStack() {
return previousItemStack;
}
/**
* The new {@link ItemStack} that will be there if the event isn't cancelled.
*
* @return The new {@link ItemStack}
*/
public ItemStack getNewItemStack() {
return newItemStack;
}
/**
* Change the {@link ItemStack} that will appear in the {@link VirtualInventory}
* to a different one.
*
* @param newItemStack The {@link ItemStack} to appear in the {@link VirtualInventory}
* if the {@link ItemUpdateEvent} is not cancelled.
*/
public void setNewItemStack(@Nullable ItemStack newItemStack) {
this.newItemStack = newItemStack;
}
/**
* Gets the slot that is affected.
*
* @return The slot
*/
public int getSlot() {
return slot;
}
/** /**
* Gets the cancellation state of this event. * Gets the cancellation state of this event.
@ -113,64 +49,4 @@ public class ItemUpdateEvent {
this.cancelled = cancel; this.cancelled = cancel;
} }
/**
* Gets if the action resulted in items being added to
* the {@link VirtualInventory}.
*
* @return If items were added to the {@link VirtualInventory}
*/
public boolean isAdd() {
if (newItemStack != null && previousItemStack != null && newItemStack.isSimilar(previousItemStack)) {
return newItemStack.getAmount() > previousItemStack.getAmount();
} else return previousItemStack == null && newItemStack != null;
}
/**
* Gets if the action resulted in items being removed
* from the {@link VirtualInventory}.
*
* @return If items were removed from the {@link VirtualInventory}
*/
public boolean isRemove() {
if (newItemStack != null && previousItemStack != null && newItemStack.isSimilar(previousItemStack)) {
return newItemStack.getAmount() < previousItemStack.getAmount();
} else return newItemStack == null && previousItemStack != null;
}
/**
* Gets if the type of the {@link ItemStack} has changed.
* This does not account for an {@link ItemStack} turning null
* or being null previously, {@link #isRemove()} and {@link #isAdd()}
* should be used for that.
*
* @return If the type of the {@link ItemStack} has changed
*/
public boolean isSwap() {
return newItemStack != null && previousItemStack != null && !newItemStack.isSimilar(previousItemStack);
}
/**
* Gets the amount of items that have been removed.
*
* @return The amount of items that have been removed
* @throws IllegalStateException when {@link #isRemove()} is false
*/
public int getRemovedAmount() {
if (!isRemove()) throw new IllegalStateException("No items have been removed");
if (newItemStack == null) return previousItemStack.getAmount();
else return previousItemStack.getAmount() - newItemStack.getAmount();
}
/**
* Gets the amount of items that have been added.
*
* @return The amount of items that have been added
* @throws IllegalStateException when {@link #isAdd()} is false
*/
public int getAddedAmount() {
if (!isAdd()) throw new IllegalStateException("No items have been added");
if (previousItemStack == null) return newItemStack.getAmount();
else return newItemStack.getAmount() - previousItemStack.getAmount();
}
} }

@ -0,0 +1,155 @@
package de.studiocode.invui.virtualinventory.event;
import de.studiocode.invui.virtualinventory.VirtualInventory;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
abstract class UpdateEvent {
private final VirtualInventory virtualInventory;
private final ItemStack previousItemStack;
private final UpdateReason updateReason;
private final int slot;
private ItemStack newItemStack;
/**
* Creates a new {@link ItemUpdateEvent}.
*
* @param virtualInventory The {@link VirtualInventory} where this action takes place.
* @param updateReason The {@link UpdateReason} for the calling of this event.
* This will probably be a {@link PlayerUpdateReason} in most cases but can be a custom one
* if you called the methods in the {@link VirtualInventory} yourself.
* if it wasn't a {@link Player}
* @param slot The slot that is affected
* @param previousItemStack The {@link ItemStack} that was there previously
* @param newItemStack The {@link ItemStack} that will be there if the event isn't cancelled
*/
public UpdateEvent(@NotNull VirtualInventory virtualInventory, int slot, @Nullable UpdateReason updateReason,
@Nullable ItemStack previousItemStack, @Nullable ItemStack newItemStack) {
this.virtualInventory = virtualInventory;
this.slot = slot;
this.updateReason = updateReason;
this.previousItemStack = previousItemStack;
this.newItemStack = newItemStack;
}
/**
* Gets the {@link VirtualInventory} where this action takes place.
*
* @return The {@link VirtualInventory}
*/
public VirtualInventory getVirtualInventory() {
return virtualInventory;
}
/**
* Gets the {@link UpdateReason} for the calling of this event.
*
* @return The reason why this event was called. Probably a {@link PlayerUpdateReason} in most cases.
*/
public UpdateReason getUpdateReason() {
return updateReason;
}
/**
* Gets the {@link ItemStack} that was there previously.
*
* @return The {@link ItemStack}
*/
public ItemStack getPreviousItemStack() {
return previousItemStack;
}
/**
* The new {@link ItemStack} that will be there if the event isn't cancelled.
*
* @return The new {@link ItemStack}
*/
public ItemStack getNewItemStack() {
return newItemStack;
}
/**
* Change the {@link ItemStack} that will appear in the {@link VirtualInventory}
* to a different one.
*
* @param newItemStack The {@link ItemStack} to appear in the {@link VirtualInventory}
* if the {@link ItemUpdateEvent} is not cancelled.
*/
public void setNewItemStack(@Nullable ItemStack newItemStack) {
this.newItemStack = newItemStack;
}
/**
* Gets the slot that is affected.
*
* @return The slot
*/
public int getSlot() {
return slot;
}
/**
* Gets if the action resulted in items being added to
* the {@link VirtualInventory}.
*
* @return If items were added to the {@link VirtualInventory}
*/
public boolean isAdd() {
if (newItemStack != null && previousItemStack != null && newItemStack.isSimilar(previousItemStack)) {
return newItemStack.getAmount() > previousItemStack.getAmount();
} else return previousItemStack == null && newItemStack != null;
}
/**
* Gets if the action resulted in items being removed
* from the {@link VirtualInventory}.
*
* @return If items were removed from the {@link VirtualInventory}
*/
public boolean isRemove() {
if (newItemStack != null && previousItemStack != null && newItemStack.isSimilar(previousItemStack)) {
return newItemStack.getAmount() < previousItemStack.getAmount();
} else return newItemStack == null && previousItemStack != null;
}
/**
* Gets if the type of the {@link ItemStack} has changed.
* This does not account for an {@link ItemStack} turning null
* or being null previously, {@link #isRemove()} and {@link #isAdd()}
* should be used for that.
*
* @return If the type of the {@link ItemStack} has changed
*/
public boolean isSwap() {
return newItemStack != null && previousItemStack != null && !newItemStack.isSimilar(previousItemStack);
}
/**
* Gets the amount of items that have been removed.
*
* @return The amount of items that have been removed
* @throws IllegalStateException when {@link #isRemove()} is false
*/
public int getRemovedAmount() {
if (!isRemove()) throw new IllegalStateException("No items have been removed");
if (newItemStack == null) return previousItemStack.getAmount();
else return previousItemStack.getAmount() - newItemStack.getAmount();
}
/**
* Gets the amount of items that have been added.
*
* @return The amount of items that have been added
* @throws IllegalStateException when {@link #isAdd()} is false
*/
public int getAddedAmount() {
if (!isAdd()) throw new IllegalStateException("No items have been added");
if (previousItemStack == null) return newItemStack.getAmount();
else return newItemStack.getAmount() - previousItemStack.getAmount();
}
}