From 274d90cf5b768a23ebb287c14bc3a42c011c4218 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sun, 8 Oct 2023 22:24:25 +0800 Subject: [PATCH] gui framework --- .../api/manager/AdventureManager.java | 2 +- .../api/util/InventoryUtils.java | 4 +- .../CustomFishingPluginImpl.java | 5 +- .../adventure/AdventureManagerImpl.java | 2 +- .../adventure/component/Languages.java | 78 ++++++++ .../ShadedAdventureComponentUtils.java | 22 +++ .../ShadedAdventureComponentWrapper.java | 44 +++++ ...adedAdventureShadedComponentLocalizer.java | 52 ++++++ .../component/ShadedComponentLocalizer.java | 77 ++++++++ .../command/sub/GUIEditorCommand.java | 17 +- .../customfishing/gui/ItemEditor.java | 171 ++++++++++++++++++ .../customfishing/gui/SelectFileGUI.java | 123 +++++++++++++ .../gui/icon/BackGroundItem.java | 23 +++ .../gui/icon/BackToFolderItem.java | 46 +++++ .../customfishing/gui/icon/Icon.java | 4 + .../customfishing/gui/icon/NextPageItem.java | 25 +++ .../gui/icon/PreviousPageItem.java | 24 +++ .../gui/icon/ScrollDownItem.java | 23 +++ .../customfishing/gui/icon/ScrollUpItem.java | 24 +++ .../competition/ranking/LocalRankingImpl.java | 1 - .../competition/ranking/RedisRankingImpl.java | 1 - 21 files changed, 751 insertions(+), 17 deletions(-) create mode 100644 plugin/src/main/java/net/momirealms/customfishing/adventure/component/Languages.java create mode 100644 plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureComponentUtils.java create mode 100644 plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureComponentWrapper.java create mode 100644 plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureShadedComponentLocalizer.java create mode 100644 plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedComponentLocalizer.java create mode 100644 plugin/src/main/java/net/momirealms/customfishing/gui/ItemEditor.java create mode 100644 plugin/src/main/java/net/momirealms/customfishing/gui/SelectFileGUI.java create mode 100644 plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackGroundItem.java create mode 100644 plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackToFolderItem.java create mode 100644 plugin/src/main/java/net/momirealms/customfishing/gui/icon/Icon.java create mode 100644 plugin/src/main/java/net/momirealms/customfishing/gui/icon/NextPageItem.java create mode 100644 plugin/src/main/java/net/momirealms/customfishing/gui/icon/PreviousPageItem.java create mode 100644 plugin/src/main/java/net/momirealms/customfishing/gui/icon/ScrollDownItem.java create mode 100644 plugin/src/main/java/net/momirealms/customfishing/gui/icon/ScrollUpItem.java diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/AdventureManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/AdventureManager.java index 14a13d4d..061eb206 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/AdventureManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/AdventureManager.java @@ -129,5 +129,5 @@ public interface AdventureManager { * @param component shaded component * @return paper component */ - Object shadedComponentToPaperComponent(Component component); + Object shadedComponentToOriginalComponent(Component component); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/util/InventoryUtils.java b/api/src/main/java/net/momirealms/customfishing/api/util/InventoryUtils.java index 906a9d0b..bb1645b6 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/util/InventoryUtils.java +++ b/api/src/main/java/net/momirealms/customfishing/api/util/InventoryUtils.java @@ -65,7 +65,7 @@ public class InventoryUtils { null, inventoryHolder, size, - isSpigot ? CustomFishingPlugin.get().getAdventure().componentToLegacy(component) : CustomFishingPlugin.get().getAdventure().shadedComponentToPaperComponent(component) + isSpigot ? CustomFishingPlugin.get().getAdventure().componentToLegacy(component) : CustomFishingPlugin.get().getAdventure().shadedComponentToOriginalComponent(component) ); } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException exception) { exception.printStackTrace(); @@ -94,7 +94,7 @@ public class InventoryUtils { null, inventoryHolder, type, - isSpigot ? CustomFishingPlugin.get().getAdventure().componentToLegacy(component) : CustomFishingPlugin.get().getAdventure().shadedComponentToPaperComponent(component) + isSpigot ? CustomFishingPlugin.get().getAdventure().componentToLegacy(component) : CustomFishingPlugin.get().getAdventure().shadedComponentToOriginalComponent(component) ); } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException exception) { exception.printStackTrace(); diff --git a/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java b/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java index 027b0f8f..3dc22c51 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java @@ -21,7 +21,6 @@ import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.ProtocolManager; import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion; import de.tr7zw.changeme.nbtapi.utils.VersionChecker; -import io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver; import net.momirealms.customfishing.adventure.AdventureManagerImpl; import net.momirealms.customfishing.api.CustomFishingPlugin; import net.momirealms.customfishing.api.util.LogUtils; @@ -211,8 +210,12 @@ public class CustomFishingPluginImpl extends CustomFishingPlugin { "org.xerial:sqlite-jdbc:3.43.0.0", mavenRepo, "dev.jorel:commandapi-bukkit-shade:9.2.0", mavenRepo, "xyz.xenondevs.invui:invui-core:1.19", "https://repo.xenondevs.xyz/releases/", + "xyz.xenondevs.invui:inventory-access:1.19", "https://repo.xenondevs.xyz/releases/", "xyz.xenondevs.invui:inventory-access-r8:1.19", "https://repo.xenondevs.xyz/releases/", "xyz.xenondevs.invui:inventory-access-r9:1.19", "https://repo.xenondevs.xyz/releases/", + "xyz.xenondevs.invui:inventory-access-r10:1.19", "https://repo.xenondevs.xyz/releases/", + "xyz.xenondevs.invui:inventory-access-r11:1.19", "https://repo.xenondevs.xyz/releases/", + "xyz.xenondevs.invui:inventory-access-r12:1.19", "https://repo.xenondevs.xyz/releases/", "xyz.xenondevs.invui:inventory-access-r13:1.19", "https://repo.xenondevs.xyz/releases/", "xyz.xenondevs.invui:inventory-access-r14:1.19", "https://repo.xenondevs.xyz/releases/", "xyz.xenondevs.invui:inventory-access-r15:1.19", "https://repo.xenondevs.xyz/releases/" diff --git a/plugin/src/main/java/net/momirealms/customfishing/adventure/AdventureManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/adventure/AdventureManagerImpl.java index 9a8a384b..47431ec9 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/adventure/AdventureManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/adventure/AdventureManagerImpl.java @@ -213,7 +213,7 @@ public class AdventureManagerImpl implements AdventureManager { } @Override - public Object shadedComponentToPaperComponent(Component component) { + public Object shadedComponentToOriginalComponent(Component component) { Object cp; try { cp = ReflectionUtils.gsonDeserializeMethod.invoke(ReflectionUtils.gsonInstance, GsonComponentSerializer.gson().serialize(component)); diff --git a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/Languages.java b/plugin/src/main/java/net/momirealms/customfishing/adventure/component/Languages.java new file mode 100644 index 00000000..35c93436 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/adventure/component/Languages.java @@ -0,0 +1,78 @@ +package net.momirealms.customfishing.adventure.component; + +import com.google.gson.stream.JsonReader; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; + +public class Languages { + + private static final Languages INSTANCE = new Languages(); + private final Map> translations = new HashMap<>(); + private Function languageProvider = Player::locale; + private boolean serverSideTranslations = true; + + private Languages() { + } + + public static Languages getInstance() { + return INSTANCE; + } + + public void addLanguage(@NotNull String lang, @NotNull Map translations) { + this.translations.put(lang, translations); + } + + public void loadLanguage(@NotNull String lang, @NotNull Reader reader) throws IOException { + var translations = new HashMap(); + try (var jsonReader = new JsonReader(reader)) { + jsonReader.beginObject(); + while (jsonReader.hasNext()) { + var key = jsonReader.nextName(); + var value = jsonReader.nextString(); + translations.put(key, value); + } + + addLanguage(lang, translations); + } + } + + public void loadLanguage(@NotNull String lang, @NotNull File file, @NotNull Charset charset) throws IOException { + try (var reader = new FileReader(file, charset)) { + loadLanguage(lang, reader); + } + } + + public @Nullable String getFormatString(@NotNull String lang, @NotNull String key) { + var map = translations.get(lang); + if (map == null) + return null; + return map.get(key); + } + + public void setLanguageProvider(@NotNull Function languageProvider) { + this.languageProvider = languageProvider; + } + + public @NotNull Locale getLanguage(@NotNull Player player) { + return languageProvider.apply(player); + } + + public void enableServerSideTranslations(boolean enable) { + serverSideTranslations = enable; + } + + public boolean doesServerSideTranslations() { + return serverSideTranslations; + } +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureComponentUtils.java b/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureComponentUtils.java new file mode 100644 index 00000000..12bc0c1e --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureComponentUtils.java @@ -0,0 +1,22 @@ +package net.momirealms.customfishing.adventure.component; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextDecoration; + +public class ShadedAdventureComponentUtils { + + private static final Style FORMATTING_TEMPLATE = Style.style() + .color(NamedTextColor.WHITE) + .decoration(TextDecoration.ITALIC, false) + .decoration(TextDecoration.BOLD, false) + .decoration(TextDecoration.STRIKETHROUGH, false) + .decoration(TextDecoration.UNDERLINED, false) + .decoration(TextDecoration.OBFUSCATED, false) + .build(); + + public static Component withoutPreFormatting(Component component) { + return component.style(component.style().merge(FORMATTING_TEMPLATE, Style.Merge.Strategy.IF_ABSENT_ON_TARGET)); + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureComponentWrapper.java b/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureComponentWrapper.java new file mode 100644 index 00000000..b3e4dd68 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureComponentWrapper.java @@ -0,0 +1,44 @@ +package net.momirealms.customfishing.adventure.component; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.inventoryaccess.component.ComponentWrapper; + +public class ShadedAdventureComponentWrapper implements ComponentWrapper { + + public static final ShadedAdventureComponentWrapper EMPTY = new ShadedAdventureComponentWrapper(Component.empty()); + + private final Component component; + + public ShadedAdventureComponentWrapper(Component component) { + this.component = component; + } + + @Override + public @NotNull String serializeToJson() { + return GsonComponentSerializer.gson().serialize(component); + } + + @Override + public @NotNull ComponentWrapper localized(@NotNull String lang) { + if (!Languages.getInstance().doesServerSideTranslations()) + return this; + + return new ShadedAdventureComponentWrapper(ShadedAdventureShadedComponentLocalizer.getInstance().localize(lang, component)); + } + + @Override + public @NotNull ComponentWrapper withoutPreFormatting() { + return new ShadedAdventureComponentWrapper(ShadedAdventureComponentUtils.withoutPreFormatting(component)); + } + + @Override + public @NotNull ShadedAdventureComponentWrapper clone() { + try { + return (ShadedAdventureComponentWrapper) super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureShadedComponentLocalizer.java b/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureShadedComponentLocalizer.java new file mode 100644 index 00000000..63c13aae --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureShadedComponentLocalizer.java @@ -0,0 +1,52 @@ +package net.momirealms.customfishing.adventure.component; + +import net.kyori.adventure.text.*; + +public class ShadedAdventureShadedComponentLocalizer extends ShadedComponentLocalizer { + + private static final ShadedAdventureShadedComponentLocalizer INSTANCE = new ShadedAdventureShadedComponentLocalizer(); + + private ShadedAdventureShadedComponentLocalizer() { + super(Component::text); + } + + public static ShadedAdventureShadedComponentLocalizer getInstance() { + return INSTANCE; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + public Component localize(String lang, Component component) { + if (!(component instanceof BuildableComponent)) + throw new IllegalStateException("Component is not a BuildableComponent"); + + return localize(lang, (BuildableComponent) component); + } + + @SuppressWarnings("NonExtendableApiUsage") + private , B extends ComponentBuilder> BuildableComponent localize(String lang, BuildableComponent component) { + ComponentBuilder builder; + if (component instanceof TranslatableComponent) { + builder = localizeTranslatable(lang, (TranslatableComponent) component).toBuilder(); + } else { + builder = component.toBuilder(); + } + + builder.mapChildrenDeep(child -> { + if (child instanceof TranslatableComponent) + return localizeTranslatable(lang, (TranslatableComponent) child); + return child; + }); + + return builder.build(); + } + + private BuildableComponent localizeTranslatable(String lang, TranslatableComponent component) { + var formatString = Languages.getInstance().getFormatString(lang, component.key()); + if (formatString == null) + return component; + + var children = decomposeFormatString(lang, formatString, component, component.args()); + return Component.textOfChildren(children.toArray(ComponentLike[]::new)).style(component.style()); + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedComponentLocalizer.java b/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedComponentLocalizer.java new file mode 100644 index 00000000..d9ad9500 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedComponentLocalizer.java @@ -0,0 +1,77 @@ +package net.momirealms.customfishing.adventure.component; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.regex.Pattern; + +abstract class ShadedComponentLocalizer { + + private static final Pattern FORMAT_PATTERN = Pattern.compile("%(?:(\\d+)\\$)?([A-Za-z%]|$)"); + + private Function componentCreator; + + public ShadedComponentLocalizer(Function componentCreator) { + this.componentCreator = componentCreator; + } + + public void setComponentCreator(Function componentCreator) { + this.componentCreator = componentCreator; + } + + public abstract T localize(String lang, T component); + + protected List decomposeFormatString(String lang, String formatString, T component, List args) { + var matcher = FORMAT_PATTERN.matcher(formatString); + + var components = new ArrayList(); + var sb = new StringBuilder(); + var nextArgIdx = 0; + + var i = 0; + while (matcher.find(i)) { + var start = matcher.start(); + var end = matcher.end(); + + // check for escaped % + var matchedStr = formatString.substring(i, start); + if ("%%".equals(matchedStr)) { + sb.append('%'); + } else { + // check for invalid format, only %s is supported + var argType = matcher.group(2); + if (!"s".equals(argType)) { + throw new IllegalStateException("Unsupported format: '" + matchedStr + "'"); + } + + // retrieve argument index + var argIdxStr = matcher.group(1); + var argIdx = argIdxStr == null ? nextArgIdx++ : Integer.parseInt(argIdxStr) - 1; + + // validate argument index + if (argIdx < 0) + throw new IllegalStateException("Invalid argument index: " + argIdx); + + // append the text before the argument + sb.append(formatString, i, start); + // add text component + components.add(componentCreator.apply(sb.toString())); + // add argument component + components.add(args.size() <= argIdx ? componentCreator.apply("") : localize(lang, args.get(argIdx))); + // clear string builder + sb.setLength(0); + } + + // start next search after matcher end index + i = end; + } + + // append the text after the last argument + if (i < formatString.length()) { + sb.append(formatString, i, formatString.length()); + components.add(componentCreator.apply(sb.toString())); + } + + return components; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/command/sub/GUIEditorCommand.java b/plugin/src/main/java/net/momirealms/customfishing/command/sub/GUIEditorCommand.java index c78ceb63..3a75a624 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/command/sub/GUIEditorCommand.java +++ b/plugin/src/main/java/net/momirealms/customfishing/command/sub/GUIEditorCommand.java @@ -1,6 +1,10 @@ package net.momirealms.customfishing.command.sub; import dev.jorel.commandapi.CommandAPICommand; +import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.gui.SelectFileGUI; + +import java.io.File; public class GUIEditorCommand { @@ -8,15 +12,8 @@ public class GUIEditorCommand { public CommandAPICommand getEditorCommand() { return new CommandAPICommand("edit") - .withSubcommands( - - ); - } - - private CommandAPICommand getLootCommand() { - return new CommandAPICommand("loot") - .withSubcommands( - - ); + .executesPlayer((player, arg) -> { + new SelectFileGUI(player, new File(CustomFishingPlugin.get().getDataFolder(), "contents")); + }); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/ItemEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/ItemEditor.java new file mode 100644 index 00000000..e76ec520 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/gui/ItemEditor.java @@ -0,0 +1,171 @@ +package net.momirealms.customfishing.gui; + +import net.momirealms.customfishing.adventure.AdventureManagerImpl; +import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; +import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.api.util.LogUtils; +import net.momirealms.customfishing.gui.icon.BackGroundItem; +import net.momirealms.customfishing.gui.icon.BackToFolderItem; +import net.momirealms.customfishing.gui.icon.NextPageItem; +import net.momirealms.customfishing.gui.icon.PreviousPageItem; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.invui.gui.Gui; +import xyz.xenondevs.invui.gui.PagedGui; +import xyz.xenondevs.invui.gui.structure.Markers; +import xyz.xenondevs.invui.item.Item; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.AbstractItem; +import xyz.xenondevs.invui.item.impl.SimpleItem; +import xyz.xenondevs.invui.window.AnvilWindow; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class ItemEditor { + + private static final String SEARCH = "Search"; + private final Player player; + private final YamlConfiguration yaml; + private String prefix; + private final File file; + + public ItemEditor(Player player, File file) { + this.yaml = YamlConfiguration.loadConfiguration(file); + this.player = player; + this.file = file; + this.prefix = SEARCH; + this.reOpenWithFilter(); + } + + public void reOpenWithFilter() { + Item border = new SimpleItem(new ItemBuilder(Material.AIR)); + Gui upperGui = Gui.normal() + .setStructure( + "a # #" + ) + .addIngredient('a', new SimpleItem(new ItemBuilder(Material.NAME_TAG).setDisplayName(prefix))) + .addIngredient('#', border) + .build(); + + var gui = PagedGui.items() + .setStructure( + "x x x x x x x x x", + "x x x x x x x x x", + "x x x x x x x x x", + "# # a # c # b # #" + ) + .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) + .addIngredient('#', new BackGroundItem()) + .addIngredient('a', new PreviousPageItem()) + .addIngredient('b', new NextPageItem()) + .addIngredient('c', new BackToFolderItem(file.getParentFile())) + .setContent(getItemList()) + .build(); + + var temp = prefix; + var window = AnvilWindow.split() + .setViewer(player) + .setTitle(new ShadedAdventureComponentWrapper( + AdventureManagerImpl.getInstance().getComponentFromMiniMessage("Select item to edit") + )) + .addRenameHandler(s -> { + if (s.equals(temp)) return; + prefix = s; + reOpenWithFilter(); + }) + .setUpperGui(upperGui) + .setLowerGui(gui) + .build(); + + window.open(); + } + + public List getItemList() { + List itemList = new ArrayList<>(); + for (Map.Entry entry : this.yaml.getValues(false).entrySet()) { + String key = entry.getKey(); + if (entry.getValue() instanceof ConfigurationSection section) { + if (!prefix.equals(SEARCH) && !entry.getKey().startsWith(prefix)) continue; + String material = section.getString("material"); + if (material != null) { + if (material.contains(":")) { + ItemStack itemStack = CustomFishingPlugin.get().getItemManager().buildAnyPluginItemByID(player, material); + if (itemStack != null) { + ItemBuilder itemBuilder = new ItemBuilder(itemStack.getType()); + itemBuilder.setCustomModelData(itemStack.getItemMeta().getCustomModelData()); + itemBuilder.setCustomModelData(section.getInt("custom-model-data")); + itemList.add(new ItemInList(key, itemBuilder, this)); + continue; + } + } else { + ItemBuilder itemBuilder = new ItemBuilder(Material.valueOf(material.toUpperCase(Locale.ENGLISH))); + itemBuilder.setCustomModelData(section.getInt("custom-model-data")); + itemList.add(new ItemInList(key, itemBuilder, this)); + continue; + } + } + } + itemList.add(new ItemInList(key, new ItemBuilder(Material.STRUCTURE_VOID), this)); + } + return itemList; + } + + public void removeKey(String key) { + yaml.set(key, null); + } + + public void save() { + try { + yaml.save(file); + } catch (IOException e) { + LogUtils.warn("Failed to save file", e); + } + } + + public static class ItemInList extends AbstractItem { + + private final String key; + private final ItemBuilder itemBuilder; + private final ItemEditor itemEditor; + + public ItemInList(String key, ItemBuilder itemBuilder, ItemEditor itemEditor) { + this.key = key; + this.itemBuilder = itemBuilder; + this.itemEditor = itemEditor; + } + + @Override + public ItemProvider getItemProvider() { + return itemBuilder.setDisplayName(new ShadedAdventureComponentWrapper(AdventureManagerImpl.getInstance().getComponentFromMiniMessage( + key + ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureManagerImpl.getInstance().getComponentFromMiniMessage( + "Left click to edit" + ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureManagerImpl.getInstance().getComponentFromMiniMessage( + "Right click to delete" + ))); + } + + @Override + public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + if (clickType.isLeftClick()) { + + } else if (clickType.isRightClick()) { + this.itemEditor.removeKey(key); + this.itemEditor.save(); + this.itemEditor.reOpenWithFilter(); + } + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/SelectFileGUI.java b/plugin/src/main/java/net/momirealms/customfishing/gui/SelectFileGUI.java new file mode 100644 index 00000000..598d467b --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/gui/SelectFileGUI.java @@ -0,0 +1,123 @@ +package net.momirealms.customfishing.gui; + +import net.momirealms.customfishing.adventure.AdventureManagerImpl; +import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; +import net.momirealms.customfishing.gui.icon.*; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.invui.animation.impl.SequentialAnimation; +import xyz.xenondevs.invui.gui.Gui; +import xyz.xenondevs.invui.gui.ScrollGui; +import xyz.xenondevs.invui.gui.SlotElement; +import xyz.xenondevs.invui.gui.structure.Markers; +import xyz.xenondevs.invui.item.Item; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.AbstractItem; +import xyz.xenondevs.invui.window.Window; + +import java.io.File; +import java.util.ArrayDeque; +import java.util.Deque; + +public class SelectFileGUI { + + public SelectFileGUI(Player player, File folder) { + File[] files = folder.listFiles(); + Deque items = new ArrayDeque<>(); + if (files != null) { + for (File file : files) { + if (file.isFile() && file.getName().endsWith(".yml")) { + items.addLast(new FileItem(file)); + } else if (file.isDirectory()) { + items.addFirst(new FolderItem(file)); + } + } + } + + Gui gui = ScrollGui.items() + .setStructure( + "x x x x x x x x u", + "x x x x x x x x #", + "x x x x x x x x b", + "x x x x x x x x #", + "x x x x x x x x d" + ) + .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) + .addIngredient('#', new BackGroundItem()) + .addIngredient('u', new ScrollUpItem()) + .addIngredient('d', new ScrollDownItem()) + .addIngredient('b', new BackToFolderItem(folder.getParentFile())) + .setContent(items.stream().toList()) + .build(); + + Window window = Window.single() + .setViewer(player) + .setTitle(new ShadedAdventureComponentWrapper( + AdventureManagerImpl.getInstance().getComponentFromMiniMessage("Select file") + )) + .setGui(gui) + .build(); + + gui.playAnimation(new SequentialAnimation(1, true), slotElement -> { + if (slotElement instanceof SlotElement.ItemSlotElement itemSlotElement) { + return !(itemSlotElement.getItem() instanceof Icon); + } + return true; + }); + + window.open(); + } + + public static class FileItem extends AbstractItem { + + private final File file; + + public FileItem(File file) { + this.file = file; + } + + @Override + public ItemProvider getItemProvider() { + return new ItemBuilder(Material.PAPER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureManagerImpl.getInstance().getComponentFromMiniMessage( + "<#FDF5E6>" + file.getName() + ))); + } + + @Override + public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + String path = file.getPath(); + String[] split = path.split("\\\\"); + String type = split[3]; + switch (type) { + case "item" -> { + new ItemEditor(player, file); + } + } + } + } + + public static class FolderItem extends AbstractItem { + + private final File file; + + public FolderItem(File file) { + this.file = file; + } + + @Override + public ItemProvider getItemProvider() { + return new ItemBuilder(Material.BOOK).setDisplayName(new ShadedAdventureComponentWrapper(AdventureManagerImpl.getInstance().getComponentFromMiniMessage( + "<#D2B48C>" + file.getName() + ))); + } + + @Override + public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + new SelectFileGUI(player, file); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackGroundItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackGroundItem.java new file mode 100644 index 00000000..10e88ea3 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackGroundItem.java @@ -0,0 +1,23 @@ +package net.momirealms.customfishing.gui.icon; + +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.AbstractItem; + +public class BackGroundItem extends AbstractItem implements Icon { + + @Override + public ItemProvider getItemProvider() { + return new ItemBuilder(Material.BLACK_STAINED_GLASS_PANE); + } + + @Override + public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + + } +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackToFolderItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackToFolderItem.java new file mode 100644 index 00000000..03fe3878 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackToFolderItem.java @@ -0,0 +1,46 @@ +package net.momirealms.customfishing.gui.icon; + +import net.momirealms.customfishing.adventure.AdventureManagerImpl; +import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; +import net.momirealms.customfishing.gui.SelectFileGUI; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.AbstractItem; + +import java.io.File; +import java.util.List; + +public class BackToFolderItem extends AbstractItem implements Icon { + + private final File file; + + public BackToFolderItem(File file) { + this.file = file; + } + + @Override + public ItemProvider getItemProvider() { + if (file != null && file.getPath().startsWith("plugins\\CustomFishing\\contents")) { + return new ItemBuilder(Material.ORANGE_STAINED_GLASS_PANE) + .setDisplayName(new ShadedAdventureComponentWrapper(AdventureManagerImpl.getInstance().getComponentFromMiniMessage( + "<#FF8C00>Back to parent folder" + ))) + .setLore(List.of(new ShadedAdventureComponentWrapper(AdventureManagerImpl.getInstance().getComponentFromMiniMessage( + "<#FFA500>-> " + file.getName() + )))); + } else { + return new ItemBuilder(Material.BLACK_STAINED_GLASS_PANE); + } + } + + @Override + public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + if (file != null && file.getPath().startsWith("plugins\\CustomFishing\\contents")) + new SelectFileGUI(player, file); + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/Icon.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/Icon.java new file mode 100644 index 00000000..26e581bd --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/Icon.java @@ -0,0 +1,4 @@ +package net.momirealms.customfishing.gui.icon; + +public interface Icon { +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/NextPageItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/NextPageItem.java new file mode 100644 index 00000000..62e618da --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/NextPageItem.java @@ -0,0 +1,25 @@ +package net.momirealms.customfishing.gui.icon; + +import org.bukkit.Material; +import xyz.xenondevs.invui.gui.PagedGui; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.controlitem.PageItem; + +public class NextPageItem extends PageItem implements Icon { + + public NextPageItem() { + super(true); + } + + @Override + public ItemProvider getItemProvider(PagedGui gui) { + ItemBuilder builder = new ItemBuilder(Material.GREEN_STAINED_GLASS_PANE); + builder.setDisplayName("§7Next page") + .addLoreLines(gui.hasNextPage() + ? "§7Go to page §e" + (gui.getCurrentPage() + 2) + "§7/§e" + gui.getPageAmount() + : "§cThere are no more pages"); + + return builder; + } +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/PreviousPageItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/PreviousPageItem.java new file mode 100644 index 00000000..41fb3c38 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/PreviousPageItem.java @@ -0,0 +1,24 @@ +package net.momirealms.customfishing.gui.icon; + +import org.bukkit.Material; +import xyz.xenondevs.invui.gui.PagedGui; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.controlitem.PageItem; + +public class PreviousPageItem extends PageItem implements Icon { + + public PreviousPageItem() { + super(false); + } + + @Override + public ItemProvider getItemProvider(PagedGui gui) { + ItemBuilder builder = new ItemBuilder(Material.RED_STAINED_GLASS_PANE); + builder.setDisplayName("§7Previous page") + .addLoreLines(gui.hasPreviousPage() + ? "§7Go to page §e" + gui.getCurrentPage() + "§7/§e" + gui.getPageAmount() + : "§cYou can't go further back"); + return builder; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/ScrollDownItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/ScrollDownItem.java new file mode 100644 index 00000000..ea6ed66c --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/ScrollDownItem.java @@ -0,0 +1,23 @@ +package net.momirealms.customfishing.gui.icon; + +import org.bukkit.Material; +import xyz.xenondevs.invui.gui.ScrollGui; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.controlitem.ScrollItem; + +public class ScrollDownItem extends ScrollItem implements Icon { + + public ScrollDownItem() { + super(1); + } + + @Override + public ItemProvider getItemProvider(ScrollGui gui) { + ItemBuilder builder = new ItemBuilder(Material.GREEN_STAINED_GLASS_PANE); + builder.setDisplayName("§7Scroll down"); + if (!gui.canScroll(1)) + builder.addLoreLines("§cYou can't scroll further down"); + return builder; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/ScrollUpItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/ScrollUpItem.java new file mode 100644 index 00000000..d3619dda --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/ScrollUpItem.java @@ -0,0 +1,24 @@ +package net.momirealms.customfishing.gui.icon; + +import org.bukkit.Material; +import xyz.xenondevs.invui.gui.ScrollGui; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.controlitem.ScrollItem; + +public class ScrollUpItem extends ScrollItem implements Icon { + + public ScrollUpItem() { + super(-1); + } + + @Override + public ItemProvider getItemProvider(ScrollGui gui) { + ItemBuilder builder = new ItemBuilder(Material.RED_STAINED_GLASS_PANE); + builder.setDisplayName("§7Scroll up"); + if (!gui.canScroll(-1)) + builder.addLoreLines("§cYou've reached the top"); + + return builder; + } +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/ranking/LocalRankingImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/ranking/LocalRankingImpl.java index c7905b8f..8b00564d 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/ranking/LocalRankingImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/ranking/LocalRankingImpl.java @@ -20,7 +20,6 @@ package net.momirealms.customfishing.mechanic.competition.ranking; import net.momirealms.customfishing.api.common.Pair; import net.momirealms.customfishing.api.mechanic.competition.CompetitionPlayer; import net.momirealms.customfishing.api.mechanic.competition.Ranking; -import org.jetbrains.annotations.Nullable; import java.util.*; diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/ranking/RedisRankingImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/ranking/RedisRankingImpl.java index e8b628ea..4d12d973 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/ranking/RedisRankingImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/ranking/RedisRankingImpl.java @@ -21,7 +21,6 @@ import net.momirealms.customfishing.api.common.Pair; import net.momirealms.customfishing.api.mechanic.competition.CompetitionPlayer; import net.momirealms.customfishing.api.mechanic.competition.Ranking; import net.momirealms.customfishing.storage.method.database.nosql.RedisManager; -import org.jetbrains.annotations.Nullable; import redis.clients.jedis.Jedis; import redis.clients.jedis.resps.Tuple;