From ea0bb4441680fffe1000d3240e18787dcc28c9e1 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Mon, 21 Apr 2025 23:20:15 +0200 Subject: [PATCH] Add memoization support and implement equals/hashCode methods --- .../de/steamwar/inventory/ui/UIInventory.java | 40 ++++++++++++++ .../inventory/ui/fragments/ActionBar.java | 12 +++-- .../ui/fragments/HorizontalScrollLayout.java | 38 +++++++------ .../inventory/ui/fragments/PageLayout.java | 53 +++++++++++-------- .../inventory/ui/item/ItemBuilder.java | 19 +++++++ .../steamwar/inventory/ui/item/OnClick.java | 17 +++++- .../de/steamwar/inventory/ui/item/Space.java | 5 ++ .../de/steamwar/inventory/ui/item/UIItem.java | 17 ++++++ .../inventory/ui/util/RenderContext.java | 11 ++++ 9 files changed, 168 insertions(+), 44 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/UIInventory.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/UIInventory.java index a86c680c..6694f926 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/UIInventory.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/UIInventory.java @@ -26,6 +26,7 @@ import de.steamwar.inventory.ui.util.RenderItem; import de.steamwar.inventory.ui.util.Size; import de.steamwar.message.Message; import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.Setter; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -38,6 +39,7 @@ import org.bukkit.inventory.ItemStack; import java.util.*; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; public abstract class UIInventory { @@ -49,6 +51,9 @@ public abstract class UIInventory { private Player currentRender; private final int playerSlots; + private List> memoHooks = new ArrayList<>(); + private int buildHookCount = 0; + protected UIInventory() { this(0); } @@ -63,6 +68,31 @@ public abstract class UIInventory { void onClose() { } + public T useMemo(Supplier supplier, List dependencies) { + int currentHook = buildHookCount++; + + if (currentHook >= memoHooks.size()) { + T value = supplier.get(); + memoHooks.add(new MemoHookMeta<>(dependencies, value)); + return value; + } + + MemoHookMeta hook = (MemoHookMeta) memoHooks.get(currentHook); + + if (!hook.getDependencies().equals(dependencies)) { + hook.setDependencies(dependencies); + T value = supplier.get(); + hook.setValue(value); + return value; + } + + return hook.getValue(); + } + + public T useMemo(Supplier supplier) { + return useMemo(supplier, Collections.emptyList()); + } + public void addViewer(Player... players) { viewers.addAll(Arrays.asList(players)); @@ -92,7 +122,9 @@ public abstract class UIInventory { int invSize = inventoryHeight * width; currentRender = player; + buildHookCount = 0; ComponentRender render = render().render(new RenderContext(new Size(width, height), new Size(width, height), this)); + buildHookCount = -1; currentRender = null; if (render.getSize().getWidth() > width || render.getSize().getHeight() > height) { @@ -199,4 +231,12 @@ public abstract class UIInventory { onClose(); } } + + @Getter + @Setter + @AllArgsConstructor + private class MemoHookMeta { + private List dependencies; + private T value; + } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/ActionBar.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/ActionBar.java index e4404c85..d2f0fd65 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/ActionBar.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/ActionBar.java @@ -52,10 +52,14 @@ public class ActionBar extends UIFragment { @Override public UIComponent build(RenderContext context) { - UIComponent[] components = new UIComponent[context.getContainerSize().getWidth()]; - for (int i = 0; i < components.length; i++) { - components[i] = actionBar.getOrDefault(i, filler); - } + UIComponent[] components = context.useMemo(() -> { + UIComponent[] cmp = new UIComponent[context.getContainerSize().getWidth()]; + for (int i = 0; i < cmp.length; i++) { + cmp[i] = actionBar.getOrDefault(i, filler); + } + + return cmp; + }, Arrays.asList(actionBar)); return new Row(Arrays.asList(components)).size(new Size(context.getContainerSize().getWidth(), 1)); } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/HorizontalScrollLayout.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/HorizontalScrollLayout.java index 456bf46c..b415952b 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/HorizontalScrollLayout.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/HorizontalScrollLayout.java @@ -64,24 +64,28 @@ public class HorizontalScrollLayout extends UIFragment { boolean hasPrev = scroll > 0; boolean hasNext = data.size() - scroll > 9; - Map actionBar = new HashMap<>(this.actionBar); + Map actionBar = context.useMemo(() -> { + Map content = new HashMap<>(this.actionBar); - actionBar.put(0, new UIItem(ItemBuilder.color(hasPrev ? DyeColor.LIME : DyeColor.GRAY).message(Core.MESSAGE).translateName(hasPrev ? "SWLISINV_PREVIOUS_PAGE_ACTIVE" : "SWLISINV_PREVIOUS_PAGE_INACTIVE")).onClickWithUpdate(inventoryClickEvent -> { - if (scroll > 0) { - scroll = Math.max(0, scroll - 9); - return true; - } else { - return false; - } - })); - actionBar.put(8, new UIItem(ItemBuilder.color(hasNext ? DyeColor.LIME : DyeColor.GRAY).message(Core.MESSAGE).translateName(hasNext ? "SWLISINV_NEXT_PAGE_ACTIVE" : "SWLISINV_NEXT_PAGE_INACTIVE")).onClickWithUpdate(inventoryClickEvent -> { - if (hasNext) { - scroll = Math.min(scroll + 9, data.size() - 9); - return true; - } else { - return false; - } - })); + content.put(0, new UIItem(ItemBuilder.color(hasPrev ? DyeColor.LIME : DyeColor.GRAY).message(Core.MESSAGE).translateName(hasPrev ? "SWLISINV_PREVIOUS_PAGE_ACTIVE" : "SWLISINV_PREVIOUS_PAGE_INACTIVE")).onClickWithUpdate(inventoryClickEvent -> { + if (scroll > 0) { + scroll = Math.max(0, scroll - 9); + return true; + } else { + return false; + } + })); + content.put(8, new UIItem(ItemBuilder.color(hasNext ? DyeColor.LIME : DyeColor.GRAY).message(Core.MESSAGE).translateName(hasNext ? "SWLISINV_NEXT_PAGE_ACTIVE" : "SWLISINV_NEXT_PAGE_INACTIVE")).onClickWithUpdate(inventoryClickEvent -> { + if (hasNext) { + scroll = Math.min(scroll + 9, data.size() - 9); + return true; + } else { + return false; + } + })); + + return content; + }, Arrays.asList(hasPrev, hasNext)); return new Column( new FixedSizeContainer(context.getContainerSize().shrink(0, 1), diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/PageLayout.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/PageLayout.java index d17a83ba..429cd04d 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/PageLayout.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/PageLayout.java @@ -55,35 +55,44 @@ public class PageLayout extends UIFragment { @Override public UIComponent build(RenderContext context) { - List uiItems = new ArrayList<>(); int itemsOnPage = context.getContainerSize().getWidth() * (context.getContainerSize().getHeight() - 1); - int ipageLimit = items.size() - page * itemsOnPage; - if (ipageLimit > itemsOnPage) { - ipageLimit = itemsOnPage; - } + List uiItems = context.useMemo(() -> { + int i = page * itemsOnPage; - int i = page * itemsOnPage; - for (int ipage = 0; ipage < ipageLimit; ipage++) { - UIListEntry e = items.get(i); + int ipageLimit = items.size() - page * itemsOnPage; + if (ipageLimit > itemsOnPage) { + ipageLimit = itemsOnPage; + } - uiItems.add(new UIItem(e.item).onClickNoUpdate(inventoryClickEvent -> callback.clicked(inventoryClickEvent.getClick(), e.object))); - i++; - } + List pageItems = new ArrayList<>(); + for (int ipage = 0; ipage < ipageLimit; ipage++) { + UIListEntry e = items.get(i); - Map uiBar = new HashMap<>(itemsOnBar); + pageItems.add(new UIItem(e.item).onClickNoUpdate(inventoryClickEvent -> callback.clicked(inventoryClickEvent.getClick(), e.object))); + i++; + } - if (page != 0) { - uiBar.put(0, new UIItem(ItemBuilder.color(DyeColor.LIME).message(Core.MESSAGE).translateName("SWLISINV_PREVIOUS_PAGE_ACTIVE")).onClick(inventoryClickEvent -> page--)); - } else { - uiBar.put(0, new UIItem(ItemBuilder.color(DyeColor.GRAY).message(Core.MESSAGE).translateName("SWLISINV_PREVIOUS_PAGE_INACTIVE")).cancelOnClick()); - } - if (page < items.size() / itemsOnPage - (items.size() % itemsOnPage == 0 ? 1 : 0)) { - uiBar.put(8, new UIItem(ItemBuilder.color(DyeColor.LIME).message(Core.MESSAGE).translateName("SWLISINV_NEXT_PAGE_ACTIVE")).onClick(inventoryClickEvent -> page++)); - } else { - uiBar.put(8, new UIItem(ItemBuilder.color(DyeColor.GRAY).message(Core.MESSAGE).translateName("SWLISINV_NEXT_PAGE_INACTIVE")).cancelOnClick()); - } + return pageItems; + }, Arrays.asList(page, items.size(), itemsOnPage)); + + Map uiBar = context.useMemo(() -> { + Map content = new HashMap<>(itemsOnBar); + + if (page != 0) { + content.put(0, new UIItem(ItemBuilder.color(DyeColor.LIME).message(Core.MESSAGE).translateName("SWLISINV_PREVIOUS_PAGE_ACTIVE")).onClick(inventoryClickEvent -> page--)); + } else { + content.put(0, new UIItem(ItemBuilder.color(DyeColor.GRAY).message(Core.MESSAGE).translateName("SWLISINV_PREVIOUS_PAGE_INACTIVE")).cancelOnClick()); + } + if (page < items.size() / itemsOnPage - (items.size() % itemsOnPage == 0 ? 1 : 0)) { + content.put(8, new UIItem(ItemBuilder.color(DyeColor.LIME).message(Core.MESSAGE).translateName("SWLISINV_NEXT_PAGE_ACTIVE")).onClick(inventoryClickEvent -> page++)); + } else { + content.put(8, new UIItem(ItemBuilder.color(DyeColor.GRAY).message(Core.MESSAGE).translateName("SWLISINV_NEXT_PAGE_INACTIVE")).cancelOnClick()); + } + + return content; + }, Arrays.asList(page, items.size(), itemsOnPage)); return new Column( new FixedSizeContainer(context.getContainerSize().shrink(0, 1), diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/ItemBuilder.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/ItemBuilder.java index 351b4fe6..ac4dbe90 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/ItemBuilder.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/ItemBuilder.java @@ -183,4 +183,23 @@ public class ItemBuilder { itemMeta.setCustomModelData(modelData); return this; } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + + ItemBuilder that = (ItemBuilder) o; + return Objects.equals(itemStack, that.itemStack) && Objects.equals(itemMeta, that.itemMeta) && Objects.equals(message, that.message) && Objects.equals(translateName, that.translateName) && Objects.equals(translateLore, that.translateLore); + } + + @Override + public String toString() { + return "ItemBuilder{" + + "itemStack=" + itemStack + + ", itemMeta=" + itemMeta + + ", message=" + message + + ", translateName=" + translateName + + ", translateLore=" + translateLore + + '}'; + } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/OnClick.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/OnClick.java index f6d3e203..3199c3ae 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/OnClick.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/OnClick.java @@ -26,7 +26,6 @@ import de.steamwar.inventory.ui.util.RenderItem; import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.InventoryClickEvent; -import java.util.function.Consumer; import java.util.function.Function; public class OnClick implements UIComponent { @@ -56,4 +55,20 @@ public class OnClick implements UIComponent { return render; } + + @Override + public String toString() { + return "OnClick{" + + "children=" + children + + ", allowDoubleClick=" + allowDoubleClick + + '}'; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + + OnClick onClick = (OnClick) o; + return allowDoubleClick == onClick.allowDoubleClick && children.equals(onClick.children); + } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/Space.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/Space.java index d947966c..6739b50e 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/Space.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/Space.java @@ -34,4 +34,9 @@ public class Space implements UIComponent { new Size(1, 1) ); } + + @Override + public boolean equals(Object obj) { + return obj instanceof Space; + } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/UIItem.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/UIItem.java index cdc7de01..44143727 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/UIItem.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/UIItem.java @@ -21,9 +21,11 @@ package de.steamwar.inventory.ui.item; import de.steamwar.inventory.ui.UIComponent; import de.steamwar.inventory.ui.util.*; +import lombok.EqualsAndHashCode; import org.bukkit.inventory.ItemStack; import java.util.Arrays; +import java.util.Objects; public class UIItem implements UIComponent { @@ -44,4 +46,19 @@ public class UIItem implements UIComponent { new Size(1, 1) ); } + + @Override + public String toString() { + return "UIItem{" + + "itemStack=" + itemStack + + '}'; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + + UIItem uiItem = (UIItem) o; + return Objects.equals(itemStack, uiItem.itemStack); + } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/RenderContext.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/RenderContext.java index 910415f0..67420961 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/RenderContext.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/RenderContext.java @@ -23,10 +23,21 @@ import de.steamwar.inventory.ui.UIInventory; import lombok.Data; import lombok.With; +import java.util.List; +import java.util.function.Supplier; + @Data @With public class RenderContext { private final Size inventorySize; private final Size containerSize; private final UIInventory inventory; + + public T useMemo(Supplier supplier, List dependencies) { + return inventory.useMemo(supplier, dependencies); + } + + public T useMemo(Supplier supplier) { + return inventory.useMemo(supplier); + } }