From d4f815de8d5e869250c981dc90d32c4661f0a201 Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Fri, 6 Sep 2019 05:36:20 -0500 Subject: [PATCH 01/11] correct valid items --- .../java/com/songoda/core/compatibility/LegacyMaterials.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/songoda/core/compatibility/LegacyMaterials.java b/src/main/java/com/songoda/core/compatibility/LegacyMaterials.java index e74dbc4e..9bfc17c9 100644 --- a/src/main/java/com/songoda/core/compatibility/LegacyMaterials.java +++ b/src/main/java/com/songoda/core/compatibility/LegacyMaterials.java @@ -1952,8 +1952,8 @@ public enum LegacyMaterials { case PURPLE_WALL_BANNER: case REDSTONE_WALL_TORCH: case REDSTONE_WIRE: - case RED_TULIP: - case SKELETON_SPAWN_EGG: + case RED_WALL_BANNER: + case SKELETON_WALL_SKULL: case SPRUCE_WALL_SIGN: case SWEET_BERRY_BUSH: case TALL_SEAGRASS: From 677941e1b343f6a9ac4a8fa1acb716443a375e02 Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Fri, 6 Sep 2019 05:43:30 -0500 Subject: [PATCH 02/11] fix simple page items overwriting with navigation buttons --- .../com/songoda/core/gui/SimplePagedGui.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/songoda/core/gui/SimplePagedGui.java b/src/main/java/com/songoda/core/gui/SimplePagedGui.java index bc873234..b753da18 100644 --- a/src/main/java/com/songoda/core/gui/SimplePagedGui.java +++ b/src/main/java/com/songoda/core/gui/SimplePagedGui.java @@ -26,7 +26,7 @@ public class SimplePagedGui extends Gui { private int rowsPerPage, maxCellSlot; protected ItemStack headerBackItem; protected ItemStack footerBackItem; - final int nextPageIndex = -4, prevPageIndex = -6; + final int nextPageIndex = 4, prevPageIndex = 6; public SimplePagedGui() { this(null); @@ -80,7 +80,7 @@ public class SimplePagedGui extends Gui { @Override public SimplePagedGui setItem(int cell, ItemStack item) { // set the cell relative to the current page - int cellIndex = page == 1 || (useHeader && cell < 9) ? cell : (cell + (page - 1) * (rowsPerPage * 9)); + int cellIndex = cell < 0 ? cell : (page == 1 || (useHeader && cell < 9) ? cell : (cell + (page - 1) * (rowsPerPage * 9))); cellItems.put(cellIndex, item); if (open && cell >= 0 && cell < inventory.getSize()) { @@ -120,20 +120,20 @@ public class SimplePagedGui extends Gui { @Override protected void updatePageNavigation() { if (page > 1) { - inventory.setItem((rows * 9) - 6, prevPage); - this.setButton(prevPageIndex, prevPage, ClickType.LEFT, (event) -> this.prevPage(event.manager)); + inventory.setItem(inventory.getSize() - prevPageIndex, prevPage); + this.setButton(-prevPageIndex, prevPage, ClickType.LEFT, (event) -> this.prevPage(event.manager)); } else { - inventory.setItem((rows * 9) - 6, footerBackItem != null ? footerBackItem : blankItem); - this.setItem(prevPageIndex, null); - this.clearActions(prevPageIndex); + inventory.setItem(inventory.getSize() - prevPageIndex, footerBackItem != null ? footerBackItem : blankItem); + this.setItem(-prevPageIndex, null); + this.clearActions(-prevPageIndex); } if (pages > 1 && page != pages) { - inventory.setItem((rows * 9) - 4, nextPage); - this.setButton(nextPageIndex, nextPage, ClickType.LEFT, (event) -> this.nextPage(event.manager)); + inventory.setItem(inventory.getSize() - nextPageIndex, nextPage); + this.setButton(-nextPageIndex, nextPage, ClickType.LEFT, (event) -> this.nextPage(event.manager)); } else { - inventory.setItem((rows * 9) - 4, footerBackItem != null ? footerBackItem : blankItem); - this.setItem(nextPageIndex, null); - this.clearActions(nextPageIndex); + inventory.setItem(inventory.getSize() - nextPageIndex, footerBackItem != null ? footerBackItem : blankItem); + this.setItem(-nextPageIndex, null); + this.clearActions(-nextPageIndex); } } From cb4db2bb3448d738db8d7e9e78b665bd05c780fb Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Fri, 6 Sep 2019 09:57:02 -0500 Subject: [PATCH 03/11] add to valid items list --- src/main/java/com/songoda/core/SongodaCore.java | 2 +- .../java/com/songoda/core/compatibility/LegacyMaterials.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/songoda/core/SongodaCore.java b/src/main/java/com/songoda/core/SongodaCore.java index e5ff5fae..2101161e 100644 --- a/src/main/java/com/songoda/core/SongodaCore.java +++ b/src/main/java/com/songoda/core/SongodaCore.java @@ -46,7 +46,7 @@ public class SongodaCore { * Whenever we make a major change to the core GUI, updater, * or other function used by the core, increment this number */ - private final static int coreRevision = 2; + private final static int coreRevision = 3; private final static int updaterVersion = 1; private final static Set registeredPlugins = new HashSet<>(); diff --git a/src/main/java/com/songoda/core/compatibility/LegacyMaterials.java b/src/main/java/com/songoda/core/compatibility/LegacyMaterials.java index 9bfc17c9..43d20647 100644 --- a/src/main/java/com/songoda/core/compatibility/LegacyMaterials.java +++ b/src/main/java/com/songoda/core/compatibility/LegacyMaterials.java @@ -1970,6 +1970,8 @@ public enum LegacyMaterials { switch (this) { case ACACIA_WOOD: case BIRCH_WOOD: + case BREWING_STAND: + case CAULDRON: case DARK_OAK_WOOD: case JUNGLE_WOOD: case OAK_WOOD: From 2488e97ba659232664bdf43a801d374582d3d2e5 Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Fri, 6 Sep 2019 11:33:40 -0500 Subject: [PATCH 04/11] add simple file storage for objects --- .../songoda/core/configuration/Config.java | 15 +- .../core/configuration/DataStoreObject.java | 35 +++ .../core/configuration/SimpleDataStore.java | 220 ++++++++++++++++++ 3 files changed, 267 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/songoda/core/configuration/DataStoreObject.java create mode 100644 src/main/java/com/songoda/core/configuration/SimpleDataStore.java diff --git a/src/main/java/com/songoda/core/configuration/Config.java b/src/main/java/com/songoda/core/configuration/Config.java index 0f929ebb..a803922c 100644 --- a/src/main/java/com/songoda/core/configuration/Config.java +++ b/src/main/java/com/songoda/core/configuration/Config.java @@ -126,16 +126,18 @@ public class Config extends ConfigSection { fileName = file; } - public Config(@NotNull Plugin plugin, @NotNull String directory, @NotNull String file) { + public Config(@NotNull Plugin plugin, @Nullable String directory, @NotNull String file) { this.plugin = plugin; dirName = directory; fileName = file; } + @NotNull public ConfigFileConfigurationAdapter getFileConfig() { return config; } + @NotNull public File getFile() { if (file == null) { if (dirName != null) { @@ -175,6 +177,7 @@ public class Config extends ConfigSection { * @param autosaveInterval time in seconds * @return this class */ + @NotNull public Config setAutosaveInterval(int autosaveInterval) { this.autosaveInterval = autosaveInterval; return this; @@ -202,6 +205,7 @@ public class Config extends ConfigSection { /** * Default comment applied to config nodes */ + @Nullable public ConfigFormattingRules.CommentStyle getDefaultNodeCommentFormat() { return defaultNodeCommentFormat; } @@ -211,7 +215,8 @@ public class Config extends ConfigSection { * * @return this config */ - public Config setDefaultNodeCommentFormat(ConfigFormattingRules.CommentStyle defaultNodeCommentFormat) { + @NotNull + public Config setDefaultNodeCommentFormat(@Nullable ConfigFormattingRules.CommentStyle defaultNodeCommentFormat) { this.defaultNodeCommentFormat = defaultNodeCommentFormat; return this; } @@ -219,6 +224,7 @@ public class Config extends ConfigSection { /** * Default comment applied to section nodes */ + @Nullable public ConfigFormattingRules.CommentStyle getDefaultSectionCommentFormat() { return defaultSectionCommentFormat; } @@ -228,7 +234,8 @@ public class Config extends ConfigSection { * * @return this config */ - public Config setDefaultSectionCommentFormat(ConfigFormattingRules.CommentStyle defaultSectionCommentFormat) { + @NotNull + public Config setDefaultSectionCommentFormat(@Nullable ConfigFormattingRules.CommentStyle defaultSectionCommentFormat) { this.defaultSectionCommentFormat = defaultSectionCommentFormat; return this; } @@ -245,6 +252,7 @@ public class Config extends ConfigSection { * * @return this config */ + @NotNull public Config setRootNodeSpacing(int rootNodeSpacing) { this.rootNodeSpacing = rootNodeSpacing; return this; @@ -264,6 +272,7 @@ public class Config extends ConfigSection { * * @return this config */ + @NotNull public Config setCommentSpacing(int commentSpacing) { this.commentSpacing = commentSpacing; return this; diff --git a/src/main/java/com/songoda/core/configuration/DataStoreObject.java b/src/main/java/com/songoda/core/configuration/DataStoreObject.java new file mode 100644 index 00000000..5c1c573b --- /dev/null +++ b/src/main/java/com/songoda/core/configuration/DataStoreObject.java @@ -0,0 +1,35 @@ +package com.songoda.core.configuration; + +import org.bukkit.configuration.ConfigurationSection; + +public interface DataStoreObject { + + /** + * @return a unique hashable instance of T to store this value under + */ + public abstract T getKey(); + + /** + * @return a unique identifier for saving this value with + */ + public abstract String getConfigKey(); + + /** + * Save this data to a ConfigurationSection + * + * @param sec + */ + public abstract void saveToSection(ConfigurationSection sec); + + /** + * @return true if this data has changed from the state saved to file + */ + public boolean hasChanged(); + + /** + * Mark this data as needing a save or not + * + * @param isChanged + */ + public void setChanged(boolean isChanged); +} diff --git a/src/main/java/com/songoda/core/configuration/SimpleDataStore.java b/src/main/java/com/songoda/core/configuration/SimpleDataStore.java new file mode 100644 index 00000000..9e3e3020 --- /dev/null +++ b/src/main/java/com/songoda/core/configuration/SimpleDataStore.java @@ -0,0 +1,220 @@ +package com.songoda.core.configuration; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.function.Function; +import java.util.logging.Level; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Used to easily store a set of one data value + * + * @param Key value that DataObject class uses to uniquely identify values + * @param DataObject class that is used to store the data + * @since 2019-09-06 + * @author jascotty2 + */ +public class SimpleDataStore> { + + protected final Plugin plugin; + protected final String filename, dirName; + private final Function getFromSection; + protected final HashMap data = new HashMap(); + private File file; + private final Object lock = new Object(); + SaveTask saveTask; + Timer autosaveTimer; + /** + * time in seconds to start a save after a change is made + */ + int autosaveInterval = 60; + + public SimpleDataStore(@NotNull Plugin plugin, @NotNull String filename, Function loadFunction) { + this.plugin = plugin; + this.filename = filename; + dirName = null; + this.getFromSection = loadFunction; + } + + public SimpleDataStore(@NotNull Plugin plugin, @Nullable String directory, @NotNull String filename, @NotNull Function loadFunction) { + this.plugin = plugin; + this.filename = filename; + this.dirName = directory; + this.getFromSection = loadFunction; + } + + @NotNull + public File getFile() { + if (file == null) { + if (dirName != null) { + this.file = new File(plugin.getDataFolder() + dirName, filename != null ? filename : "data.yml"); + } else { + this.file = new File(plugin.getDataFolder(), filename != null ? filename : "data.yml"); + } + } + return file; + } + + /** + * @return a directly-modifiable instance of the data mapping for this storage + */ + public Map getData() { + return data; + } + + /** + * Returns the value to which the specified key is mapped, + * or {@code null} if this map contains no mapping for the key. + * + * @param key key whose mapping is to be retrieved from this storage + * @return the value associated with key, or + * null if there was no mapping for key. + */ + @Nullable + public T get(K key) { + return data.get(key); + } + + /** + * Removes the mapping for the specified key from this storage if present. + * + * @param key key whose mapping is to be removed from this storage + * @return the previous value associated with key, or + * null if there was no mapping for key. + */ + @Nullable + public T remove(@NotNull K key) { + T temp; + synchronized (lock) { + temp = data.remove(key); + } + save(); + return temp; + } + + /** + * Removes the mapping for the specified key from this storage if present. + * + * @param value value whose mapping is to be removed from this storage + * @return the previous value associated with key, or + * null if there was no mapping for key. + */ + @Nullable + public T remove(@NotNull T value) { + if (value == null) { + return null; + } + T temp; + synchronized (lock) { + temp = data.remove(value.getKey()); + } + save(); + return temp; + } + + /** + * Adds the specified value in this storage. If the map previously contained + * a mapping for the key, the old value is replaced. + * + * @param value value to be added + * @return the previous value associated with value.getKey(), or + * null if there was no mapping for value.getKey(). + */ + @Nullable + public T add(@NotNull T value) { + if (value == null) { + return null; + } + T temp; + synchronized (lock) { + temp = data.put(value.getKey(), value); + } + save(); + return temp; + } + + /** + * Load data from the associated file + */ + public void load() { + if (!getFile().exists()) { + return; + } + try { + YamlConfiguration f = new YamlConfiguration(); + f.options().pathSeparator('\0'); + f.load(file); + + synchronized (lock) { + data.clear(); + f.getValues(false).entrySet().stream() + .filter(d -> d.getValue() instanceof ConfigurationSection) + .map(Map.Entry::getValue) + .map(v -> getFromSection.apply((ConfigurationSection) v)) + .forEach(v -> data.put(v.getKey(), v)); + } + } catch (IOException | InvalidConfigurationException ex) { + plugin.getLogger().log(Level.SEVERE, "Failed to load data from " + file.getName(), ex); + } + } + + /** + * Optionally save this storage's data to file if there have been changes made + */ + public void saveChanges() { + if (saveTask != null || data.values().stream().anyMatch(v -> v.hasChanged())) { + flushSave(); + } + } + + /** + * Save this file data. This saves later asynchronously. + */ + public void save() { + // save async even if no plugin or if plugin disabled + if (saveTask == null) { + autosaveTimer = new Timer((plugin != null ? plugin.getName() + "-DataStoreSave-" : "DataStoreSave-") + getFile().getName()); + autosaveTimer.schedule(saveTask = new SaveTask(), autosaveInterval * 1000L); + } + } + + /** + * Force a new save of this storage's data + */ + public void flushSave() { + if (saveTask != null) { + //Close Threads + saveTask.cancel(); + autosaveTimer.cancel(); + saveTask = null; + autosaveTimer = null; + } + YamlConfiguration f = new YamlConfiguration(); + synchronized (lock) { + data.values().stream().forEach(e -> e.saveToSection(f.createSection(e.getConfigKey()))); + } + try { + f.save(getFile()); + data.values().stream().forEach(e -> e.setChanged(false)); + } catch (IOException ex) { + plugin.getLogger().log(Level.SEVERE, "Failed to save data to " + file.getName(), ex); + } + } + + class SaveTask extends TimerTask { + + @Override + public void run() { + flushSave(); + } + } +} From 72187d9efe8ee969d90348ebe185a18a9d24c20c Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Fri, 6 Sep 2019 13:37:30 -0500 Subject: [PATCH 05/11] add a user-customizable gui --- .../core/configuration/SimpleDataStore.java | 66 +++- .../com/songoda/core/gui/CustomizableGui.java | 337 ++++++++++++++++++ src/main/java/com/songoda/core/gui/Gui.java | 203 ++++++++--- .../com/songoda/core/gui/SimplePagedGui.java | 10 - 4 files changed, 536 insertions(+), 80 deletions(-) create mode 100644 src/main/java/com/songoda/core/gui/CustomizableGui.java diff --git a/src/main/java/com/songoda/core/configuration/SimpleDataStore.java b/src/main/java/com/songoda/core/configuration/SimpleDataStore.java index 9e3e3020..3eb143ed 100644 --- a/src/main/java/com/songoda/core/configuration/SimpleDataStore.java +++ b/src/main/java/com/songoda/core/configuration/SimpleDataStore.java @@ -2,6 +2,7 @@ package com.songoda.core.configuration; import java.io.File; import java.io.IOException; +import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Timer; @@ -18,17 +19,16 @@ import org.jetbrains.annotations.Nullable; /** * Used to easily store a set of one data value * - * @param Key value that DataObject class uses to uniquely identify values * @param DataObject class that is used to store the data * @since 2019-09-06 * @author jascotty2 */ -public class SimpleDataStore> { +public class SimpleDataStore { protected final Plugin plugin; protected final String filename, dirName; private final Function getFromSection; - protected final HashMap data = new HashMap(); + protected final HashMap data = new HashMap(); private File file; private final Object lock = new Object(); SaveTask saveTask; @@ -38,7 +38,7 @@ public class SimpleDataStore> { */ int autosaveInterval = 60; - public SimpleDataStore(@NotNull Plugin plugin, @NotNull String filename, Function loadFunction) { + public SimpleDataStore(@NotNull Plugin plugin, @NotNull String filename, @NotNull Function loadFunction) { this.plugin = plugin; this.filename = filename; dirName = null; @@ -65,22 +65,23 @@ public class SimpleDataStore> { } /** - * @return a directly-modifiable instance of the data mapping for this storage + * @return a directly-modifiable instance of the data mapping for this + * storage */ - public Map getData() { + public Map getData() { return data; } /** - * Returns the value to which the specified key is mapped, - * or {@code null} if this map contains no mapping for the key. + * Returns the value to which the specified key is mapped, or {@code null} + * if this map contains no mapping for the key. * * @param key key whose mapping is to be retrieved from this storage * @return the value associated with key, or * null if there was no mapping for key. */ @Nullable - public T get(K key) { + public T get(Object key) { return data.get(key); } @@ -92,7 +93,7 @@ public class SimpleDataStore> { * null if there was no mapping for key. */ @Nullable - public T remove(@NotNull K key) { + public T remove(@NotNull Object key) { T temp; synchronized (lock) { temp = data.remove(key); @@ -142,6 +143,48 @@ public class SimpleDataStore> { return temp; } + /** + * Adds the specified value in this storage. If the map previously contained + * a mapping for the key, the old value is replaced. + * + * @param value values to be added + */ + @Nullable + public void addAll(@NotNull T[] value) { + if (value == null) { + return; + } + synchronized (lock) { + for (int i = 0; i < value.length; ++i) { + if (value[i] != null) { + data.put(value[i].getKey(), value[i]); + } + } + } + save(); + } + + /** + * Adds the specified value in this storage. If the map previously contained + * a mapping for the key, the old value is replaced. + * + * @param value values to be added + */ + @Nullable + public void addAll(@NotNull Collection value) { + if (value == null) { + return; + } + synchronized (lock) { + for (T v : value) { + if (v != null) { + data.put(v.getKey(), v); + } + } + } + save(); + } + /** * Load data from the associated file */ @@ -168,7 +211,8 @@ public class SimpleDataStore> { } /** - * Optionally save this storage's data to file if there have been changes made + * Optionally save this storage's data to file if there have been changes + * made */ public void saveChanges() { if (saveTask != null || data.values().stream().anyMatch(v -> v.hasChanged())) { diff --git a/src/main/java/com/songoda/core/gui/CustomizableGui.java b/src/main/java/com/songoda/core/gui/CustomizableGui.java new file mode 100644 index 00000000..63589464 --- /dev/null +++ b/src/main/java/com/songoda/core/gui/CustomizableGui.java @@ -0,0 +1,337 @@ +package com.songoda.core.gui; + +import com.songoda.core.compatibility.LegacyMaterials; +import com.songoda.core.configuration.DataStoreObject; +import com.songoda.core.configuration.SimpleDataStore; +import com.songoda.core.gui.methods.Clickable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a GUI screen that can be user-configured + * + * @since 2019-09-06 + * @author jascotty2 + */ +public class CustomizableGui extends Gui { + + final Map buttons; + + public CustomizableGui(SimpleDataStore buttons) { + this((Map) (Map) buttons.getData()); + } + + public CustomizableGui(SimpleDataStore buttons, Gui parent) { + this((Map) (Map) buttons.getData(), parent); + } + + public CustomizableGui(@NotNull Map buttons) { + this(buttons, null); + } + + public CustomizableGui(@NotNull Map buttons, @Nullable Gui parent) { + super(parent); + this.buttons = buttons; + if (buttons.containsKey("__DEFAULT__")) { + blankItem = GuiUtils.getBorderItem(buttons.get("__DEFAULT__").icon); + } + } + + @NotNull + public CustomButton[] getButtons() { + return buttons.values().toArray(new CustomButton[buttons.size()]); + } + + @NotNull + @Override + public CustomizableGui setDefaultItem(ItemStack item) { + if ((blankItem = item) != null) { + buttons.put("__DEFAULT__", (new CustomButton("__DEFAULT__")).setIcon(LegacyMaterials.getMaterial(item))); + } + return this; + } + + @Nullable + public CustomButton getButton(@NotNull String key) { + return key == null ? null : buttons.get(key.toLowerCase()); + } + + @NotNull + public CustomizableGui setItem(int defaultRow, int defaultCol, @NotNull String key, @NotNull ItemStack item) { + final int cell = defaultCol + defaultRow * 9; + return setItem(cell, key, item); + } + + @NotNull + public CustomizableGui setItem(int defaultCell, @NotNull String key, @NotNull ItemStack item) { + CustomButton btn = key == null ? null : buttons.get(key = key.toLowerCase()); + if (btn == null) { + buttons.put(key, btn = (new CustomButton(key, defaultCell)).setIcon(LegacyMaterials.getMaterial(item))); + } else { + ItemStack btnItem = btn.icon.getItem(); + ItemMeta itemMeta = item.getItemMeta(); + ItemMeta btnItemMeta = btnItem.getItemMeta(); + if (itemMeta != null && btnItemMeta != null) { + btnItemMeta.setDisplayName(itemMeta.getDisplayName()); + btnItemMeta.setLore(itemMeta.getLore()); + btnItem.setItemMeta(itemMeta); + } + item = btnItem; + } + cellItems.put(btn.position, item); + if (inventory != null && btn.position >= 0 && btn.position < inventory.getSize()) { + inventory.setItem(btn.position, item); + } + return this; + } + + @NotNull + public CustomizableGui setItem(int defaultRow, int defaultCol, @NotNull String key, @NotNull LegacyMaterials defaultItem, @NotNull String title, @NotNull String... lore) { + final int cell = defaultCol + defaultRow * 9; + return setItem(cell, key, defaultItem, title, lore); + } + + @NotNull + public CustomizableGui setItem(int defaultCell, @NotNull String key, @NotNull LegacyMaterials defaultItem, @NotNull String title, @NotNull String... lore) { + CustomButton btn = key == null ? null : buttons.get(key = key.toLowerCase()); + if (btn == null) { + buttons.put(key, btn = (new CustomButton(key, defaultCell)).setIcon(defaultItem)); + } + ItemStack item = GuiUtils.createButtonItem(btn.icon, title, lore); + cellItems.put(btn.position, item); + if (inventory != null && btn.position >= 0 && btn.position < inventory.getSize()) { + inventory.setItem(btn.position, item); + } + return this; + } + + @NotNull + public CustomizableGui highlightItem(@NotNull String key) { + CustomButton btn = key == null ? null : buttons.get(key.toLowerCase()); + if (btn != null) { + this.highlightItem(btn.position); + } + return this; + } + + @NotNull + public CustomizableGui removeHighlight(@NotNull String key) { + CustomButton btn = key == null ? null : buttons.get(key.toLowerCase()); + if (btn != null) { + this.removeHighlight(btn.position); + } + return this; + } + + @NotNull + public CustomizableGui updateItem(@NotNull String key, @Nullable String title, @NotNull String... lore) { + CustomButton btn = key == null ? null : buttons.get(key.toLowerCase()); + if (btn != null) { + this.updateItem(btn.position, title, lore); + } + return this; + } + + @NotNull + public CustomizableGui updateItem(@NotNull String key, @Nullable String title, @Nullable List lore) { + CustomButton btn = key == null ? null : buttons.get(key.toLowerCase()); + if (btn != null) { + this.updateItem(btn.position, title, lore); + } + return this; + } + + @NotNull + public CustomizableGui updateItem(@NotNull String key, @NotNull LegacyMaterials itemTo, @NotNull String title, @NotNull String... lore) { + CustomButton btn = key == null ? null : buttons.get(key.toLowerCase()); + if (btn != null) { + this.updateItem(btn.position, itemTo, title, lore); + } + return this; + } + + @NotNull + public CustomizableGui updateItem(@NotNull String key, @NotNull LegacyMaterials itemTo, @NotNull String title, @Nullable List lore) { + CustomButton btn = key == null ? null : buttons.get(key.toLowerCase()); + if (btn != null) { + this.updateItem(btn.position, itemTo, title, lore); + } + return this; + } + + @NotNull + public CustomizableGui setAction(@NotNull String key, Clickable action) { + CustomButton btn = key == null ? null : buttons.get(key = key.toLowerCase()); + if (btn != null) { + setConditional(btn.position, null, action); + } + return this; + } + + @NotNull + public CustomizableGui setAction(@NotNull String key, @Nullable ClickType type, @Nullable Clickable action) { + CustomButton btn = key == null ? null : buttons.get(key = key.toLowerCase()); + if (btn != null) { + setConditional(btn.position, type, action); + } + return this; + } + + @NotNull + public CustomizableGui setButton(int defaultCell, @NotNull String key, ItemStack item, @Nullable Clickable action) { + setItem(defaultCell, key, item); + setAction(key, null, action); + return this; + } + + @NotNull + public CustomizableGui setButton(int defaultCell, @NotNull String key, ItemStack item, @Nullable ClickType type, @Nullable Clickable action) { + setItem(defaultCell, key, item); + setAction(key, type, action); + return this; + } + + @NotNull + public CustomizableGui setButton(int defaultRow, int defaultCol, @NotNull String key, ItemStack item, @Nullable Clickable action) { + final int defaultCell = defaultCol + defaultRow * 9; + setItem(defaultCell, key, item); + setAction(key, null, action); + return this; + } + + @NotNull + public CustomizableGui setButton(int defaultRow, int defaultCol, @NotNull String key, ItemStack item, @Nullable ClickType type, @Nullable Clickable action) { + final int defaultCell = defaultCol + defaultRow * 9; + setItem(defaultCell, key, item); + setAction(key, type, action); + return this; + } + + @NotNull + @Override + public CustomizableGui setNextPage(int row, int col, @NotNull ItemStack item) { + return this.setNextPage(col + row * 9, item); + } + + @NotNull + @Override + public CustomizableGui setNextPage(int cell, @NotNull ItemStack item) { + CustomButton btn = buttons.get("__NEXT__"); + if (btn == null) { + buttons.put("__NEXT__", btn = (new CustomButton("__NEXT__", cell)).setIcon(LegacyMaterials.getMaterial(item))); + } else { + ItemStack btnItem = btn.icon.getItem(); + ItemMeta itemMeta = item.getItemMeta(); + ItemMeta btnItemMeta = btnItem.getItemMeta(); + if (itemMeta != null && btnItemMeta != null) { + btnItemMeta.setDisplayName(itemMeta.getDisplayName()); + btnItemMeta.setLore(itemMeta.getLore()); + btnItem.setItemMeta(itemMeta); + } + item = btnItem; + } + return (CustomizableGui) super.setNextPage(btn.position, item); + } + + @NotNull + @Override + public CustomizableGui setPrevPage(int row, int col, @NotNull ItemStack item) { + return this.setPrevPage(col + row * 9, item); + } + + @NotNull + @Override + public CustomizableGui setPrevPage(int cell, @NotNull ItemStack item) { + CustomButton btn = buttons.get("__PREV__"); + if (btn == null) { + buttons.put("__PREV__", btn = (new CustomButton("__PREV__", cell)).setIcon(LegacyMaterials.getMaterial(item))); + } else { + ItemStack btnItem = btn.icon.getItem(); + ItemMeta itemMeta = item.getItemMeta(); + ItemMeta btnItemMeta = btnItem.getItemMeta(); + if (itemMeta != null && btnItemMeta != null) { + btnItemMeta.setDisplayName(itemMeta.getDisplayName()); + btnItemMeta.setLore(itemMeta.getLore()); + btnItem.setItemMeta(itemMeta); + } + item = btnItem; + } + return (CustomizableGui) super.setPrevPage(btn.position, item); + } + + public CustomizableGui clearActions(@NotNull String key) { + CustomButton btn = key == null ? null : buttons.get(key = key.toLowerCase()); + if (btn != null) { + this.clearActions(btn.position); + } + return this; + } + + public static class CustomButton implements DataStoreObject { + + boolean _changed = false; + final String key; + int position = -1; + LegacyMaterials icon = LegacyMaterials.STONE; + + public CustomButton(String key) { + this.key = key; + } + + public CustomButton(String key, int position) { + this.key = key; + this.position = position; + } + + public static CustomButton loadFromSection(ConfigurationSection sec) { + CustomButton dat = new CustomButton(sec.getName()); + dat.icon = sec.contains("icon") ? LegacyMaterials.getMaterial(sec.getString("icon"), LegacyMaterials.STONE) : LegacyMaterials.STONE; + dat.position = sec.getInt("position"); + return dat; + } + + @Override + public void saveToSection(ConfigurationSection sec) { + sec.set("icon", icon.name()); + sec.set("position", position); + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getConfigKey() { + return key; + } + + @Override + public boolean hasChanged() { + return _changed; + } + + @Override + public void setChanged(boolean isChanged) { + _changed = isChanged; + } + + public LegacyMaterials getIcon() { + return icon; + } + + public CustomButton setIcon(LegacyMaterials icon) { + this.icon = icon != null ? icon : LegacyMaterials.STONE; + _changed = true; + return this; + } + } +} diff --git a/src/main/java/com/songoda/core/gui/Gui.java b/src/main/java/com/songoda/core/gui/Gui.java index 37bfe67c..9fc91b8c 100644 --- a/src/main/java/com/songoda/core/gui/Gui.java +++ b/src/main/java/com/songoda/core/gui/Gui.java @@ -26,6 +26,8 @@ import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * TODO: animated buttons @@ -62,8 +64,8 @@ public class Gui { this.rows = 3; } - public Gui(GuiType type) { - this.inventoryType = type; + public Gui(@NotNull GuiType type) { + this.inventoryType = type != null ? type : GuiType.STANDARD; switch (type) { case HOPPER: case DISPENSER: @@ -74,7 +76,7 @@ public class Gui { } } - public Gui(Gui parent) { + public Gui(@Nullable Gui parent) { this.parent = parent; } @@ -82,17 +84,18 @@ public class Gui { this.rows = Math.max(1, Math.min(6, rows)); } - public Gui(int rows, Gui parent) { + public Gui(int rows, @Nullable Gui parent) { this.parent = parent; this.rows = Math.max(1, Math.min(6, rows)); } + @NotNull public List getPlayers() { return inventory == null ? Collections.EMPTY_LIST : inventory.getViewers().stream() - .filter(e -> e instanceof Player) - .map(e -> (Player) e) - .collect(Collectors.toList()); + .filter(e -> e instanceof Player) + .map(e -> (Player) e) + .collect(Collectors.toList()); } public boolean isOpen() { @@ -139,7 +142,8 @@ public class Gui { } /** - * Close the GUI without calling onClose() and without opening any parent GUIs + * Close the GUI without calling onClose() and without opening any parent + * GUIs */ public void exit() { allowClose = true; @@ -151,21 +155,25 @@ public class Gui { .forEach(Player::closeInventory); } + @NotNull public GuiType getType() { return inventoryType; } + @NotNull public Gui setUnlocked(int cell) { unlockedCells.put(cell, true); return this; } + @NotNull public Gui setUnlocked(int row, int col) { final int cell = col + row * 9; unlockedCells.put(cell, true); return this; } + @NotNull public Gui setUnlockedRange(int cellFirst, int cellLast) { for (int cell = cellFirst; cell <= cellLast; ++cell) { unlockedCells.put(cell, true); @@ -173,6 +181,7 @@ public class Gui { return this; } + @NotNull public Gui setUnlockedRange(int cellRowFirst, int cellColFirst, int cellRowLast, int cellColLast) { final int last = cellColLast + cellRowLast * 9; for (int cell = cellColFirst + cellRowFirst * 9; cell <= last; ++cell) { @@ -181,17 +190,20 @@ public class Gui { return this; } + @NotNull public Gui setUnlocked(int cell, boolean open) { unlockedCells.put(cell, open); return this; } + @NotNull public Gui setUnlocked(int row, int col, boolean open) { final int cell = col + row * 9; unlockedCells.put(cell, open); return this; } + @NotNull public Gui setTitle(String title) { this.title = title; return this; @@ -201,6 +213,7 @@ public class Gui { return rows; } + @NotNull public Gui setRows(int rows) { switch (inventoryType) { case HOPPER: @@ -212,20 +225,24 @@ public class Gui { return this; } - public Gui setDefaultAction(Clickable action) { + @NotNull + public Gui setDefaultAction(@Nullable Clickable action) { defaultClicker = action; return this; } + @NotNull public Gui setDefaultItem(ItemStack item) { blankItem = item; return this; } + @Nullable public ItemStack getDefaultItem() { return blankItem; } + @Nullable public ItemStack getItem(int cell) { if (inventory != null && unlockedCells.getOrDefault(cell, false)) { return inventory.getItem(cell); @@ -233,6 +250,7 @@ public class Gui { return cellItems.get(cell); } + @Nullable public ItemStack getItem(int row, int col) { final int cell = col + row * 9; if (inventory != null && unlockedCells.getOrDefault(cell, false)) { @@ -241,7 +259,8 @@ public class Gui { return cellItems.get(cell); } - public Gui setItem(int cell, ItemStack item) { + @NotNull + public Gui setItem(int cell, @Nullable ItemStack item) { cellItems.put(cell, item); if (inventory != null && cell >= 0 && cell < inventory.getSize()) { inventory.setItem(cell, item); @@ -249,7 +268,8 @@ public class Gui { return this; } - public Gui setItem(int row, int col, ItemStack item) { + @NotNull + public Gui setItem(int row, int col, @Nullable ItemStack item) { final int cell = col + row * 9; cellItems.put(cell, item); if (inventory != null && cell >= 0 && cell < inventory.getSize()) { @@ -258,6 +278,7 @@ public class Gui { return this; } + @NotNull public Gui highlightItem(int cell) { ItemStack item = cellItems.get(cell); if (item != null && item.getType() != Material.AIR) { @@ -266,6 +287,7 @@ public class Gui { return this; } + @NotNull public Gui highlightItem(int row, int col) { final int cell = col + row * 9; ItemStack item = cellItems.get(cell); @@ -275,6 +297,7 @@ public class Gui { return this; } + @NotNull public Gui removeHighlight(int cell) { ItemStack item = cellItems.get(cell); if (item != null && item.getType() != Material.AIR) { @@ -283,6 +306,7 @@ public class Gui { return this; } + @NotNull public Gui removeHighlight(int row, int col) { final int cell = col + row * 9; ItemStack item = cellItems.get(cell); @@ -292,11 +316,13 @@ public class Gui { return this; } - public Gui updateItem(int row, int col, String name, String... lore) { + @NotNull + public Gui updateItem(int row, int col, @Nullable String name, @NotNull String... lore) { return updateItem(col + row * 9, name, lore); } - public Gui updateItem(int cell, String name, String... lore) { + @NotNull + public Gui updateItem(int cell, @Nullable String name, @NotNull String... lore) { ItemStack item = cellItems.get(cell); if (item != null && item.getType() != Material.AIR) { setItem(cell, GuiUtils.updateItem(item, name, lore)); @@ -304,11 +330,13 @@ public class Gui { return this; } - public Gui updateItem(int row, int col, String name, List lore) { + @NotNull + public Gui updateItem(int row, int col, @Nullable String name, @Nullable List lore) { return updateItem(col + row * 9, name, lore); } - public Gui updateItem(int cell, String name, List lore) { + @NotNull + public Gui updateItem(int cell, @NotNull String name, @Nullable List lore) { ItemStack item = cellItems.get(cell); if (item != null && item.getType() != Material.AIR) { setItem(cell, GuiUtils.updateItem(item, title, lore)); @@ -316,11 +344,13 @@ public class Gui { return this; } - public Gui updateItem(int row, int col, ItemStack itemTo, String title, String... lore) { + @NotNull + public Gui updateItem(int row, int col, @NotNull ItemStack itemTo, @Nullable String title, @NotNull String... lore) { return updateItem(col + row * 9, itemTo, title, lore); } - public Gui updateItem(int cell, ItemStack itemTo, String title, String... lore) { + @NotNull + public Gui updateItem(int cell, @NotNull ItemStack itemTo, @Nullable String title, @NotNull String... lore) { ItemStack item = cellItems.get(cell); if (item != null && item.getType() != Material.AIR) { setItem(cell, GuiUtils.updateItem(item, itemTo, title, lore)); @@ -328,11 +358,13 @@ public class Gui { return this; } - public Gui updateItem(int row, int col, LegacyMaterials itemTo, String title, String... lore) { + @NotNull + public Gui updateItem(int row, int col, @NotNull LegacyMaterials itemTo, @Nullable String title, @NotNull String... lore) { return updateItem(col + row * 9, itemTo, title, lore); } - public Gui updateItem(int cell, LegacyMaterials itemTo, String title, String... lore) { + @NotNull + public Gui updateItem(int cell, @NotNull LegacyMaterials itemTo, @Nullable String title, @Nullable String... lore) { ItemStack item = cellItems.get(cell); if (item != null && item.getType() != Material.AIR) { setItem(cell, GuiUtils.updateItem(item, itemTo, title, lore)); @@ -340,11 +372,13 @@ public class Gui { return this; } - public Gui updateItem(int row, int col, ItemStack itemTo, String title, List lore) { + @NotNull + public Gui updateItem(int row, int col, @NotNull ItemStack itemTo, @Nullable String title, @Nullable List lore) { return updateItem(col + row * 9, itemTo, title, lore); } - public Gui updateItem(int cell, ItemStack itemTo, String title, List lore) { + @NotNull + public Gui updateItem(int cell, @NotNull ItemStack itemTo, @Nullable String title, @Nullable List lore) { ItemStack item = cellItems.get(cell); if (item != null && item.getType() != Material.AIR) { setItem(cell, GuiUtils.updateItem(item, itemTo, title, lore)); @@ -352,11 +386,13 @@ public class Gui { return this; } - public Gui updateItem(int row, int col, LegacyMaterials itemTo, String title, List lore) { + @NotNull + public Gui updateItem(int row, int col, @NotNull LegacyMaterials itemTo, @Nullable String title, @Nullable List lore) { return updateItem(col + row * 9, itemTo, title, lore); } - public Gui updateItem(int cell, LegacyMaterials itemTo, String title, List lore) { + @NotNull + public Gui updateItem(int cell, @NotNull LegacyMaterials itemTo, @Nullable String title, @Nullable List lore) { ItemStack item = cellItems.get(cell); if (item != null && item.getType() != Material.AIR) { setItem(cell, GuiUtils.updateItem(item, itemTo, title, lore)); @@ -364,34 +400,40 @@ public class Gui { return this; } - public Gui setAction(int cell, Clickable action) { + @NotNull + public Gui setAction(int cell, @Nullable Clickable action) { setConditional(cell, null, action); return this; } - public Gui setAction(int row, int col, Clickable action) { + @NotNull + public Gui setAction(int row, int col, @Nullable Clickable action) { setConditional(col + row * 9, null, action); return this; } - public Gui setAction(int cell, ClickType type, Clickable action) { + @NotNull + public Gui setAction(int cell, @Nullable ClickType type, @Nullable Clickable action) { setConditional(cell, type, action); return this; } - public Gui setAction(int row, int col, ClickType type, Clickable action) { + @NotNull + public Gui setAction(int row, int col, @Nullable ClickType type, @Nullable Clickable action) { setConditional(col + row * 9, type, action); return this; } - public Gui setActionForRange(int cellFirst, int cellLast, Clickable action) { + @NotNull + public Gui setActionForRange(int cellFirst, int cellLast, @Nullable Clickable action) { for (int cell = cellFirst; cell <= cellLast; ++cell) { setConditional(cell, null, action); } return this; } - public Gui setActionForRange(int cellRowFirst, int cellColFirst, int cellRowLast, int cellColLast, Clickable action) { + @NotNull + public Gui setActionForRange(int cellRowFirst, int cellColFirst, int cellRowLast, int cellColLast, @Nullable Clickable action) { final int last = cellColLast + cellRowLast * 9; for (int cell = cellColFirst + cellRowFirst * 9; cell <= last; ++cell) { setConditional(cell, null, action); @@ -399,14 +441,16 @@ public class Gui { return this; } - public Gui setActionForRange(int cellFirst, int cellLast, ClickType type, Clickable action) { + @NotNull + public Gui setActionForRange(int cellFirst, int cellLast, @Nullable ClickType type, @Nullable Clickable action) { for (int cell = cellFirst; cell <= cellLast; ++cell) { setConditional(cell, type, action); } return this; } - public Gui setActionForRange(int cellRowFirst, int cellColFirst, int cellRowLast, int cellColLast, ClickType type, Clickable action) { + @NotNull + public Gui setActionForRange(int cellRowFirst, int cellColFirst, int cellRowLast, int cellColLast, @Nullable ClickType type, @Nullable Clickable action) { final int last = cellColLast + cellRowLast * 9; for (int cell = cellColFirst + cellRowFirst * 9; cell <= last; ++cell) { setConditional(cell, type, action); @@ -414,90 +458,128 @@ public class Gui { return this; } + @NotNull public Gui clearActions(int cell) { conditionalButtons.remove(cell); return this; } + @NotNull public Gui clearActions(int row, int col) { final int cell = col + row * 9; conditionalButtons.remove(cell); return this; } - public Gui setButton(int cell, ItemStack item, Clickable action) { + @NotNull + public Gui setButton(int cell, ItemStack item, @Nullable Clickable action) { setItem(cell, item); setConditional(cell, null, action); return this; } - public Gui setButton(int row, int col, ItemStack item, Clickable action) { + @NotNull + public Gui setButton(int row, int col, @Nullable ItemStack item, @Nullable Clickable action) { final int cell = col + row * 9; setItem(cell, item); setConditional(cell, null, action); return this; } - public Gui setButton(int cell, ItemStack item, ClickType type, Clickable action) { + @NotNull + public Gui setButton(int cell, @Nullable ItemStack item, @Nullable ClickType type, @Nullable Clickable action) { setItem(cell, item); setConditional(cell, type, action); return this; } - public Gui setButton(int row, int col, ItemStack item, ClickType type, Clickable action) { + @NotNull + public Gui setButton(int row, int col, @Nullable ItemStack item, @Nullable ClickType type, @Nullable Clickable action) { final int cell = col + row * 9; setItem(cell, item); setConditional(cell, type, action); return this; } - protected void setConditional(int cell, ClickType type, Clickable action) { + protected void setConditional(int cell, @Nullable ClickType type, @Nullable Clickable action) { Map conditionals = conditionalButtons.get(cell); - if (action != null) { - if (conditionals == null) { - conditionalButtons.put(cell, conditionals = new HashMap()); - } - conditionals.put(type, action); + if (conditionals == null) { + conditionalButtons.put(cell, conditionals = new HashMap()); } + conditionals.put(type, action); } - public Gui setOnOpen(Openable action) { + @NotNull + public Gui setOnOpen(@Nullable Openable action) { opener = action; return this; } - public Gui setOnClose(Closable action) { + @NotNull + public Gui setOnClose(@Nullable Closable action) { closer = action; return this; } - public Gui setOnDrop(Droppable action) { + @NotNull + public Gui setOnDrop(@Nullable Droppable action) { dropper = action; return this; } - public Gui setOnPage(Pagable action) { + @NotNull + public Gui setOnPage(@Nullable Pagable action) { pager = action; return this; } - public Gui setNextPage(int row, int col, ItemStack item) { + public Gui setNextPage(ItemStack item) { + nextPage = item; + return this; + } + + public Gui setPrevPage(ItemStack item) { + prevPage = item; + return this; + } + + @NotNull + public Gui setNextPage(int cell, @NotNull ItemStack item) { + nextPageIndex = cell; + if (page < pages) { + setButton(nextPageIndex, nextPage = item, ClickType.LEFT, (event) -> this.nextPage(event.manager)); + } + return this; + } + + @NotNull + public Gui setNextPage(int row, int col, @NotNull ItemStack item) { nextPageIndex = col + row * 9; if (page < pages) { - setButton(nextPageIndex, item, ClickType.LEFT, (event) -> this.nextPage(event.manager)); + setButton(nextPageIndex, nextPage = item, ClickType.LEFT, (event) -> this.nextPage(event.manager)); } return this; } - public Gui setPrevPage(int row, int col, ItemStack item) { + @NotNull + public Gui setPrevPage(int cell, @NotNull ItemStack item) { + prevPageIndex = cell; + if (page > 1) { + setButton(prevPageIndex, prevPage = item, ClickType.LEFT, (event) -> this.prevPage(event.manager)); + } + return this; + } + + @NotNull + public Gui setPrevPage(int row, int col, @NotNull ItemStack item) { prevPageIndex = col + row * 9; if (page > 1) { - setButton(prevPageIndex, item, ClickType.LEFT, (event) -> this.prevPage(event.manager)); + setButton(prevPageIndex, prevPage = item, ClickType.LEFT, (event) -> this.prevPage(event.manager)); } return this; } - public void nextPage(GuiManager manager) { + public void nextPage(@NotNull GuiManager manager) { if (page < pages) { int lastPage = page; ++page; @@ -515,7 +597,7 @@ public class Gui { } } - public void prevPage(GuiManager manager) { + public void prevPage(@NotNull GuiManager manager) { if (page > 1) { int lastPage = page; --page; @@ -547,11 +629,13 @@ public class Gui { } } - protected Inventory getOrCreateInventory(GuiManager manager) { + @NotNull + protected Inventory getOrCreateInventory(@NotNull GuiManager manager) { return inventory != null ? inventory : generateInventory(manager); } - protected Inventory generateInventory(GuiManager manager) { + @NotNull + protected Inventory generateInventory(@NotNull GuiManager manager) { final int cells = rows * 9; InventoryType t = inventoryType == null ? InventoryType.CHEST : inventoryType.type; switch (t) { @@ -573,6 +657,7 @@ public class Gui { return inventory; } + @Nullable public Gui getParent() { return parent; } @@ -595,11 +680,11 @@ public class Gui { return title; } - protected boolean onClickOutside(GuiManager manager, Player player, InventoryClickEvent event) { + protected boolean onClickOutside(@NotNull GuiManager manager, @NotNull Player player, @NotNull InventoryClickEvent event) { return dropper != null ? dropper.onDrop(new GuiDropItemEvent(manager, this, player, event)) : true; } - protected boolean onClick(GuiManager manager, Player player, Inventory inventory, InventoryClickEvent event) { + protected boolean onClick(@NotNull GuiManager manager, @NotNull Player player, @NotNull Inventory inventory, @NotNull InventoryClickEvent event) { final int cell = event.getSlot(); Map conditionals = conditionalButtons.get(cell); Clickable button; @@ -608,7 +693,7 @@ public class Gui { button.onClick(new GuiClickEvent(manager, this, player, event, cell, true)); } else { // no event for this button - if(defaultClicker != null) { + if (defaultClicker != null) { // this is a default action, not a triggered action defaultClicker.onClick(new GuiClickEvent(manager, this, player, event, cell, true)); } @@ -617,19 +702,19 @@ public class Gui { return true; } - protected boolean onClickPlayerInventory(GuiManager manager, Player player, Inventory openInv, InventoryClickEvent event) { + protected boolean onClickPlayerInventory(@NotNull GuiManager manager, @NotNull Player player, @NotNull Inventory openInv, @NotNull InventoryClickEvent event) { // no events for this yet return false; } - public void onOpen(GuiManager manager, Player player) { + public void onOpen(@NotNull GuiManager manager, @NotNull Player player) { open = true; if (opener != null) { opener.onOpen(new GuiOpenEvent(manager, this, player)); } } - public void onClose(GuiManager manager, Player player) { + public void onClose(@NotNull GuiManager manager, @NotNull Player player) { if (!allowClose) { manager.showGUI(player, this); return; diff --git a/src/main/java/com/songoda/core/gui/SimplePagedGui.java b/src/main/java/com/songoda/core/gui/SimplePagedGui.java index b753da18..f966db89 100644 --- a/src/main/java/com/songoda/core/gui/SimplePagedGui.java +++ b/src/main/java/com/songoda/core/gui/SimplePagedGui.java @@ -62,16 +62,6 @@ public class SimplePagedGui extends Gui { return this; } - public SimplePagedGui setNextPage(ItemStack item) { - nextPage = item; - return this; - } - - public SimplePagedGui setPrevPage(ItemStack item) { - prevPage = item; - return this; - } - @Override public SimplePagedGui setItem(int row, int col, ItemStack item) { return setItem(col + row * 9, item); From fa03cef4cd5fd27c2b007eb1f8c8376d8d1787e7 Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Fri, 6 Sep 2019 18:49:06 -0500 Subject: [PATCH 06/11] add enable check to stacker manager --- .../java/com/songoda/core/gui/CustomizableGui.java | 2 -- .../com/songoda/core/hooks/EntityStackerManager.java | 10 ++++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/songoda/core/gui/CustomizableGui.java b/src/main/java/com/songoda/core/gui/CustomizableGui.java index 63589464..777b6477 100644 --- a/src/main/java/com/songoda/core/gui/CustomizableGui.java +++ b/src/main/java/com/songoda/core/gui/CustomizableGui.java @@ -4,8 +4,6 @@ import com.songoda.core.compatibility.LegacyMaterials; import com.songoda.core.configuration.DataStoreObject; import com.songoda.core.configuration.SimpleDataStore; import com.songoda.core.gui.methods.Clickable; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import org.bukkit.configuration.ConfigurationSection; diff --git a/src/main/java/com/songoda/core/hooks/EntityStackerManager.java b/src/main/java/com/songoda/core/hooks/EntityStackerManager.java index abd8739f..ea52df65 100644 --- a/src/main/java/com/songoda/core/hooks/EntityStackerManager.java +++ b/src/main/java/com/songoda/core/hooks/EntityStackerManager.java @@ -22,6 +22,16 @@ public class EntityStackerManager { return manager; } + /** + * Check to see if there is a default stacker hook loaded.
+ * NOTE: using a default stacker assumes that this library is shaded + * + * @return returns false if there are no supported plugins + */ + public static boolean isEnabled() { + return manager.isEnabled(); + } + /** * Grab the default hologram plugin.
* NOTE: using a default hologram assumes that this library is shaded From fcadf9fb7a146902ca9d4b1f31cae5d3b62c4138 Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Fri, 6 Sep 2019 19:01:23 -0500 Subject: [PATCH 07/11] add invisistring utils --- .../com/songoda/core/utils/TextUtils.java | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/songoda/core/utils/TextUtils.java b/src/main/java/com/songoda/core/utils/TextUtils.java index bfdbbf6d..73bcb8ce 100644 --- a/src/main/java/com/songoda/core/utils/TextUtils.java +++ b/src/main/java/com/songoda/core/utils/TextUtils.java @@ -26,14 +26,51 @@ public class TextUtils { return ChatColor.translateAlternateColorCodes('&', text); } + /** + * Convert a string to an invisible colored string that's lore-safe
+ * (Safe to use as lore)
+ * Note: Do not use semi-colons in this string, or they will be lost when decoding! + * + * @param s string to convert + * @return encoded string + */ + public static String convertToInvisibleLoreString(String s) { + if (s == null || s.equals("")) + return ""; + StringBuilder hidden = new StringBuilder(); + for (char c : s.toCharArray()) hidden.append(ChatColor.COLOR_CHAR).append(';').append(ChatColor.COLOR_CHAR).append(c); + return hidden.toString(); + } + + /** + * Convert a string to an invisible colored string
+ * (Not safe to use as lore)
+ * Note: Do not use semi-colons in this string, or they will be lost when decoding! + * + * @param s string to convert + * @return encoded string + */ public static String convertToInvisibleString(String s) { if (s == null || s.equals("")) return ""; StringBuilder hidden = new StringBuilder(); - for (char c : s.toCharArray()) hidden.append(ChatColor.COLOR_CHAR + "").append(c); + for (char c : s.toCharArray()) hidden.append(ChatColor.COLOR_CHAR).append(c); return hidden.toString(); } + /** + * Removes color markers used to encode strings as invisible text + * + * @param s encoded string + * @return string with color markers removed + */ + public static String convertFromInvisibleString(String s) { + if (s == null || s.equals("")) { + return ""; + } + return s.replaceAll(ChatColor.COLOR_CHAR + ";" + ChatColor.COLOR_CHAR + "|" + ChatColor.COLOR_CHAR, ""); + } + protected static final List supportedCharsets = new ArrayList(); static { From 1bd5010471d7af9eb38eaa3c932d393008e145ac Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sat, 7 Sep 2019 08:37:31 -0500 Subject: [PATCH 08/11] add egg-throw compatible sound --- .../java/com/songoda/core/compatibility/CompatibleSounds.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/songoda/core/compatibility/CompatibleSounds.java b/src/main/java/com/songoda/core/compatibility/CompatibleSounds.java index 92d723af..fd62fb8b 100644 --- a/src/main/java/com/songoda/core/compatibility/CompatibleSounds.java +++ b/src/main/java/com/songoda/core/compatibility/CompatibleSounds.java @@ -293,7 +293,7 @@ public enum CompatibleSounds { ENTITY_DROWNED_SHOOT, ENTITY_DROWNED_STEP, ENTITY_DROWNED_SWIM, - ENTITY_EGG_THROW, + ENTITY_EGG_THROW(ServerVersion.V1_9, v("SHOOT_ARROW", true)), ENTITY_ELDER_GUARDIAN_AMBIENT, ENTITY_ELDER_GUARDIAN_AMBIENT_LAND, ENTITY_ELDER_GUARDIAN_CURSE, From 58ddb600b6855f344807a832f98df28be39f8adb Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sat, 7 Sep 2019 08:53:50 -0500 Subject: [PATCH 09/11] add spawn egg lookup --- .../songoda/core/compatibility/LegacyMaterials.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/songoda/core/compatibility/LegacyMaterials.java b/src/main/java/com/songoda/core/compatibility/LegacyMaterials.java index 43d20647..24951086 100644 --- a/src/main/java/com/songoda/core/compatibility/LegacyMaterials.java +++ b/src/main/java/com/songoda/core/compatibility/LegacyMaterials.java @@ -6,6 +6,7 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import org.bukkit.Material; +import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; /** @@ -932,7 +933,7 @@ public enum LegacyMaterials { TNT_MINECART("EXPLOSIVE_MINECART"), TORCH, TOTEM_OF_UNDYING("TOTEM"), - TRADER_LLAMA_SPAWN_EGG(ServerVersion.V1_14, "LLAMA_SPAWN_EGG"), + TRADER_LLAMA_SPAWN_EGG(), TRAPPED_CHEST, TRIDENT, TRIPWIRE, @@ -2026,6 +2027,13 @@ public enum LegacyMaterials { return false; } + public static LegacyMaterials getSpawnEgg(EntityType type) { + if(type == EntityType.MUSHROOM_COW) { + return MOOSHROOM_SPAWN_EGG; + } + return lookupMap.get(type.name() + "_SPAWN_EGG"); + } + public static LegacyMaterials getGlassPaneColor(int color) { switch (color) { case 0: From f2faaab50b134b3a625835f79c2bb9571ae3dc19 Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sat, 7 Sep 2019 09:21:53 -0500 Subject: [PATCH 10/11] add item stack edit function --- .../core/compatibility/CompatibleSounds.java | 2 +- .../com/songoda/core/utils/ItemUtils.java | 122 +++++++++++++----- 2 files changed, 91 insertions(+), 33 deletions(-) diff --git a/src/main/java/com/songoda/core/compatibility/CompatibleSounds.java b/src/main/java/com/songoda/core/compatibility/CompatibleSounds.java index fd62fb8b..47968539 100644 --- a/src/main/java/com/songoda/core/compatibility/CompatibleSounds.java +++ b/src/main/java/com/songoda/core/compatibility/CompatibleSounds.java @@ -767,7 +767,7 @@ public enum CompatibleSounds { ITEM_CROSSBOW_QUICK_CHARGE_3, ITEM_CROSSBOW_SHOOT, ITEM_ELYTRA_FLYING, - ITEM_FIRECHARGE_USE, + ITEM_FIRECHARGE_USE(ServerVersion.V1_9, v("GHAST_FIREBALL", true)), ITEM_FLINTANDSTEEL_USE("FIRE_IGNITE"), ITEM_HOE_TILL, ITEM_NETHER_WART_PLANT, diff --git a/src/main/java/com/songoda/core/utils/ItemUtils.java b/src/main/java/com/songoda/core/utils/ItemUtils.java index 67ef730c..19363f05 100644 --- a/src/main/java/com/songoda/core/utils/ItemUtils.java +++ b/src/main/java/com/songoda/core/utils/ItemUtils.java @@ -16,9 +16,11 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Stream; import org.bukkit.Bukkit; +import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; @@ -28,11 +30,9 @@ import org.bukkit.inventory.meta.SkullMeta; public class ItemUtils { - static boolean check_compatibility = false; static boolean can_getI18NDisplayName = true; - static void init() { - check_compatibility = true; + static { try { ItemStack.class.getMethod("getI18NDisplayName"); } catch (NoSuchMethodException | SecurityException ex) { @@ -40,6 +40,34 @@ public class ItemUtils { } } + public static String getItemName(ItemStack it) { + if (it == null) { + return null; + } else if (can_getI18NDisplayName) { + return it.getI18NDisplayName(); + } else { + return itemName(it.getType()); + } + } + + static String itemName(Material mat) { + String matName = mat.name().replace("_", " "); + StringBuilder titleCase = new StringBuilder(matName.length()); + + Stream.of(matName.split(" ")).forEach(s -> { + s = s.toLowerCase(); + if (s.equals("of")) { + titleCase.append(s).append(" "); + } else { + char[] str = s.toCharArray(); + str[0] = Character.toUpperCase(str[0]); + titleCase.append(new String(str)).append(" "); + } + }); + + return titleCase.toString().trim(); + } + /** * Clone of org.bukkit.inventory.ItemStack.asQuantity, since it is a paper-only function * @@ -187,38 +215,34 @@ public class ItemUtils { return item; } - public static String getItemName(ItemStack it) { - if (!check_compatibility) { - init(); - } - if (it == null) { - return null; - } else if (can_getI18NDisplayName) { - return it.getI18NDisplayName(); - } else { - return itemName(it.getType()); - } - } + private static Class mc_Item = NMSUtils.getNMSClass("Item"); + private static Method mc_Item_getItem; + private static Field mc_Item_maxStackSize; - static String itemName(Material mat) { - String matName = mat.name().replace("_", " "); - StringBuilder titleCase = new StringBuilder(matName.length()); + static { + if(mc_ItemStack != null) { + try { + mc_Item_getItem = mc_ItemStack.getDeclaredMethod("getItem"); + mc_Item_maxStackSize = mc_Item.getDeclaredField("maxStackSize"); + mc_Item_maxStackSize.setAccessible(true); + } catch (Exception ex) { + } + } + } - Stream.of(matName.split(" ")).forEach(s -> { - s = s.toLowerCase(); - if (s.equals("of")) { - titleCase.append(s).append(" "); - } else { - char[] str = s.toCharArray(); - str[0] = Character.toUpperCase(str[0]); - titleCase.append(new String(str)).append(" "); - } - }); + public static ItemStack setMaxStack(ItemStack item, int max) { + if (item != null && mc_Item_maxStackSize != null) { + try { + Object objItemStack = mc_Item_getItem.invoke(cb_CraftItemStack_asNMSCopy.invoke(null, item)); + mc_Item_maxStackSize.set(objItemStack, max); + } catch (ReflectiveOperationException e) { + Bukkit.getLogger().log(Level.SEVERE, "Failed to set max stack size on item " + item, e); + } + } + return item; + } - return titleCase.toString().trim(); - } - - public static ItemStack getPlayerSkull(OfflinePlayer player) { + public static ItemStack getPlayerSkull(OfflinePlayer player) { ItemStack head = LegacyMaterials.PLAYER_HEAD.getItem(); if (ServerVersion.isServerVersionBelow(ServerVersion.V1_8)) { return head; @@ -273,6 +297,40 @@ public class ItemUtils { } } + /** + * Use up whatever item the player is holding in their main hand + * + * @param player player to grab item from + */ + public static void takeActiveItem(Player player) { + takeActiveItem(player, 1); + } + + /** + * Use up whatever item the player is holding in their main hand + * + * @param player player to grab item from + * @param amount number of items to use up + */ + public static void takeActiveItem(Player player, int amount) { + if (player.getGameMode() == GameMode.CREATIVE) return; + + ItemStack item = player.getInventory().getItemInHand(); + + int result = item.getAmount() - amount; + item.setAmount(result); + + player.setItemInHand(result > 0 ? item : null); + } + + /** + * Quickly check to see if the two items use the same material.
+ * NOTE: Does not check meta data; only checks the item material. + * + * @param is1 first item to compare + * @param is2 item to compare against + * @return true if both items are of the same material + */ public static boolean isSimilarMaterial(ItemStack is1, ItemStack is2) { LegacyMaterials mat1 = LegacyMaterials.getMaterial(is1); return mat1 != null && mat1 == LegacyMaterials.getMaterial(is2); From f41541501cf4ff452cfbcd830bbf934fa58e5fb3 Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sat, 7 Sep 2019 09:33:36 -0500 Subject: [PATCH 11/11] Version 2.0.7 --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4c6656a7..3df27d1f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,7 +4,7 @@ stages: variables: name: "SongodaCore" path: "/builds/$CI_PROJECT_PATH" - version: "2.0.6" + version: "2.0.7" build: stage: build