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) {
if (clicked == null) return;
ItemStack previousStack = clicked.clone();
UpdateReason updateReason = new PlayerUpdateReason(player, event);
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()) {
int leftOverAmount;
@ -232,6 +234,8 @@ public abstract class BaseGUI implements GUI, Controllable {
clicked.setAmount(leftOverAmount);
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.gui.GUI;
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.UpdateReason;
import de.studiocode.invui.window.Window;
@ -31,6 +32,7 @@ public class VirtualInventory implements ConfigurationSerializable {
private ItemStack[] items;
private int[] stackSizes;
private Consumer<ItemUpdateEvent> itemUpdateHandler;
private Consumer<InventoryUpdatedEvent> inventoryUpdatedHandler;
private int guiShiftPriority = 0;
/**
@ -166,6 +168,15 @@ public class VirtualInventory implements ConfigurationSerializable {
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}
*
@ -225,6 +236,16 @@ public class VirtualInventory implements ConfigurationSerializable {
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.
*
@ -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
* and it's own maximum stack size.
* and its own maximum stack size.
*
* @param slot The slot
* @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
* @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);
if (itemUpdateHandler != null) itemUpdateHandler.accept(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
* as the assumed {@link ItemStack} provided as parameter.
@ -389,9 +425,12 @@ public class VirtualInventory implements ConfigurationSerializable {
* @return If the action was successful
*/
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()) {
setItemStackSilently(slot, event.getNewItemStack());
ItemStack newStack = event.getNewItemStack();
setItemStackSilently(slot, newStack);
callAfterUpdateEvent(updateReason, slot, previousStack, newStack);
return true;
}
return false;
@ -432,12 +471,14 @@ public class VirtualInventory implements ConfigurationSerializable {
ItemStack newItemStack = itemStack.clone();
newItemStack.setAmount(min(currentAmount + itemStack.getAmount(), maxStackSize));
ItemUpdateEvent event = callUpdateEvent(updateReason, slot, currentStack, newItemStack);
ItemUpdateEvent event = callPreUpdateEvent(updateReason, slot, currentStack, newItemStack);
if (!event.isCancelled()) {
newItemStack = event.getNewItemStack();
items[slot] = newItemStack;
notifyWindows();
callAfterUpdateEvent(updateReason, slot, currentStack, newItemStack);
int newAmount = newItemStack != null ? newItemStack.getAmount() : 0;
return itemStack.getAmount() - (newAmount - currentAmount);
}
@ -470,12 +511,14 @@ public class VirtualInventory implements ConfigurationSerializable {
newItemStack = null;
}
ItemUpdateEvent event = callUpdateEvent(updateReason, slot, currentStack, newItemStack);
ItemUpdateEvent event = callPreUpdateEvent(updateReason, slot, currentStack, newItemStack);
if (!event.isCancelled()) {
newItemStack = event.getNewItemStack();
items[slot] = newItemStack;
notifyWindows();
callAfterUpdateEvent(updateReason, slot, currentStack, newItemStack);
return newItemStack != null ? newItemStack.getAmount() : 0;
}
@ -718,10 +761,13 @@ public class VirtualInventory implements ConfigurationSerializable {
newStack.setAmount(amount - take);
} else newStack = null;
ItemUpdateEvent event = callUpdateEvent(updateReason, index, itemStack, newStack);
ItemUpdateEvent event = callPreUpdateEvent(updateReason, index, itemStack, newStack);
if (!event.isCancelled()) {
newStack = event.getNewItemStack();
items[index] = newStack;
notifyWindows();
callAfterUpdateEvent(updateReason, index, itemStack, newStack);
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.
*/
public class ItemUpdateEvent {
private final VirtualInventory virtualInventory;
private final ItemStack previousItemStack;
private final UpdateReason updateReason;
private final int slot;
private ItemStack newItemStack;
private boolean cancelled;
public class ItemUpdateEvent extends UpdateEvent {
/**
* Creates a new {@link ItemUpdateEvent}.
@ -32,68 +25,11 @@ public class ItemUpdateEvent {
*/
public ItemUpdateEvent(@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;
super(virtualInventory, slot, updateReason, previousItemStack, 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;
}
private boolean cancelled;
/**
* Gets the cancellation state of this event.
@ -113,64 +49,4 @@ public class ItemUpdateEvent {
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();
}
}