From 648316468043dfd2610acc22be18b07b665c7599 Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Fri, 19 Sep 2025 23:20:55 +0200 Subject: [PATCH 1/4] GH-959 Move warp contents from language files. --- ...ove_WarpInventory_to_dedicated_config.java | 21 +++ .../configuration/migrations/Migrations.java | 3 +- .../feature/warp/command/DelWarpCommand.java | 2 +- .../feature/warp/command/SetWarpCommand.java | 2 +- .../feature/warp/command/WarpCommand.java | 2 +- .../warp/{ => inventory}/WarpInventory.java | 160 +++++++++--------- .../warp/inventory/WarpInventoryConfig.java | 97 +++++++++++ .../inventory/WarpInventoryConfigService.java | 66 ++++++++ .../inventory/WarpInventoryRepository.java | 18 ++ .../WarpInventoryRepositoryImpl.java | 78 +++++++++ .../feature/warp/messages/ENWarpMessages.java | 49 ------ .../feature/warp/messages/PLWarpMessages.java | 51 ------ .../feature/warp/messages/WarpMessages.java | 53 ------ 13 files changed, 367 insertions(+), 235 deletions(-) create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migration_0010_Move_WarpInventory_to_dedicated_config.java rename eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/{ => inventory}/WarpInventory.java (60%) create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfig.java create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfigService.java create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepository.java create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepositoryImpl.java diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migration_0010_Move_WarpInventory_to_dedicated_config.java b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migration_0010_Move_WarpInventory_to_dedicated_config.java new file mode 100644 index 000000000..f505bf0b8 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migration_0010_Move_WarpInventory_to_dedicated_config.java @@ -0,0 +1,21 @@ +package com.eternalcode.core.configuration.migrations; + +import static eu.okaeri.configs.migrate.ConfigMigrationDsl.move; + +import eu.okaeri.configs.migrate.builtin.NamedMigration; + +public class Migration_0010_Move_WarpInventory_to_dedicated_config extends NamedMigration { + Migration_0010_Move_WarpInventory_to_dedicated_config() { + super( + "Move WarpInventory items from messages to dedicated warp-inventory.yml config and clean up old fields", + move("warpInventory.title", "warpInventory.display.title"), + move("warpInventory.border.enabled", "warpInventory.border.enabled"), + move("warpInventory.border.material", "warpInventory.border.material"), + move("warpInventory.border.fillType", "warpInventory.border.fillType"), + move("warpInventory.border.name", "warpInventory.border.name"), + move("warpInventory.border.lore", "warpInventory.border.lore"), + move("warpInventory.decorationItems.items", "warpInventory.decorationItems.items"), + move("warpInventory.items", "warpInventory.items") + ); + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migrations.java b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migrations.java index f9d0748b1..ea54e27d5 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migrations.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migrations.java @@ -11,7 +11,8 @@ public class Migrations { new Migration_0006_Move_alert_to_broadcast_section(), new Migration_0007_Move_clear_to_dedicated_section(), new Migration_0008_Move_repair_to_dedicated_section(), - new Migration_0009_Improve_Homes_Config() + new Migration_0009_Improve_Homes_Config(), + new Migration_0010_Move_WarpInventory_to_dedicated_config() }; } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/DelWarpCommand.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/DelWarpCommand.java index 54aaf7e91..fc2a885c0 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/DelWarpCommand.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/DelWarpCommand.java @@ -2,7 +2,7 @@ import com.eternalcode.annotations.scan.command.DescriptionDocs; import com.eternalcode.core.feature.warp.Warp; -import com.eternalcode.core.feature.warp.WarpInventory; +import com.eternalcode.core.feature.warp.inventory.WarpInventory; import com.eternalcode.core.feature.warp.WarpService; import com.eternalcode.core.injector.annotations.Inject; import com.eternalcode.core.notice.NoticeService; diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/SetWarpCommand.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/SetWarpCommand.java index 8698a24ca..9ac1073a2 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/SetWarpCommand.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/SetWarpCommand.java @@ -2,7 +2,7 @@ import com.eternalcode.annotations.scan.command.DescriptionDocs; import com.eternalcode.core.feature.warp.Warp; -import com.eternalcode.core.feature.warp.WarpInventory; +import com.eternalcode.core.feature.warp.inventory.WarpInventory; import com.eternalcode.core.feature.warp.WarpService; import com.eternalcode.core.feature.warp.WarpSettings; import com.eternalcode.core.injector.annotations.Inject; diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/WarpCommand.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/WarpCommand.java index 18a21392a..1e12cb5af 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/WarpCommand.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/WarpCommand.java @@ -3,7 +3,7 @@ import com.eternalcode.annotations.scan.command.DescriptionDocs; import com.eternalcode.core.configuration.implementation.PluginConfiguration; import com.eternalcode.core.feature.warp.Warp; -import com.eternalcode.core.feature.warp.WarpInventory; +import com.eternalcode.core.feature.warp.inventory.WarpInventory; import com.eternalcode.core.feature.warp.WarpService; import com.eternalcode.core.feature.warp.WarpSettings; import com.eternalcode.core.feature.warp.WarpTeleportService; diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/WarpInventory.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventory.java similarity index 60% rename from eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/WarpInventory.java rename to eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventory.java index 8a250463d..6fde89d5f 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/WarpInventory.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventory.java @@ -1,16 +1,16 @@ -package com.eternalcode.core.feature.warp; +package com.eternalcode.core.feature.warp.inventory; import com.eternalcode.commons.adventure.AdventureUtil; +import com.eternalcode.commons.concurrent.FutureHandler; import com.eternalcode.commons.scheduler.Scheduler; -import com.eternalcode.core.configuration.ConfigurationManager; import com.eternalcode.core.configuration.contextual.ConfigItem; -import com.eternalcode.core.feature.warp.messages.WarpMessages; -import com.eternalcode.core.feature.warp.messages.WarpMessages.WarpInventorySection; +import com.eternalcode.core.feature.warp.Warp; +import com.eternalcode.core.feature.warp.WarpInventoryItem; +import com.eternalcode.core.feature.warp.WarpService; +import com.eternalcode.core.feature.warp.WarpSettings; +import com.eternalcode.core.feature.warp.WarpTeleportService; import com.eternalcode.core.injector.annotations.Inject; import com.eternalcode.core.injector.annotations.component.Service; -import com.eternalcode.core.translation.AbstractTranslation; -import com.eternalcode.core.translation.Translation; -import com.eternalcode.core.translation.TranslationManager; import dev.triumphteam.gui.builder.item.BaseItemBuilder; import dev.triumphteam.gui.builder.item.ItemBuilder; import dev.triumphteam.gui.guis.Gui; @@ -39,75 +39,75 @@ public class WarpInventory { private static final int BORDER_ROW_COUNT = 2; private static final int UGLY_BORDER_ROW_COUNT = 1; - private final TranslationManager translationManager; private final WarpService warpService; private final Server server; private final MiniMessage miniMessage; private final WarpTeleportService warpTeleportService; - private final ConfigurationManager configurationManager; private final WarpSettings warpSettings; private final Scheduler scheduler; + private final WarpInventoryConfigService warpInventoryConfigService; @Inject WarpInventory( - TranslationManager translationManager, WarpService warpService, Server server, MiniMessage miniMessage, WarpTeleportService warpTeleportService, - ConfigurationManager configurationManager, WarpSettings warpSettings, - Scheduler scheduler + Scheduler scheduler, + WarpInventoryConfigService warpInventoryConfigService ) { - this.translationManager = translationManager; this.warpService = warpService; this.server = server; this.miniMessage = miniMessage; this.warpTeleportService = warpTeleportService; - this.configurationManager = configurationManager; this.warpSettings = warpSettings; this.scheduler = scheduler; + this.warpInventoryConfigService = warpInventoryConfigService; } public void openInventory(Player player) { - Gui gui = this.createInventory(player); - this.scheduler.run(() -> gui.open(player)); + this.warpInventoryConfigService.getWarpInventoryData() + .thenAccept(warpData -> { + this.scheduler.run(() -> { + Gui gui = this.createInventory(player, warpData); + gui.open(player); + }); + }) + .exceptionally(FutureHandler::handleException); } - private Gui createInventory(Player player) { - Translation translation = this.translationManager.getMessages(); - WarpMessages.WarpInventorySection warpSection = translation.warp().warpInventory(); - + private Gui createInventory(Player player, WarpInventoryConfigService.WarpInventoryConfigData warpData) { int rowsCount; - int size = warpSection.items().size(); + int size = warpData.items().size(); - if (!warpSection.border().enabled()) { + if (!warpData.border().enabled()) { rowsCount = (size + 1) / GUI_ROW_SIZE_WITHOUT_BORDER + 1; } else { - switch (warpSection.border().fillType()) { + switch (warpData.border().fillType()) { case BORDER, ALL -> rowsCount = (size - 1) / GUI_ROW_SIZE_WITH_BORDER + 1 + BORDER_ROW_COUNT; case TOP, BOTTOM -> rowsCount = (size - 1) / GUI_ROW_SIZE_WITHOUT_BORDER + 1 + UGLY_BORDER_ROW_COUNT; - default -> throw new IllegalStateException("Unexpected value: " + warpSection.border().fillType()); + default -> throw new IllegalStateException("Unexpected value: " + warpData.border().fillType()); } } Gui gui = Gui.gui() - .title(this.miniMessage.deserialize(warpSection.title())) + .title(this.miniMessage.deserialize(warpData.title())) .rows(rowsCount) .disableAllInteractions() .create(); - this.createWarpItems(player, warpSection, gui); - this.createBorder(warpSection, gui); - this.createDecorations(warpSection, gui); + this.createWarpItems(player, warpData, gui); + this.createBorder(warpData, gui); + this.createDecorations(warpData, gui); return gui; } - private void createBorder(WarpInventorySection warpSection, Gui gui) { - if (warpSection.border().enabled()) { - WarpInventorySection.BorderSection borderSection = warpSection.border(); + private void createBorder(WarpInventoryConfigService.WarpInventoryConfigData warpData, Gui gui) { + if (warpData.border().enabled()) { + WarpInventoryConfig.BorderSection borderSection = warpData.border(); ItemBuilder borderItem = ItemBuilder.from(borderSection.material()); @@ -134,8 +134,8 @@ private void createBorder(WarpInventorySection warpSection, Gui gui) { } } - private void createDecorations(WarpInventorySection warpSection, Gui gui) { - for (ConfigItem item : warpSection.decorationItems().items()) { + private void createDecorations(WarpInventoryConfigService.WarpInventoryConfigData warpData, Gui gui) { + for (ConfigItem item : warpData.decorationItems().items()) { BaseItemBuilder baseItemBuilder = this.createItem(item); GuiItem guiItem = baseItemBuilder.asGuiItem(); @@ -157,8 +157,8 @@ private void createDecorations(WarpInventorySection warpSection, Gui gui) { } } - private void createWarpItems(Player player, WarpInventorySection warpSection, Gui gui) { - warpSection.items().values().forEach(item -> { + private void createWarpItems(Player player, WarpInventoryConfigService.WarpInventoryConfigData warpData, Gui gui) { + warpData.items().values().forEach(item -> { Optional warpOptional = this.warpService.findWarp(item.warpName()); if (warpOptional.isEmpty()) { @@ -215,34 +215,35 @@ public void addWarp(Warp warp) { return; } - AbstractTranslation translation = (AbstractTranslation) this.translationManager.getMessages(); - WarpMessages.WarpInventorySection warpSection = translation.warp().warpInventory(); - int slot = getSlot(warpSection); - - warpSection.addItem( - warp.getName(), - WarpInventoryItem.builder() - .withWarpName(warp.getName()) - .withWarpItem(ConfigItem.builder() - .withName(this.warpSettings.itemNamePrefix() + warp.getName()) - .withLore(Collections.singletonList(this.warpSettings.itemLore())) - .withMaterial(this.warpSettings.itemMaterial()) - .withTexture(this.warpSettings.itemTexture()) - .withSlot(slot) - .withGlow(true) - .build()) - .build()); - - this.configurationManager.save(translation); + this.warpInventoryConfigService.getWarpInventoryData() + .thenAccept(warpData -> { + int slot = getSlot(warpData); + + WarpInventoryItem warpInventoryItem = WarpInventoryItem.builder() + .withWarpName(warp.getName()) + .withWarpItem(ConfigItem.builder() + .withName(this.warpSettings.itemNamePrefix() + warp.getName()) + .withLore(Collections.singletonList(this.warpSettings.itemLore())) + .withMaterial(this.warpSettings.itemMaterial()) + .withTexture(this.warpSettings.itemTexture()) + .withSlot(slot) + .withGlow(true) + .build()) + .build(); + + this.warpInventoryConfigService.addWarpItem(warp.getName(), warpInventoryItem) + .exceptionally(FutureHandler::handleException); + }) + .exceptionally(FutureHandler::handleException); } - private int getSlot(WarpMessages.WarpInventorySection warpSection) { - int size = warpSection.items().size(); - if (!warpSection.border().enabled()) { + private int getSlot(WarpInventoryConfigService.WarpInventoryConfigData warpData) { + int size = warpData.items().size(); + if (!warpData.border().enabled()) { return GUI_ITEM_SLOT_WITHOUT_BORDER + size; } - return switch (warpSection.border().fillType()) { + return switch (warpData.border().fillType()) { case BORDER -> GUI_ITEM_SLOT_WITH_BORDER + size + ((size / WarpInventory.GUI_ROW_SIZE_WITH_BORDER) * 2); case ALL -> GUI_ITEM_SLOT_WITH_ALL_BORDER + size + ((size / WarpInventory.GUI_ROW_SIZE_WITH_BORDER) * 2); case TOP -> GUI_ITEM_SLOT_WITH_TOP_BORDER + size; @@ -255,29 +256,32 @@ public void removeWarp(String warpName) { return; } - AbstractTranslation translation = (AbstractTranslation) this.translationManager.getMessages(); - WarpMessages.WarpInventorySection warpSection = translation.warp().warpInventory(); - WarpInventoryItem removed = warpSection.removeItem(warpName); - - if (removed != null) { - this.shiftWarpItems(removed, warpSection); - } - - this.configurationManager.save(translation); + this.warpInventoryConfigService.removeWarpItem(warpName) + .thenAccept(removed -> { + if (removed != null) { + this.shiftWarpItems(removed); + } + }) + .exceptionally(FutureHandler::handleException); } - private void shiftWarpItems(WarpInventoryItem removed, WarpMessages.WarpInventorySection warpSection) { + private void shiftWarpItems(WarpInventoryItem removed) { int removedSlot = removed.warpItem.slot; - List itemsToShift = warpSection.items().values().stream() - .filter(item -> item.warpItem.slot > removedSlot) - .sorted(Comparator.comparingInt(item -> item.warpItem.slot)) - .toList(); - int currentShift = removedSlot; - for (WarpInventoryItem item : itemsToShift) { - int nextShift = item.warpItem.slot; - item.warpItem.slot = currentShift; - currentShift = nextShift; - } + this.warpInventoryConfigService.getWarpItems() + .thenAccept(items -> { + List itemsToShift = items.values().stream() + .filter(item -> item.warpItem.slot > removedSlot) + .sorted(Comparator.comparingInt(item -> item.warpItem.slot)) + .toList(); + + int currentShift = removedSlot; + for (WarpInventoryItem item : itemsToShift) { + int nextShift = item.warpItem.slot; + item.warpItem.slot = currentShift; + currentShift = nextShift; + } + }) + .exceptionally(FutureHandler::handleException); } } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfig.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfig.java new file mode 100644 index 000000000..73958575e --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfig.java @@ -0,0 +1,97 @@ +package com.eternalcode.core.feature.warp.inventory; + +import com.eternalcode.core.configuration.AbstractConfigurationFile; +import com.eternalcode.core.configuration.contextual.ConfigItem; +import com.eternalcode.core.feature.warp.WarpInventoryItem; +import com.eternalcode.core.injector.annotations.component.ConfigurationFile; +import eu.okaeri.configs.OkaeriConfig; +import eu.okaeri.configs.annotation.Comment; +import java.io.File; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; +import lombok.experimental.Accessors; +import org.bukkit.Material; + +@Getter +@Accessors(fluent = true) +@ConfigurationFile +public class WarpInventoryConfig extends AbstractConfigurationFile { + + @Comment({" ", + "# Warp inventory configuration", + "# This file contains the GUI layout and item definitions for the warp inventory", + "# Text content (titles, names, lore) should be configured in language files"}) + public DisplaySection display = new DisplaySection(); + + @Comment({" ", + "# Border configuration for the warp inventory"}) + public BorderSection border = new BorderSection(); + + @Comment({" ", + "# Decoration items that can be placed in the inventory"}) + public DecorationItemsSection decorationItems = new DecorationItemsSection(); + + @Comment({" ", + "# Warp items configuration - maps warp names to their inventory representation"}) + public Map items = new HashMap<>(); + + @Override + public File getConfigFile(File dataFolder) { + return new File(dataFolder, "warp-inventory.yml"); + } + + @Getter + @Accessors(fluent = true) + public static class DisplaySection extends OkaeriConfig { + @Comment("# Title of the warp inventory GUI") + public String title = "» Available warps:"; + } + + @Getter + @Accessors(fluent = true) + public static class BorderSection extends OkaeriConfig { + @Comment({" ", + "# Changes of border section may affect the appearance of the GUI inventory, after changes adjust slots of existing items."}) + public boolean enabled = true; + + public Material material = Material.GRAY_STAINED_GLASS_PANE; + + public FillType fillType = FillType.BORDER; + + public String name = ""; + + public List lore = Collections.emptyList(); + } + + @Getter + @Accessors(fluent = true) + public static class DecorationItemsSection extends OkaeriConfig { + public List items = List.of(); + } + + public enum FillType { + BORDER, + ALL, + TOP, + BOTTOM + } + + public void setItems(Map items) { + this.items = items; + } + + public void addItem(String warpName, WarpInventoryItem item) { + this.items.put(warpName, item); + } + + public WarpInventoryItem removeItem(String warpName) { + return this.items.remove(warpName); + } + + public Map getItems() { + return this.items; + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfigService.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfigService.java new file mode 100644 index 000000000..2f4650a76 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfigService.java @@ -0,0 +1,66 @@ +package com.eternalcode.core.feature.warp.inventory; + +import com.eternalcode.core.feature.warp.WarpInventoryItem; +import com.eternalcode.core.feature.warp.inventory.WarpInventoryConfig.BorderSection; +import com.eternalcode.core.feature.warp.inventory.WarpInventoryConfig.DecorationItemsSection; +import com.eternalcode.core.injector.annotations.Inject; +import com.eternalcode.core.injector.annotations.component.Service; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +@Service +public class WarpInventoryConfigService { + + private final WarpInventoryRepository warpInventoryRepository; + private final WarpInventoryConfig warpInventoryConfig; + + @Inject + public WarpInventoryConfigService( + WarpInventoryRepository warpInventoryRepository, + WarpInventoryConfig warpInventoryConfig + ) { + this.warpInventoryRepository = warpInventoryRepository; + this.warpInventoryConfig = warpInventoryConfig; + } + + public CompletableFuture getWarpInventoryData() { + return this.warpInventoryRepository.getAllWarpInventoryItems() + .thenApply(items -> { + return new WarpInventoryConfigData( + this.warpInventoryConfig.display().title(), + this.warpInventoryConfig.border(), + this.warpInventoryConfig.decorationItems(), + items + ); + }); + } + + public CompletableFuture addWarpItem(String warpName, WarpInventoryItem item) { + return this.warpInventoryRepository.saveWarpInventoryItem(warpName, item); + } + + public CompletableFuture removeWarpItem(String warpName) { + return this.warpInventoryRepository.getWarpInventoryItem(warpName) + .thenCompose(item -> { + if (item != null) { + return this.warpInventoryRepository.removeWarpInventoryItem(warpName) + .thenApply(v -> item); + } + return CompletableFuture.completedFuture(null); + }); + } + + public CompletableFuture> getWarpItems() { + return this.warpInventoryRepository.getAllWarpInventoryItems(); + } + + + public record WarpInventoryConfigData( + String title, + BorderSection border, + DecorationItemsSection decorationItems, + Map items + ) { + + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepository.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepository.java new file mode 100644 index 000000000..6a19cf91e --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepository.java @@ -0,0 +1,18 @@ +package com.eternalcode.core.feature.warp.inventory; + +import com.eternalcode.core.feature.warp.WarpInventoryItem; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +public interface WarpInventoryRepository { + + CompletableFuture saveWarpInventoryItem(String warpName, WarpInventoryItem item); + + CompletableFuture removeWarpInventoryItem(String warpName); + + CompletableFuture getWarpInventoryItem(String warpName); + + CompletableFuture> getAllWarpInventoryItems(); + + WarpInventoryConfig getConfig(); +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepositoryImpl.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepositoryImpl.java new file mode 100644 index 000000000..f32f57643 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepositoryImpl.java @@ -0,0 +1,78 @@ +package com.eternalcode.core.feature.warp.inventory; + +import com.eternalcode.commons.scheduler.Scheduler; +import com.eternalcode.core.configuration.ConfigurationManager; +import com.eternalcode.core.feature.warp.WarpInventoryItem; +import com.eternalcode.core.injector.annotations.Inject; +import com.eternalcode.core.injector.annotations.component.Repository; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Function; + +@Repository +class WarpInventoryRepositoryImpl implements WarpInventoryRepository { + + private static final Object READ_WRITE_LOCK = new Object(); + + private final WarpInventoryConfig warpInventoryConfig; + private final ConfigurationManager configurationManager; + private final Scheduler scheduler; + + @Inject + WarpInventoryRepositoryImpl( + ConfigurationManager configurationManager, + WarpInventoryConfig warpInventoryConfig, + Scheduler scheduler + ) { + this.configurationManager = configurationManager; + this.warpInventoryConfig = warpInventoryConfig; + this.scheduler = scheduler; + } + + @Override + public CompletableFuture saveWarpInventoryItem(String warpName, WarpInventoryItem item) { + return this.transactionalRun(items -> items.put(warpName, item)); + } + + @Override + public CompletableFuture removeWarpInventoryItem(String warpName) { + return this.transactionalRun(items -> items.remove(warpName)); + } + + @Override + public CompletableFuture getWarpInventoryItem(String warpName) { + return this.transactionalSupply(items -> Optional.ofNullable(items.get(warpName)).orElse(null)); + } + + @Override + public CompletableFuture> getAllWarpInventoryItems() { + return this.transactionalSupply(items -> new HashMap<>(items)); + } + + @Override + public WarpInventoryConfig getConfig() { + return this.warpInventoryConfig; + } + + private CompletableFuture transactionalRun(Consumer> editor) { + return this.transactionalSupply(items -> { + editor.accept(items); + return null; + }); + } + + private CompletableFuture transactionalSupply(Function, T> editor) { + return this.scheduler.completeAsync(() -> { + synchronized (READ_WRITE_LOCK) { + Map items = new HashMap<>(this.warpInventoryConfig.getItems()); + T result = editor.apply(items); + this.warpInventoryConfig.setItems(items); + this.configurationManager.save(this.warpInventoryConfig); + return result; + } + }); + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/messages/ENWarpMessages.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/messages/ENWarpMessages.java index e28152d22..040b6e5e7 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/messages/ENWarpMessages.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/messages/ENWarpMessages.java @@ -1,17 +1,10 @@ package com.eternalcode.core.feature.warp.messages; -import com.eternalcode.core.configuration.contextual.ConfigItem; -import com.eternalcode.core.feature.warp.WarpInventoryItem; import com.eternalcode.multification.notice.Notice; import eu.okaeri.configs.OkaeriConfig; import eu.okaeri.configs.annotation.Comment; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import lombok.Getter; import lombok.experimental.Accessors; -import org.bukkit.Material; @Getter @Accessors(fluent = true) @@ -40,46 +33,4 @@ public class ENWarpMessages extends OkaeriConfig implements WarpMessages { @Comment({" ", "# {WARPS} - List of warps (separated by commas)"}) public Notice available = Notice.chat("Available warps: {WARPS}"); - - @Comment({" ", "# Settings for warp inventory"}) - public ENWarpInventory warpInventory = new ENWarpInventory(); - - @Getter - @Accessors(fluent = true) - public static class ENWarpInventory extends OkaeriConfig implements WarpInventorySection { - public String title = "» Available warps:"; - - @Comment({" ", - "# Warps located inside GUI inventory can be customized here. More warps will be added on creation with /setwarp command. "}) - public Map items = new HashMap<>(); - - public void setItems(Map items) { - this.items = items; - } - - public ENWarpInventory.ENBorderSection border = new ENWarpInventory.ENBorderSection(); - public ENWarpInventory.ENDecorationItemsSection decorationItems = - new ENWarpInventory.ENDecorationItemsSection(); - - @Getter - public static class ENBorderSection extends OkaeriConfig implements BorderSection { - - @Comment({" ", - "# Changes of border section may affect the appearance of the GUI inventory, after changes adjust slots of existing items."}) - public boolean enabled = true; - - public Material material = Material.GRAY_STAINED_GLASS_PANE; - - public FillType fillType = FillType.BORDER; - - public String name = ""; - - public List lore = Collections.emptyList(); - } - - @Getter - public static class ENDecorationItemsSection extends OkaeriConfig implements DecorationItemsSection { - public List items = List.of(); - } - } } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/messages/PLWarpMessages.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/messages/PLWarpMessages.java index 9666e603a..863337e8f 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/messages/PLWarpMessages.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/messages/PLWarpMessages.java @@ -1,17 +1,10 @@ package com.eternalcode.core.feature.warp.messages; -import com.eternalcode.core.configuration.contextual.ConfigItem; -import com.eternalcode.core.feature.warp.WarpInventoryItem; import com.eternalcode.multification.notice.Notice; import eu.okaeri.configs.OkaeriConfig; import eu.okaeri.configs.annotation.Comment; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import lombok.Getter; import lombok.experimental.Accessors; -import org.bukkit.Material; @Getter @Accessors(fluent = true) @@ -38,48 +31,4 @@ public class PLWarpMessages extends OkaeriConfig implements WarpMessages { @Comment({" ", "# {WARPS} - Lista dostępnych warpów"}) public Notice available = Notice.chat("Dostepne warpy: {WARPS}!"); - - @Comment({" ", "# Ustawienia gui listy dostępnych warpów"}) - public PLWarpInventory warpInventory = new PLWarpInventory(); - - @Getter - @Accessors(fluent = true) - public static class PLWarpInventory extends OkaeriConfig implements WarpInventorySection { - public String title = "» Lista dostępnych warpów"; - - @Comment({ - " ", - "# Poniższa lista określa przedmioty w GUI, które są wyświetlane w liście dostępnych warpów.", - "# Możesz edytować przedmioty, a dodawanie kolejnych warpów następuje automatycznie za pomocą komendy /setwarp", - }) - public Map items = new HashMap<>(); - - public void setItems(Map items) { - this.items = items; - } - - public PLWarpInventory.PLBorderSection border = new PLWarpInventory.PLBorderSection(); - public PLWarpInventory.PLDecorationItemsSection decorationItems = - new PLWarpInventory.PLDecorationItemsSection(); - - @Getter - public static class PLBorderSection extends OkaeriConfig implements BorderSection { - @Comment({" ", - "# Zmiany w tej sekcji mogą wpłynąć na wygląd GUI, zwróć uwagę na zmiany slotów przedmiotów w GUI."}) - public boolean enabled = true; - - public Material material = Material.GRAY_STAINED_GLASS_PANE; - - public FillType fillType = FillType.BORDER; - - public String name = ""; - - public List lore = Collections.emptyList(); - } - - @Getter - public static class PLDecorationItemsSection extends OkaeriConfig implements DecorationItemsSection { - public List items = List.of(); - } - } } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/messages/WarpMessages.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/messages/WarpMessages.java index 6c5f9f7f5..9eabdfaa0 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/messages/WarpMessages.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/messages/WarpMessages.java @@ -1,12 +1,6 @@ package com.eternalcode.core.feature.warp.messages; -import com.eternalcode.core.configuration.contextual.ConfigItem; -import com.eternalcode.core.feature.warp.WarpInventoryItem; import com.eternalcode.multification.notice.Notice; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.bukkit.Material; public interface WarpMessages { Notice warpAlreadyExists(); @@ -25,51 +19,4 @@ public interface WarpMessages { Notice noPermissionsProvided(); Notice missingWarpArgument(); Notice missingPermissionArgument(); - - WarpInventorySection warpInventory(); - - interface WarpInventorySection { - String title(); - - Map items(); - void setItems(Map items); - - default void addItem(String name, WarpInventoryItem item) { - Map items = new HashMap<>(this.items()); - items.put(name, item); - - this.setItems(items); - } - - default WarpInventoryItem removeItem(String name) { - Map items = new HashMap<>(this.items()); - WarpInventoryItem removed = items.remove(name); - - this.setItems(items); - return removed; - } - - BorderSection border(); - DecorationItemsSection decorationItems(); - - interface BorderSection { - boolean enabled(); - - Material material(); - - FillType fillType(); - - String name(); - - List lore(); - - enum FillType { - TOP, BOTTOM, BORDER, ALL - } - } - - interface DecorationItemsSection { - List items(); - } - } } From d9617b8264d463419285cdfce8bf82cacc7c1cf2 Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Fri, 19 Sep 2025 23:31:31 +0200 Subject: [PATCH 2/4] Fix. --- ...ove_WarpInventory_to_dedicated_config.java | 21 ------------------- .../configuration/migrations/Migrations.java | 3 +-- 2 files changed, 1 insertion(+), 23 deletions(-) delete mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migration_0010_Move_WarpInventory_to_dedicated_config.java diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migration_0010_Move_WarpInventory_to_dedicated_config.java b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migration_0010_Move_WarpInventory_to_dedicated_config.java deleted file mode 100644 index f505bf0b8..000000000 --- a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migration_0010_Move_WarpInventory_to_dedicated_config.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.eternalcode.core.configuration.migrations; - -import static eu.okaeri.configs.migrate.ConfigMigrationDsl.move; - -import eu.okaeri.configs.migrate.builtin.NamedMigration; - -public class Migration_0010_Move_WarpInventory_to_dedicated_config extends NamedMigration { - Migration_0010_Move_WarpInventory_to_dedicated_config() { - super( - "Move WarpInventory items from messages to dedicated warp-inventory.yml config and clean up old fields", - move("warpInventory.title", "warpInventory.display.title"), - move("warpInventory.border.enabled", "warpInventory.border.enabled"), - move("warpInventory.border.material", "warpInventory.border.material"), - move("warpInventory.border.fillType", "warpInventory.border.fillType"), - move("warpInventory.border.name", "warpInventory.border.name"), - move("warpInventory.border.lore", "warpInventory.border.lore"), - move("warpInventory.decorationItems.items", "warpInventory.decorationItems.items"), - move("warpInventory.items", "warpInventory.items") - ); - } -} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migrations.java b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migrations.java index ea54e27d5..f9d0748b1 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migrations.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migrations.java @@ -11,8 +11,7 @@ public class Migrations { new Migration_0006_Move_alert_to_broadcast_section(), new Migration_0007_Move_clear_to_dedicated_section(), new Migration_0008_Move_repair_to_dedicated_section(), - new Migration_0009_Improve_Homes_Config(), - new Migration_0010_Move_WarpInventory_to_dedicated_config() + new Migration_0009_Improve_Homes_Config() }; } From c07228c874d5734de7eb53105f3078bf94fe232a Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Sat, 20 Sep 2025 16:14:49 +0200 Subject: [PATCH 3/4] Improve error handling. use `ReentrantReadWriteLock` --- .../feature/warp/inventory/WarpInventory.java | 202 ++++++++++-------- .../warp/inventory/WarpInventoryConfig.java | 47 ++-- .../inventory/WarpInventoryConfigService.java | 32 ++- .../inventory/WarpInventoryRepository.java | 2 - .../WarpInventoryRepositoryImpl.java | 51 ++++- 5 files changed, 197 insertions(+), 137 deletions(-) diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventory.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventory.java index 6fde89d5f..6d2f3ce9c 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventory.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventory.java @@ -19,6 +19,7 @@ import java.util.Comparator; import java.util.List; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Material; @@ -48,7 +49,7 @@ public class WarpInventory { private final WarpInventoryConfigService warpInventoryConfigService; @Inject - WarpInventory( + public WarpInventory( WarpService warpService, Server server, MiniMessage miniMessage, @@ -78,19 +79,7 @@ public void openInventory(Player player) { } private Gui createInventory(Player player, WarpInventoryConfigService.WarpInventoryConfigData warpData) { - int rowsCount; - int size = warpData.items().size(); - - if (!warpData.border().enabled()) { - rowsCount = (size + 1) / GUI_ROW_SIZE_WITHOUT_BORDER + 1; - } - else { - switch (warpData.border().fillType()) { - case BORDER, ALL -> rowsCount = (size - 1) / GUI_ROW_SIZE_WITH_BORDER + 1 + BORDER_ROW_COUNT; - case TOP, BOTTOM -> rowsCount = (size - 1) / GUI_ROW_SIZE_WITHOUT_BORDER + 1 + UGLY_BORDER_ROW_COUNT; - default -> throw new IllegalStateException("Unexpected value: " + warpData.border().fillType()); - } - } + int rowsCount = calculateRowsCount(warpData); Gui gui = Gui.gui() .title(this.miniMessage.deserialize(warpData.title())) @@ -105,33 +94,51 @@ private Gui createInventory(Player player, WarpInventoryConfigService.WarpInvent return gui; } + private int calculateRowsCount(WarpInventoryConfigService.WarpInventoryConfigData warpData) { + int size = warpData.items().size(); + + if (!warpData.border().enabled()) { + return (size + GUI_ROW_SIZE_WITHOUT_BORDER - 1) / GUI_ROW_SIZE_WITHOUT_BORDER; + } + + return switch (warpData.border().fillType()) { + case BORDER, ALL -> (size + GUI_ROW_SIZE_WITH_BORDER - 1) / GUI_ROW_SIZE_WITH_BORDER + BORDER_ROW_COUNT; + case TOP, BOTTOM -> (size + GUI_ROW_SIZE_WITHOUT_BORDER - 1) / GUI_ROW_SIZE_WITHOUT_BORDER + UGLY_BORDER_ROW_COUNT; + }; + } + private void createBorder(WarpInventoryConfigService.WarpInventoryConfigData warpData, Gui gui) { - if (warpData.border().enabled()) { - WarpInventoryConfig.BorderSection borderSection = warpData.border(); + if (!warpData.border().enabled()) { + return; + } - ItemBuilder borderItem = ItemBuilder.from(borderSection.material()); + WarpInventoryConfig.BorderSection borderSection = warpData.border(); + GuiItem guiItem = createBorderItem(borderSection); - if (!borderSection.name().isBlank()) { - borderItem.name(AdventureUtil.resetItalic(this.miniMessage.deserialize(borderSection.name()))); - } + switch (borderSection.fillType()) { + case BORDER -> gui.getFiller().fillBorder(guiItem); + case ALL -> gui.getFiller().fill(guiItem); + case TOP -> gui.getFiller().fillTop(guiItem); + case BOTTOM -> gui.getFiller().fillBottom(guiItem); + } + } - if (!borderSection.lore().isEmpty()) { - borderItem.lore(borderSection.lore() - .stream() - .map(entry -> AdventureUtil.resetItalic(this.miniMessage.deserialize(entry))) - .toList()); - } + private GuiItem createBorderItem(WarpInventoryConfig.BorderSection borderSection) { + ItemBuilder borderItem = ItemBuilder.from(borderSection.material()); - GuiItem guiItem = new GuiItem(borderItem.build()); + if (!borderSection.name().isBlank()) { + borderItem.name(AdventureUtil.resetItalic(this.miniMessage.deserialize(borderSection.name()))); + } - switch (borderSection.fillType()) { - case BORDER -> gui.getFiller().fillBorder(guiItem); - case ALL -> gui.getFiller().fill(guiItem); - case TOP -> gui.getFiller().fillTop(guiItem); - case BOTTOM -> gui.getFiller().fillBottom(guiItem); - default -> throw new IllegalStateException("Unexpected value: " + borderSection.fillType()); - } + if (!borderSection.lore().isEmpty()) { + List loreComponents = borderSection.lore() + .stream() + .map(entry -> AdventureUtil.resetItalic(this.miniMessage.deserialize(entry))) + .toList(); + borderItem.lore(loreComponents); } + + return new GuiItem(borderItem.build()); } private void createDecorations(WarpInventoryConfigService.WarpInventoryConfigData warpData, Gui gui) { @@ -141,22 +148,24 @@ private void createDecorations(WarpInventoryConfigService.WarpInventoryConfigDat guiItem.setAction(event -> { Player player = (Player) event.getWhoClicked(); - - if (item.commands.isEmpty()) { - return; - } - - for (String command : item.commands) { - this.server.dispatchCommand(player, command); - } - - player.closeInventory(); + this.executeDecorationCommands(player, item); }); gui.setItem(item.slot(), guiItem); } } + private void executeDecorationCommands(Player player, ConfigItem item) { + if (item.commands.isEmpty()) { + return; + } + + for (String command : item.commands) { + this.server.dispatchCommand(player, command); + } + player.closeInventory(); + } + private void createWarpItems(Player player, WarpInventoryConfigService.WarpInventoryConfigData warpData, Gui gui) { warpData.items().values().forEach(item -> { Optional warpOptional = this.warpService.findWarp(item.warpName()); @@ -166,26 +175,29 @@ private void createWarpItems(Player player, WarpInventoryConfigService.WarpInven } Warp warp = warpOptional.get(); - ConfigItem warpItem = item.warpItem(); if (!warp.hasPermissions(player)) { return; } - BaseItemBuilder baseItemBuilder = this.createItem(warpItem); - GuiItem guiItem = baseItemBuilder.asGuiItem(); + this.createWarpGuiItem(player, warp, item.warpItem(), gui); + }); + } - guiItem.setAction(event -> { - if (!warp.hasPermissions(player)) { - return; - } + private void createWarpGuiItem(Player player, Warp warp, ConfigItem warpItem, Gui gui) { + BaseItemBuilder baseItemBuilder = this.createItem(warpItem); + GuiItem guiItem = baseItemBuilder.asGuiItem(); - player.closeInventory(); - this.warpTeleportService.teleport(player, warp); - }); + guiItem.setAction(event -> { + if (!warp.hasPermissions(player)) { + return; + } - gui.setItem(warpItem.slot(), guiItem); + player.closeInventory(); + this.warpTeleportService.teleport(player, warp); }); + + gui.setItem(warpItem.slot(), guiItem); } private BaseItemBuilder createItem(ConfigItem item) { @@ -210,33 +222,36 @@ private BaseItemBuilder createItem(ConfigItem item) { .glow(item.glow()); } - public void addWarp(Warp warp) { + public CompletableFuture addWarp(Warp warp) { if (!this.warpService.exists(warp.getName())) { - return; + return CompletableFuture.completedFuture(null); } - this.warpInventoryConfigService.getWarpInventoryData() - .thenAccept(warpData -> { + return this.warpInventoryConfigService.getWarpInventoryData() + .thenCompose(warpData -> { int slot = getSlot(warpData); - WarpInventoryItem warpInventoryItem = WarpInventoryItem.builder() - .withWarpName(warp.getName()) - .withWarpItem(ConfigItem.builder() - .withName(this.warpSettings.itemNamePrefix() + warp.getName()) - .withLore(Collections.singletonList(this.warpSettings.itemLore())) - .withMaterial(this.warpSettings.itemMaterial()) - .withTexture(this.warpSettings.itemTexture()) - .withSlot(slot) - .withGlow(true) - .build()) - .build(); - - this.warpInventoryConfigService.addWarpItem(warp.getName(), warpInventoryItem) - .exceptionally(FutureHandler::handleException); + WarpInventoryItem warpInventoryItem = createWarpInventoryItem(warp, slot); + + return this.warpInventoryConfigService.addWarpItem(warp.getName(), warpInventoryItem); }) .exceptionally(FutureHandler::handleException); } + private WarpInventoryItem createWarpInventoryItem(Warp warp, int slot) { + return WarpInventoryItem.builder() + .withWarpName(warp.getName()) + .withWarpItem(ConfigItem.builder() + .withName(this.warpSettings.itemNamePrefix() + warp.getName()) + .withLore(Collections.singletonList(this.warpSettings.itemLore())) + .withMaterial(this.warpSettings.itemMaterial()) + .withTexture(this.warpSettings.itemTexture()) + .withSlot(slot) + .withGlow(true) + .build()) + .build(); + } + private int getSlot(WarpInventoryConfigService.WarpInventoryConfigData warpData) { int size = warpData.items().size(); if (!warpData.border().enabled()) { @@ -244,44 +259,49 @@ private int getSlot(WarpInventoryConfigService.WarpInventoryConfigData warpData) } return switch (warpData.border().fillType()) { - case BORDER -> GUI_ITEM_SLOT_WITH_BORDER + size + ((size / WarpInventory.GUI_ROW_SIZE_WITH_BORDER) * 2); - case ALL -> GUI_ITEM_SLOT_WITH_ALL_BORDER + size + ((size / WarpInventory.GUI_ROW_SIZE_WITH_BORDER) * 2); + case BORDER -> GUI_ITEM_SLOT_WITH_BORDER + size + ((size / GUI_ROW_SIZE_WITH_BORDER) * 2); + case ALL -> GUI_ITEM_SLOT_WITH_ALL_BORDER + size + ((size / GUI_ROW_SIZE_WITH_BORDER) * 2); case TOP -> GUI_ITEM_SLOT_WITH_TOP_BORDER + size; case BOTTOM -> size; }; } - public void removeWarp(String warpName) { + public CompletableFuture removeWarp(String warpName) { if (!this.warpSettings.autoAddNewWarps()) { - return; + return CompletableFuture.completedFuture(null); } - this.warpInventoryConfigService.removeWarpItem(warpName) - .thenAccept(removed -> { + return this.warpInventoryConfigService.removeWarpItem(warpName) + .thenCompose(removed -> { if (removed != null) { - this.shiftWarpItems(removed); + return this.shiftWarpItems(removed); } + return CompletableFuture.completedFuture(null); }) .exceptionally(FutureHandler::handleException); } - private void shiftWarpItems(WarpInventoryItem removed) { + private CompletableFuture shiftWarpItems(WarpInventoryItem removed) { int removedSlot = removed.warpItem.slot; - this.warpInventoryConfigService.getWarpItems() - .thenAccept(items -> { + return this.warpInventoryConfigService.getWarpItems() + .thenApply(items -> { List itemsToShift = items.values().stream() .filter(item -> item.warpItem.slot > removedSlot) .sorted(Comparator.comparingInt(item -> item.warpItem.slot)) .toList(); - int currentShift = removedSlot; - for (WarpInventoryItem item : itemsToShift) { - int nextShift = item.warpItem.slot; - item.warpItem.slot = currentShift; - currentShift = nextShift; - } - }) - .exceptionally(FutureHandler::handleException); + this.performSlotShift(itemsToShift, removedSlot); + return null; + }); + } + + private void performSlotShift(List itemsToShift, int removedSlot) { + int currentShift = removedSlot; + for (WarpInventoryItem item : itemsToShift) { + int nextShift = item.warpItem.slot; + item.warpItem.slot = currentShift; + currentShift = nextShift; + } } } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfig.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfig.java index 73958575e..3e2a92d54 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfig.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfig.java @@ -43,6 +43,25 @@ public File getConfigFile(File dataFolder) { return new File(dataFolder, "warp-inventory.yml"); } + public Map getItems() { + return new HashMap<>(this.items); + } + + public void setItems(Map items) { + if (items == null) { + this.items = new HashMap<>(); + return; + } + this.items = new HashMap<>(items); + } + + public enum FillType { + BORDER, + ALL, + TOP, + BOTTOM + } + @Getter @Accessors(fluent = true) public static class DisplaySection extends OkaeriConfig { @@ -57,41 +76,23 @@ public static class BorderSection extends OkaeriConfig { "# Changes of border section may affect the appearance of the GUI inventory, after changes adjust slots of existing items."}) public boolean enabled = true; + @Comment("# Material for border items") public Material material = Material.GRAY_STAINED_GLASS_PANE; + @Comment("# How to fill the border") public FillType fillType = FillType.BORDER; + @Comment("# Display name for border items (empty for no name)") public String name = ""; + @Comment("# Lore lines for border items") public List lore = Collections.emptyList(); } @Getter @Accessors(fluent = true) public static class DecorationItemsSection extends OkaeriConfig { + @Comment("# List of decoration items to display in the inventory") public List items = List.of(); } - - public enum FillType { - BORDER, - ALL, - TOP, - BOTTOM - } - - public void setItems(Map items) { - this.items = items; - } - - public void addItem(String warpName, WarpInventoryItem item) { - this.items.put(warpName, item); - } - - public WarpInventoryItem removeItem(String warpName) { - return this.items.remove(warpName); - } - - public Map getItems() { - return this.items; - } } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfigService.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfigService.java index 2f4650a76..1b29a8aad 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfigService.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfigService.java @@ -25,21 +25,35 @@ public WarpInventoryConfigService( public CompletableFuture getWarpInventoryData() { return this.warpInventoryRepository.getAllWarpInventoryItems() - .thenApply(items -> { - return new WarpInventoryConfigData( - this.warpInventoryConfig.display().title(), - this.warpInventoryConfig.border(), - this.warpInventoryConfig.decorationItems(), - items - ); - }); + .thenApply(items -> new WarpInventoryConfigData( + this.warpInventoryConfig.display().title(), + this.warpInventoryConfig.border(), + this.warpInventoryConfig.decorationItems(), + items + )); } public CompletableFuture addWarpItem(String warpName, WarpInventoryItem item) { + if (warpName == null || warpName.trim().isEmpty()) { + return CompletableFuture.failedFuture( + new IllegalArgumentException("Warp name cannot be null or empty") + ); + } + + if (item == null) { + return CompletableFuture.failedFuture( + new IllegalArgumentException("WarpInventoryItem cannot be null") + ); + } + return this.warpInventoryRepository.saveWarpInventoryItem(warpName, item); } public CompletableFuture removeWarpItem(String warpName) { + if (warpName == null || warpName.trim().isEmpty()) { + return CompletableFuture.completedFuture(null); + } + return this.warpInventoryRepository.getWarpInventoryItem(warpName) .thenCompose(item -> { if (item != null) { @@ -54,13 +68,11 @@ public CompletableFuture> getWarpItems() { return this.warpInventoryRepository.getAllWarpInventoryItems(); } - public record WarpInventoryConfigData( String title, BorderSection border, DecorationItemsSection decorationItems, Map items ) { - } } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepository.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepository.java index 6a19cf91e..eafc8c7af 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepository.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepository.java @@ -13,6 +13,4 @@ public interface WarpInventoryRepository { CompletableFuture getWarpInventoryItem(String warpName); CompletableFuture> getAllWarpInventoryItems(); - - WarpInventoryConfig getConfig(); } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepositoryImpl.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepositoryImpl.java index f32f57643..429137d9d 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepositoryImpl.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepositoryImpl.java @@ -7,44 +7,70 @@ import com.eternalcode.core.injector.annotations.component.Repository; import java.util.HashMap; import java.util.Map; -import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Consumer; import java.util.function.Function; +import java.util.logging.Logger; @Repository class WarpInventoryRepositoryImpl implements WarpInventoryRepository { - private static final Object READ_WRITE_LOCK = new Object(); + private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private final WarpInventoryConfig warpInventoryConfig; private final ConfigurationManager configurationManager; private final Scheduler scheduler; + private final Logger logger; @Inject - WarpInventoryRepositoryImpl( + public WarpInventoryRepositoryImpl( ConfigurationManager configurationManager, WarpInventoryConfig warpInventoryConfig, - Scheduler scheduler + Scheduler scheduler, + Logger logger ) { this.configurationManager = configurationManager; this.warpInventoryConfig = warpInventoryConfig; this.scheduler = scheduler; + this.logger = logger; } @Override public CompletableFuture saveWarpInventoryItem(String warpName, WarpInventoryItem item) { + if (warpName == null || warpName.trim().isEmpty()) { + return CompletableFuture.failedFuture( + new IllegalArgumentException("Warp name cannot be null or empty") + ); + } + + if (item == null) { + return CompletableFuture.failedFuture( + new IllegalArgumentException("WarpInventoryItem cannot be null") + ); + } + return this.transactionalRun(items -> items.put(warpName, item)); } @Override public CompletableFuture removeWarpInventoryItem(String warpName) { + if (warpName == null || warpName.trim().isEmpty()) { + return CompletableFuture.failedFuture( + new IllegalArgumentException("Warp name cannot be null or empty") + ); + } + return this.transactionalRun(items -> items.remove(warpName)); } @Override public CompletableFuture getWarpInventoryItem(String warpName) { - return this.transactionalSupply(items -> Optional.ofNullable(items.get(warpName)).orElse(null)); + if (warpName == null || warpName.trim().isEmpty()) { + return CompletableFuture.completedFuture(null); + } + + return this.transactionalSupply(items -> items.get(warpName)); } @Override @@ -52,11 +78,6 @@ public CompletableFuture> getAllWarpInventoryItem return this.transactionalSupply(items -> new HashMap<>(items)); } - @Override - public WarpInventoryConfig getConfig() { - return this.warpInventoryConfig; - } - private CompletableFuture transactionalRun(Consumer> editor) { return this.transactionalSupply(items -> { editor.accept(items); @@ -66,13 +87,21 @@ private CompletableFuture transactionalRun(Consumer CompletableFuture transactionalSupply(Function, T> editor) { return this.scheduler.completeAsync(() -> { - synchronized (READ_WRITE_LOCK) { + this.readWriteLock.writeLock().lock(); + try { Map items = new HashMap<>(this.warpInventoryConfig.getItems()); T result = editor.apply(items); this.warpInventoryConfig.setItems(items); this.configurationManager.save(this.warpInventoryConfig); return result; } + catch (Exception exception) { + this.logger.severe("Error during transactional operation: " + exception.getMessage()); + throw new RuntimeException("Failed to perform transactional operation", exception); + } + finally { + this.readWriteLock.writeLock().unlock(); + } }); } } From 95291e3314083792e013281ba101906a0f2970eb Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Sat, 18 Oct 2025 00:56:23 +0200 Subject: [PATCH 4/4] Apply review feedback. --- .../feature/warp/command/WarpCommand.java | 2 +- .../feature/warp/inventory/WarpInventory.java | 52 +++++++------- .../warp/inventory/WarpInventoryConfig.java | 39 +++++------ .../WarpInventoryRepositoryImpl.java | 67 +++++-------------- 4 files changed, 57 insertions(+), 103 deletions(-) diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/WarpCommand.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/WarpCommand.java index 1e12cb5af..592313bca 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/WarpCommand.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/command/WarpCommand.java @@ -68,7 +68,7 @@ void warp(@Sender Player player) { return; } - this.warpInventory.openInventory(player); + this.warpInventory.open(player); } @Execute(name = "warp") diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventory.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventory.java index 6d2f3ce9c..3cb734e31 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventory.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventory.java @@ -18,6 +18,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import net.kyori.adventure.text.Component; @@ -67,23 +68,23 @@ public WarpInventory( this.warpInventoryConfigService = warpInventoryConfigService; } - public void openInventory(Player player) { + public void open(Player player) { this.warpInventoryConfigService.getWarpInventoryData() .thenAccept(warpData -> { this.scheduler.run(() -> { - Gui gui = this.createInventory(player, warpData); + Gui gui = this.create(player, warpData); gui.open(player); }); }) .exceptionally(FutureHandler::handleException); } - private Gui createInventory(Player player, WarpInventoryConfigService.WarpInventoryConfigData warpData) { - int rowsCount = calculateRowsCount(warpData); + private Gui create(Player player, WarpInventoryConfigService.WarpInventoryConfigData warpData) { + int rows = calculateRowsCount(warpData); Gui gui = Gui.gui() .title(this.miniMessage.deserialize(warpData.title())) - .rows(rowsCount) + .rows(rows) .disableAllInteractions() .create(); @@ -267,41 +268,38 @@ private int getSlot(WarpInventoryConfigService.WarpInventoryConfigData warpData) } public CompletableFuture removeWarp(String warpName) { - if (!this.warpSettings.autoAddNewWarps()) { - return CompletableFuture.completedFuture(null); - } - - return this.warpInventoryConfigService.removeWarpItem(warpName) - .thenCompose(removed -> { - if (removed != null) { - return this.shiftWarpItems(removed); + return this.warpInventoryConfigService.getWarpItems() + .thenCompose(items -> { + if (!items.containsKey(warpName)) { + return CompletableFuture.completedFuture(null); } - return CompletableFuture.completedFuture(null); + + return this.warpInventoryConfigService.removeWarpItem(warpName) + .thenCompose(removed -> { + if (removed != null) { + return this.shiftWarpItems(removed, items); + } + return CompletableFuture.completedFuture(null); + }); }) .exceptionally(FutureHandler::handleException); } - private CompletableFuture shiftWarpItems(WarpInventoryItem removed) { + private CompletableFuture shiftWarpItems(WarpInventoryItem removed, Map items) { int removedSlot = removed.warpItem.slot; - return this.warpInventoryConfigService.getWarpItems() - .thenApply(items -> { - List itemsToShift = items.values().stream() - .filter(item -> item.warpItem.slot > removedSlot) - .sorted(Comparator.comparingInt(item -> item.warpItem.slot)) - .toList(); - - this.performSlotShift(itemsToShift, removedSlot); - return null; - }); - } + List itemsToShift = items.values().stream() + .filter(item -> item.warpItem.slot > removedSlot) + .sorted(Comparator.comparingInt(item -> item.warpItem.slot)) + .toList(); - private void performSlotShift(List itemsToShift, int removedSlot) { int currentShift = removedSlot; for (WarpInventoryItem item : itemsToShift) { int nextShift = item.warpItem.slot; item.warpItem.slot = currentShift; currentShift = nextShift; } + + return CompletableFuture.completedFuture(null); } } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfig.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfig.java index 3e2a92d54..b0fd3b744 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfig.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryConfig.java @@ -20,22 +20,26 @@ @ConfigurationFile public class WarpInventoryConfig extends AbstractConfigurationFile { - @Comment({" ", - "# Warp inventory configuration", - "# This file contains the GUI layout and item definitions for the warp inventory", - "# Text content (titles, names, lore) should be configured in language files"}) + @Comment({ + "# Warp inventory configuration", + "# This file contains the GUI layout and item definitions for the warp inventory", + "# Text content (titles, names, lore) should be configured in language files" + }) public DisplaySection display = new DisplaySection(); - @Comment({" ", - "# Border configuration for the warp inventory"}) + @Comment({ + "# Border configuration for the warp inventory" + }) public BorderSection border = new BorderSection(); - @Comment({" ", - "# Decoration items that can be placed in the inventory"}) + @Comment({ + "# Decoration items that can be placed in the inventory" + }) public DecorationItemsSection decorationItems = new DecorationItemsSection(); - @Comment({" ", - "# Warp items configuration - maps warp names to their inventory representation"}) + @Comment({ + "# Warp items configuration - maps warp names to their inventory representation" + }) public Map items = new HashMap<>(); @Override @@ -43,18 +47,6 @@ public File getConfigFile(File dataFolder) { return new File(dataFolder, "warp-inventory.yml"); } - public Map getItems() { - return new HashMap<>(this.items); - } - - public void setItems(Map items) { - if (items == null) { - this.items = new HashMap<>(); - return; - } - this.items = new HashMap<>(items); - } - public enum FillType { BORDER, ALL, @@ -72,8 +64,7 @@ public static class DisplaySection extends OkaeriConfig { @Getter @Accessors(fluent = true) public static class BorderSection extends OkaeriConfig { - @Comment({" ", - "# Changes of border section may affect the appearance of the GUI inventory, after changes adjust slots of existing items."}) + @Comment("# Changes of border section may affect the appearance of the GUI inventory, after changes adjust slots of existing items.") public boolean enabled = true; @Comment("# Material for border items") diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepositoryImpl.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepositoryImpl.java index 429137d9d..9a82e0add 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepositoryImpl.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/warp/inventory/WarpInventoryRepositoryImpl.java @@ -8,60 +8,52 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.logging.Logger; @Repository class WarpInventoryRepositoryImpl implements WarpInventoryRepository { - private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); - private final WarpInventoryConfig warpInventoryConfig; private final ConfigurationManager configurationManager; private final Scheduler scheduler; - private final Logger logger; @Inject public WarpInventoryRepositoryImpl( ConfigurationManager configurationManager, WarpInventoryConfig warpInventoryConfig, - Scheduler scheduler, - Logger logger + Scheduler scheduler ) { this.configurationManager = configurationManager; this.warpInventoryConfig = warpInventoryConfig; this.scheduler = scheduler; - this.logger = logger; } @Override public CompletableFuture saveWarpInventoryItem(String warpName, WarpInventoryItem item) { if (warpName == null || warpName.trim().isEmpty()) { - return CompletableFuture.failedFuture( - new IllegalArgumentException("Warp name cannot be null or empty") - ); + throw new IllegalArgumentException("Warp name cannot be null or empty"); } - if (item == null) { - return CompletableFuture.failedFuture( - new IllegalArgumentException("WarpInventoryItem cannot be null") - ); + throw new IllegalArgumentException("WarpInventoryItem cannot be null"); } - return this.transactionalRun(items -> items.put(warpName, item)); + return this.scheduler.completeAsync(() -> { + this.warpInventoryConfig.items().put(warpName, item); + this.configurationManager.save(this.warpInventoryConfig); + return null; + }); } @Override public CompletableFuture removeWarpInventoryItem(String warpName) { if (warpName == null || warpName.trim().isEmpty()) { - return CompletableFuture.failedFuture( - new IllegalArgumentException("Warp name cannot be null or empty") - ); + throw new IllegalArgumentException("Warp name cannot be null or empty"); } - return this.transactionalRun(items -> items.remove(warpName)); + return this.scheduler.completeAsync(() -> { + this.warpInventoryConfig.items().remove(warpName); + this.configurationManager.save(this.warpInventoryConfig); + return null; + }); } @Override @@ -70,38 +62,11 @@ public CompletableFuture getWarpInventoryItem(String warpName return CompletableFuture.completedFuture(null); } - return this.transactionalSupply(items -> items.get(warpName)); + return this.scheduler.completeAsync(() -> this.warpInventoryConfig.items().get(warpName)); } @Override public CompletableFuture> getAllWarpInventoryItems() { - return this.transactionalSupply(items -> new HashMap<>(items)); - } - - private CompletableFuture transactionalRun(Consumer> editor) { - return this.transactionalSupply(items -> { - editor.accept(items); - return null; - }); - } - - private CompletableFuture transactionalSupply(Function, T> editor) { - return this.scheduler.completeAsync(() -> { - this.readWriteLock.writeLock().lock(); - try { - Map items = new HashMap<>(this.warpInventoryConfig.getItems()); - T result = editor.apply(items); - this.warpInventoryConfig.setItems(items); - this.configurationManager.save(this.warpInventoryConfig); - return result; - } - catch (Exception exception) { - this.logger.severe("Error during transactional operation: " + exception.getMessage()); - throw new RuntimeException("Failed to perform transactional operation", exception); - } - finally { - this.readWriteLock.writeLock().unlock(); - } - }); + return this.scheduler.completeAsync(() -> new HashMap<>(this.warpInventoryConfig.items())); } }