diff --git a/build.gradle.kts b/build.gradle.kts index c91edd78..bc61e674 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,15 +17,13 @@ repositories { maven { url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") } maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") } maven { url = uri("https://oss.sonatype.org/content/repositories/central") } - maven { url = uri("https://papermc.io/repo/repository/maven-public/") } + maven { url = uri("https://repo.papermc.io/repository/maven-public/") } maven { url = uri("https://repo.eternalcode.pl/releases") } - maven { url = uri("https://repository.minecodes.pl/releases") } - maven { url = uri("https://jitpack.io") } } dependencies { // minecraft development api - compileOnly("org.spigotmc:spigot-api:1.21.4-R0.1-SNAPSHOT") + compileOnly("org.spigotmc:spigot-api:1.21.7-R0.1-SNAPSHOT") implementation("net.kyori:adventure-platform-bukkit:4.4.0") implementation("net.kyori:adventure-text-minimessage:4.23.0") implementation("dev.rollczi:litecommands-bukkit:3.10.0") @@ -38,9 +36,6 @@ dependencies { implementation("dev.triumphteam:triumph-gui:3.1.11") implementation("de.rapha149.signgui:signgui:2.5.3") - // economy - compileOnly("com.github.MilkBowl:VaultAPI:1.7.1") - // CDN implementation("net.dzikoysk:cdn:1.14.9") @@ -86,6 +81,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.13.2") testImplementation("org.junit.jupiter:junit-jupiter-params:5.13.2") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.13.2") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") testImplementation("org.testcontainers:junit-jupiter:1.21.3") testImplementation("org.testcontainers:mysql:1.21.3") @@ -119,17 +115,10 @@ tasks { minecraftVersion("1.21.4") } - cleanPaperPluginsCache { + clean { doLast { project.file("run/plugins").deleteRecursively() - } - } - - cleanPaperCache { - doLast { - project.file("run/cache").deleteRecursively() project.file("run/logs").deleteRecursively() - project.file("run/versions").deleteRecursively() } } @@ -138,7 +127,7 @@ tasks { } shadowJar { - archiveFileName.set("ParcelLockers v${project.version} (MC 1.21.3-1.21.4).jar") + archiveFileName.set("ParcelLockers v${project.version}.jar") exclude( "org/intellij/lang/annotations/**", diff --git a/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java b/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java index 7878c36f..33a6277f 100644 --- a/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java +++ b/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java @@ -12,6 +12,7 @@ import com.eternalcode.parcellockers.content.repository.ParcelContentRepository; import com.eternalcode.parcellockers.content.repository.ParcelContentRepositoryOrmLite; import com.eternalcode.parcellockers.database.DatabaseManager; +import com.eternalcode.parcellockers.delivery.repository.DeliveryRepositoryOrmLite; import com.eternalcode.parcellockers.gui.implementation.locker.LockerMainGui; import com.eternalcode.parcellockers.gui.implementation.remote.MainGui; import com.eternalcode.parcellockers.gui.implementation.remote.ParcelListGui; @@ -27,10 +28,12 @@ import com.eternalcode.parcellockers.notification.NotificationAnnouncer; import com.eternalcode.parcellockers.parcel.Parcel; import com.eternalcode.parcellockers.parcel.ParcelManager; +import com.eternalcode.parcellockers.parcel.ParcelStatus; import com.eternalcode.parcellockers.parcel.command.ParcelCommand; import com.eternalcode.parcellockers.parcel.command.argument.ParcelArgument; import com.eternalcode.parcellockers.parcel.repository.ParcelCache; import com.eternalcode.parcellockers.parcel.repository.ParcelRepositoryOrmLite; +import com.eternalcode.parcellockers.parcel.task.ParcelSendTask; import com.eternalcode.parcellockers.updater.UpdaterService; import com.eternalcode.parcellockers.user.UserManager; import com.eternalcode.parcellockers.user.controller.LoadUserController; @@ -52,21 +55,21 @@ import io.sentry.Sentry; import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.text.minimessage.MiniMessage; -import net.milkbowl.vault.economy.Economy; import org.bstats.bukkit.Metrics; import org.bukkit.Server; import org.bukkit.command.CommandSender; -import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.java.JavaPlugin; +import org.slf4j.helpers.NOPLogger; import java.sql.SQLException; import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Stream; -import org.slf4j.helpers.NOPLogger; public final class ParcelLockers extends JavaPlugin { @@ -75,7 +78,7 @@ public final class ParcelLockers extends JavaPlugin { private LiteCommands liteCommands; private BukkitAudiences audiences; private SkullAPI skullAPI; - private Economy economy; + private DatabaseManager databaseManager; @Override @@ -141,15 +144,16 @@ public void onEnable() { ParcelRepositoryOrmLite parcelRepository = new ParcelRepositoryOrmLite(databaseManager, scheduler, parcelCache); parcelRepository.updateCaches(); + DeliveryRepositoryOrmLite deliveryRepository = new DeliveryRepositoryOrmLite(databaseManager, scheduler); + ParcelContentRepository parcelContentRepository = new ParcelContentRepositoryOrmLite(databaseManager, scheduler); - ParcelManager parcelManager = new ParcelManager(config, announcer, parcelRepository, parcelContentRepository, scheduler); + ParcelManager parcelManager = new ParcelManager(config, announcer, parcelRepository, deliveryRepository, parcelContentRepository, scheduler); ItemStorageRepository itemStorageRepository = new ItemStorageRepositoryOrmLite(databaseManager, scheduler); UserRepository userRepository = new UserRepositoryOrmLite(databaseManager, scheduler); UserManager userManager = new UserManager(userRepository); - MainGui mainGUI = new MainGui(this, server, miniMessage, config, parcelRepository, lockerRepository, userManager); ParcelListGui parcelListGUI = new ParcelListGui(this, server, miniMessage, config, parcelRepository, lockerRepository, userManager, mainGUI); @@ -168,12 +172,6 @@ public void onEnable() { .missingPermission(new PermissionMessage(announcer, config)) .build(); - /*if (!this.setupEconomy()) { - this.getLogger().severe("Disabling due to no Vault dependency or its implementator(s) found!"); - server.getPluginManager().disablePlugin(this); - return; - }*/ - LockerMainGui lockerMainGUI = new LockerMainGui(this, miniMessage, config, itemStorageRepository, parcelRepository, lockerRepository, announcer, parcelContentRepository, userRepository, this.skullAPI, parcelManager); Stream.of( @@ -187,6 +185,24 @@ public void onEnable() { new Metrics(this, 17677); new UpdaterService(this.getDescription()); + parcelRepository.findAll().thenAccept(optionalParcels -> { + List parcels = optionalParcels.orElseGet(ArrayList::new).stream() + .filter(parcel -> parcel.status() != ParcelStatus.DELIVERED) + .toList(); + + parcels.forEach(parcel -> + deliveryRepository.find(parcel.uuid()).thenAccept(optionalDelivery -> + optionalDelivery.ifPresent(delivery -> { + long delay = Math.max(0, delivery.deliveryTimestamp().toEpochMilli() - System.currentTimeMillis()); + scheduler.runLaterAsync( + new ParcelSendTask(parcel, delivery, parcelRepository, deliveryRepository, config), + Duration.ofMillis(delay) + ); + }) + ) + ); + }); + long millis = started.elapsed(TimeUnit.MILLISECONDS); this.getLogger().log(Level.INFO, "Successfully enabled ParcelLockers in {0}ms", millis); } @@ -227,24 +243,6 @@ private void softwareCheck() { logger.info("Your server is running on supported software, congratulations!"); logger.info("Server version: " + this.getServer().getVersion()); } - - private boolean setupEconomy() { - if (this.getServer().getPluginManager().getPlugin("Vault") == null) { - return false; - } - - RegisteredServiceProvider rsp = this.getServer().getServicesManager().getRegistration(Economy.class); - if (rsp == null) { - return false; // Vault is installed but no economy plugin is registered (e.g. EssentialsX) - majk - } - - this.economy = rsp.getProvider(); - return true; - } - - public Economy getEconomy() { - return this.economy; - } } diff --git a/src/main/java/com/eternalcode/parcellockers/ParcelLockersCommand.java b/src/main/java/com/eternalcode/parcellockers/ParcelLockersCommand.java index d68566ca..a6fd4b0a 100644 --- a/src/main/java/com/eternalcode/parcellockers/ParcelLockersCommand.java +++ b/src/main/java/com/eternalcode/parcellockers/ParcelLockersCommand.java @@ -3,7 +3,6 @@ import com.eternalcode.parcellockers.configuration.ConfigurationManager; import com.eternalcode.parcellockers.configuration.implementation.PluginConfiguration; import com.eternalcode.parcellockers.notification.NotificationAnnouncer; -import dev.rollczi.litecommands.annotations.async.Async; import dev.rollczi.litecommands.annotations.command.Command; import dev.rollczi.litecommands.annotations.context.Context; import dev.rollczi.litecommands.annotations.execute.Execute; @@ -26,8 +25,7 @@ public ParcelLockersCommand(ConfigurationManager configManager, PluginConfigurat this.announcer = announcer; } - @Async - @Execute(name = "reload", aliases = { "rl" }) + @Execute(name = "reload") void reload(@Context CommandSender sender) { this.configManager.reload(); this.announcer.sendMessage(sender, this.config.messages.reload); diff --git a/src/main/java/com/eternalcode/parcellockers/command/debug/DebugCommand.java b/src/main/java/com/eternalcode/parcellockers/command/debug/DebugCommand.java index cb301b4c..f5959068 100644 --- a/src/main/java/com/eternalcode/parcellockers/command/debug/DebugCommand.java +++ b/src/main/java/com/eternalcode/parcellockers/command/debug/DebugCommand.java @@ -11,6 +11,7 @@ import dev.rollczi.litecommands.annotations.execute.Execute; import dev.rollczi.litecommands.annotations.permission.Permission; import org.bukkit.Material; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -37,44 +38,44 @@ public DebugCommand(ParcelRepository parcelRepository, LockerRepository lockerRe this.announcer = announcer; } - @Execute(name = "deleteparcels") - void deleteParcels(@Context Player player) { + @Execute(name = "delete parcels") + void deleteParcels(@Context CommandSender sender) { this.parcelRepository.removeAll().exceptionally(throwable -> { - this.announcer.sendMessage(player, "&cFailed to delete parcels"); + this.announcer.sendMessage(sender, "&4Failed to delete parcels"); return null; - }).thenRun(() -> this.announcer.sendMessage(player, "&aParcels deleted")); + }).thenRun(() -> this.announcer.sendMessage(sender, "&cParcels deleted")); } - @Execute(name = "deletelockers") - void deleteLockers(@Context Player player) { + @Execute(name = "delete lockers") + void deleteLockers(@Context CommandSender sender) { this.lockerRepository.removeAll().exceptionally(throwable -> { - this.announcer.sendMessage(player, "&cFailed to delete lockers"); + this.announcer.sendMessage(sender, "&4Failed to delete lockers"); return null; - }).thenRun(() -> this.announcer.sendMessage(player, "&aLockers deleted")); + }).thenRun(() -> this.announcer.sendMessage(sender, "&cLockers deleted")); } - @Execute(name = "deleteitemstorages") - void deleteItemStorages(@Context Player player) { + @Execute(name = "delete itemstorages") + void deleteItemStorages(@Context CommandSender sender) { this.itemStorageRepository.removeAll().exceptionally(throwable -> { - this.announcer.sendMessage(player, "&cFailed to delete item storages"); + this.announcer.sendMessage(sender, "&4Failed to delete item storages"); return null; - }).thenRun(() -> this.announcer.sendMessage(player, "&aItem storages deleted")); + }).thenRun(() -> this.announcer.sendMessage(sender, "&cItem storages deleted")); } - @Execute(name = "deleteparcelcontents") - void deleteParcelContents(@Context Player player) { + @Execute(name = "delete parcelcontents") + void deleteParcelContents(@Context CommandSender sender) { this.contentRepository.removeAll().exceptionally(throwable -> { - this.announcer.sendMessage(player, "&cFailed to delete parcel contents"); + this.announcer.sendMessage(sender, "&4Failed to delete parcel contents"); return null; - }).thenRun(() -> this.announcer.sendMessage(player, "&aParcel contents deleted")); + }).thenRun(() -> this.announcer.sendMessage(sender, "&cParcel contents deleted")); } - @Execute(name = "deleteall") - void deleteAll(@Context Player player) { - this.deleteItemStorages(player); - this.deleteLockers(player); - this.deleteParcels(player); - this.deleteParcelContents(player); + @Execute(name = "delete all") + void deleteAll(@Context CommandSender sender) { + this.deleteItemStorages(sender); + this.deleteLockers(sender); + this.deleteParcels(sender); + this.deleteParcelContents(sender); } @Execute(name = "getrandomitem") diff --git a/src/main/java/com/eternalcode/parcellockers/configuration/ConfigurationManager.java b/src/main/java/com/eternalcode/parcellockers/configuration/ConfigurationManager.java index 05c604bc..12a79bca 100644 --- a/src/main/java/com/eternalcode/parcellockers/configuration/ConfigurationManager.java +++ b/src/main/java/com/eternalcode/parcellockers/configuration/ConfigurationManager.java @@ -1,10 +1,13 @@ package com.eternalcode.parcellockers.configuration; +import com.eternalcode.parcellockers.configuration.composer.DurationComposer; import com.eternalcode.parcellockers.configuration.composer.PositionComposer; +import com.eternalcode.parcellockers.shared.Position; import net.dzikoysk.cdn.Cdn; import net.dzikoysk.cdn.CdnFactory; import java.io.File; +import java.time.Duration; import java.util.HashSet; import java.util.Set; @@ -13,7 +16,8 @@ public class ConfigurationManager { private static final Cdn CDN = CdnFactory .createYamlLike() .getSettings() - .withComposer(PositionComposer.class, new PositionComposer()) + .withComposer(Position.class, new PositionComposer()) + .withComposer(Duration.class, new DurationComposer()) .build(); private final Set configs = new HashSet<>(); diff --git a/src/main/java/com/eternalcode/parcellockers/configuration/composer/DurationComposer.java b/src/main/java/com/eternalcode/parcellockers/configuration/composer/DurationComposer.java new file mode 100644 index 00000000..80eeec2d --- /dev/null +++ b/src/main/java/com/eternalcode/parcellockers/configuration/composer/DurationComposer.java @@ -0,0 +1,96 @@ +package com.eternalcode.parcellockers.configuration.composer; + +import panda.std.Result; + +import java.time.Duration; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class DurationComposer implements SimpleComposer { + + // Regex to match each time component individually + private static final Pattern TIME_PATTERN = Pattern.compile("(?:(\\d+)d)?\\s*(?:(\\d+)h)?\\s*(?:(\\d+)m)?\\s*(?:(\\d+)s)?", Pattern.CASE_INSENSITIVE); + + @Override + public Result serialize(Duration duration) { + if (duration == null) { + return Result.error(new IllegalArgumentException("Duration cannot be null")); + } + + long days = duration.toDays(); + long hours = duration.toHours() % 24; + long minutes = duration.toMinutes() % 60; + long seconds = duration.getSeconds() % 60; + + StringBuilder result = new StringBuilder(); + if (days > 0) { + result.append(days).append("d "); + } + if (hours > 0) { + result.append(hours).append("h "); + } + if (minutes > 0) { + result.append(minutes).append("m "); + } + if (seconds > 0 || result.isEmpty()) { // Always include seconds if no other components + result.append(seconds).append("s"); + } + + return Result.ok(result.toString().trim()); + } + + @Override + public Result deserialize(String input) { + try { + if (input == null || input.isBlank()) { + return Result.error(new IllegalArgumentException("Input cannot be null or blank")); + } + + // If input is already in ISO-8601 format + if (input.toUpperCase().startsWith("P")) { + return Result.ok(Duration.parse(input)); + } + + // For simple case: just a number (assume seconds) + if (input.matches("\\d+(\\.\\d+)?")) { + if (input.contains(".")) { + double seconds = Double.parseDouble(input); + return Result.ok(Duration.ofMillis((long) (seconds * 1000))); + } else { + return Result.ok(Duration.ofSeconds(Long.parseLong(input))); + } + } + + // Parse custom format (e.g., "1d 2h 30m 15s") + Duration duration = Duration.ZERO; + Matcher matcher = TIME_PATTERN.matcher(input); + + if (matcher.matches()) { + String days = matcher.group(1); + String hours = matcher.group(2); + String minutes = matcher.group(3); + String seconds = matcher.group(4); + + if (days != null) { + duration = duration.plusDays(Long.parseLong(days)); + } + if (hours != null) { + duration = duration.plusHours(Long.parseLong(hours)); + } + if (minutes != null) { + duration = duration.plusMinutes(Long.parseLong(minutes)); + } + if (seconds != null) { + duration = duration.plusSeconds(Long.parseLong(seconds)); + } + + return Result.ok(duration); + } + + return Result.error(new IllegalArgumentException( + "Invalid duration format. Expected format like '1d 2h 3m 4s' or ISO-8601 duration.")); + } catch (Exception e) { + return Result.error(new IllegalArgumentException("Invalid duration format. Expected format like '1d 2h 3m 4s' or ISO-8601 duration.")); + } + } +} diff --git a/src/main/java/com/eternalcode/parcellockers/configuration/implementation/PluginConfiguration.java b/src/main/java/com/eternalcode/parcellockers/configuration/implementation/PluginConfiguration.java index 644a1c25..4055b026 100644 --- a/src/main/java/com/eternalcode/parcellockers/configuration/implementation/PluginConfiguration.java +++ b/src/main/java/com/eternalcode/parcellockers/configuration/implementation/PluginConfiguration.java @@ -7,8 +7,10 @@ import net.dzikoysk.cdn.source.Resource; import net.dzikoysk.cdn.source.Source; import org.bukkit.Material; +import org.bukkit.Sound; import java.io.File; +import java.time.Duration; import java.util.Collections; import java.util.List; @@ -60,9 +62,6 @@ public static class Settings { @Description({ " ", "# The database port." }) public String port = "3306"; - @Description({ " ", "# Turn this on to enable SSL (Secure Sockets Layer)" }) - public boolean useSSL = false; - @Description({ " ", "# The database password." }) public String password = ""; @@ -72,14 +71,22 @@ public static class Settings { .setType(Material.CHEST) .setLore(List.of("&bPlace to create a parcel locker.")); - @Description({ " ", "# Small parcel cost." }) - public double smallParcelCost = 5.0; + @Description({" ", "# Standard parcel sending duration"}) + public Duration parcelSendDuration = Duration.ofSeconds(10); + + @Description({" ", "# Parcel sending duration for priority parcels"}) + public Duration priorityParcelSendDuration = Duration.ofSeconds(5); + + @Description({" ", "# Error sound used in the plugin."}) + public Sound errorSound = Sound.ENTITY_ENDERMAN_AMBIENT; + + @Description({ " ", "# The sound volume for the error sound." }) + public float errorSoundVolume = 1.0F; + + @Description({ " ", "# The sound pitch for the error sound." }) + public float errorSoundPitch = 1.0F; - @Description({ " ", "# Medium parcel cost." }) - public double mediumParcelCost = 10.0; - @Description({ " ", "# Large parcel cost." }) - public double largeParcelCost = 15.0; } @Contextual @@ -91,8 +98,6 @@ public static class Messages { public String invalidUsage = "&4❣ &cCorrect usage: &6{USAGE}"; public String reload = "&3❣ &bConfiguration has been successfully reloaded!"; public String parcelCommandUsage = "&9ⓘ Correct usage: &3/parcel &b &3[parcel]"; - public String parcelSuccessfullyCreated = "&2✔ &aParcel created successfully."; - public String failedToCreateParcel = "&4✘ &cAn error occurred while creating the parcel."; public String parcelSuccessfullyDeleted = "&2✔ &aParcel deleted successfully."; public String failedToDeleteParcel = "&4✘ &cAn error occurred while deleting the parcel."; public String failedToCreateParcelLocker = "&4✘ &cCould not create the parcel locker."; @@ -103,7 +108,7 @@ public static class Messages { public String broadcastParcelLockerRemoved = "&4❣ &cThe parcel locker at &4{X} {Y} {Z} &cin &4{WORLD} &chas been removed by &4{PLAYER}!"; public String parcelSent = "&2✔ &aParcel sent successfully."; public String parcelFailedToSend = "&4✘ &cAn error occurred while sending the parcel. Check the console for more information."; - public String illegalItemFailedToSend = "&4✘ &cThe parcel contains illegal items that cannot be sent!"; + public String illegalItemFailedToSend = "&4✘ &cThe parcel contains illegal items that cannot be sent. ({ITEMS})"; public String parcelCannotBeEmpty = "&4✘ &cThe parcel cannot be empty!"; public String parcelNameCannotBeEmpty = "&4✘ &cThe parcel name cannot be empty!"; public String parcelNameSet = "&2✔ &aParcel name set successfully."; @@ -119,13 +124,13 @@ public static class Messages { @Description({ " ", "# The parcel info message." }) public List parcelInfoMessages = List.of( "&7» &6Parcel info:", - "&6UUID: &e{UUID}", - "&6Sender: &e{SENDER}", - "&6Receiver: &e{RECEIVER}", - "&6Size: &e{SIZE}", - "&6Position: &6X: &e{POSITION_X}, &6Y: &e{POSITION_Y}, &6Z: &e{POSITION_Z}", - "&6Priority: &e{PRIORITY}", - "&6Description: &e{DESCRIPTION}" + "&f• &6UUID: &e{UUID}", + "&f• &6Sender: &e{SENDER}", + "&f• &6Receiver: &e{RECEIVER}", + "&f• &6Size: &e{SIZE}", + "&f• &6Position: &6X: &e{POSITION_X}, &6Y: &e{POSITION_Y}, &6Z: &e{POSITION_Z}", + "&f• &6Priority: &e{PRIORITY}", + "&f• &6Description: &e{DESCRIPTION}" ); } @@ -153,19 +158,19 @@ public static class GuiSettings { @Description({ " ", "# The item of the small parcel size button" }) public ConfigItem smallParcelSizeItem = new ConfigItem() .setName("&a➻ Small") - .setLore(List.of("&bClick to select the small parcel size.")) + .setLore(List.of("&aClick to select the small parcel size.")) .setType(Material.LIME_WOOL); @Description({ " ", "# The item of the medium parcel size button" }) public ConfigItem mediumParcelSizeItem = new ConfigItem() .setName("&e➼ Medium") - .setLore(List.of("&bClick to select the medium parcel size.")) + .setLore(List.of("&eClick to select the medium parcel size.")) .setType(Material.YELLOW_WOOL); @Description({ " ", "# The item of the large parcel size button" }) public ConfigItem largeParcelSizeItem = new ConfigItem() .setName("&c➽ Large") - .setLore(List.of("&bClick to select the large parcel size.")) + .setLore(List.of("&cClick to select the large parcel size.")) .setType(Material.RED_WOOL); @Description({ " ", "# The item represents selected small parcel size." }) @@ -191,15 +196,15 @@ public static class GuiSettings { @Description({ " ", "# The item of the priority button" }) public ConfigItem priorityItem = new ConfigItem() - .setName("&a❀ Priority") - .setLore(List.of("&bClick to select the priority.")) - .setType(Material.REDSTONE); + .setName("&c\uD83D\uDE80 Priority") + .setLore(List.of("&cClick to select the priority.")) + .setType(Material.FIREWORK_ROCKET); @Description({ " ", "# The item of the selected priority button" }) public ConfigItem selectedPriorityItem = new ConfigItem() - .setName("&a✿ Priority") - .setLore(List.of("&aCurrently selected!", "&c&oClick to unselect.")) - .setType(Material.REDSTONE_BLOCK) + .setName("&c\uD83D\uDE80 Priority") + .setLore(List.of("&4Currently selected!", "&7&oClick to unselect.")) + .setType(Material.FIREWORK_ROCKET) .setGlow(true); @Description({ " ", "# The close button item" }) @@ -222,49 +227,42 @@ public static class GuiSettings { @Description({ " ", "# The item of the parcel submit button" }) public ConfigItem submitParcelItem = new ConfigItem() - .setName("&a&l✔ Submit parcel") - .setLore(List.of("Click to submit the parcel.", "Proceed with caution! This action is final and cannot be undone.")) - .setType(Material.WAXED_OXIDIZED_COPPER) + .setName("&a✔ Submit") + .setLore(List.of("Click to submit the parcel.", "Proceed with caution! This action is final and cannot be undone.")) + .setType(Material.ENDER_PEARL) .setGlow(true); @Description({ " ", "# The item of the parcel list button" }) public ConfigItem myParcelsItem = new ConfigItem() - .setName("&3⌬ My parcels") + .setName("&3⛃ My parcels") .setLore(List.of("&bClick to open your parcels.")) .setType(Material.ENDER_CHEST); @Description({ " ", "# The item of the sent parcels button" }) public ConfigItem sentParcelsItem = new ConfigItem() - .setName("&6↪ Sent parcels") + .setName("&6\uD83D\uDCE6 Sent parcels") .setLore(List.of("&eClick to show parcels, which you sent.", "&eYou can also cancel them here, if you want to.")) - .setType(Material.FILLED_MAP) + .setType(Material.YELLOW_SHULKER_BOX) .setGlow(true); @Description({ " ", "# The parcel archive item button." }) public ConfigItem parcelArchiveItem = new ConfigItem() - .setName("&5♲ Parcel archive") - .setLore(List.of("&eClick to show all parcels, which you sent or received in the past.")) - .setType(Material.WRITTEN_BOOK); + .setName("&5\uD83D\uDCDA Parcel archive") + .setLore(List.of("&eClick to show all parcels, which you sent or received in the past.", "&c&oNot implemented yet")) + .setType(Material.BOOKSHELF); @Description({ " ", "# The item of the parcel locker collect button" }) public ConfigItem parcelLockerCollectItem = new ConfigItem() - .setName("&a✔ Collect parcels") - .setLore(List.of("&aClick to collect your parcels.")) - .setType(Material.HOPPER) + .setName("&2⬇ Collect parcels") + .setLore(List.of("&2Click to collect your parcels.")) + .setType(Material.CHEST) .setGlow(true); @Description({ " ", "# The item of the parcel locker send button" }) public ConfigItem parcelLockerSendItem = new ConfigItem() - .setName("&b⛟ Send parcels") - .setLore(List.of("&bClick to send parcels.")) - .setType(Material.SCULK_SHRIEKER) - .setGlow(true); - - @Description({ " ", "# The item of the parcel locker status button" }) - public ConfigItem parcelLockerStatusItem = new ConfigItem() - .setName("&3⌛ Parcel locker status") - .setLore(List.of("&bClick to show the status of your parcel locker.")) - .setType(Material.END_PORTAL_FRAME) + .setName("&6⬆ Send parcels") + .setLore(List.of("&6Click to send parcels.")) + .setType(Material.CHEST_MINECART) .setGlow(true); @Description({ " ", "# The item of the parcel" }) @@ -290,7 +288,7 @@ public static class GuiSettings { @Description({ " ", "# The item of the parcel name button" }) public ConfigItem parcelNameItem = new ConfigItem() - .setName("&4❁ &cParcel name") + .setName("&4✎ &cParcel name") .setLore(List.of("&cClick to edit the parcel name.")) .setType(Material.NAME_TAG); @@ -322,9 +320,9 @@ public static class GuiSettings { @Description({ " ", "# The item of the parcel destination locker button" }) public ConfigItem parcelDestinationLockerItem = new ConfigItem() - .setName("&3↠ &bDestination locker") - .setLore(List.of("&bClick to edit the parcel destination locker.")) - .setType(Material.VAULT); + .setName("&3\uD83D\uDCCD Destination locker") + .setLore(List.of("&3Click to edit the parcel destination locker.")) + .setType(Material.COMPASS); @Description({ " ", "# The item of the previous page button" }) public ConfigItem previousPageItem = new ConfigItem() @@ -345,13 +343,13 @@ public static class GuiSettings { .setType(Material.LIME_WOOL); @Description({ " ", "# The name of the parcel small content GUI" }) - public String parcelSmallContentGuiTitle = "&aSmall parcel content"; + public String parcelSmallContentGuiTitle = "&2Small parcel content"; @Description({ " ", "# The name of the parcel medium content GUI" }) - public String parcelMediumContentGuiTitle = "&eMedium parcel content"; + public String parcelMediumContentGuiTitle = "&6Medium parcel content"; @Description({ " ", "# The name of the parcel large content GUI" }) - public String parcelLargeContentGuiTitle = "&cLarge parcel content"; + public String parcelLargeContentGuiTitle = "&4Large parcel content"; @Description({ " ", "# The title of the parcel destination locker selection GUI" }) public String parcelDestinationLockerSelectionGuiTitle = "&3Select destination locker"; @@ -383,7 +381,9 @@ public static class GuiSettings { Material.JIGSAW, Material.DEBUG_STICK, Material.SPAWNER, - Material.BEDROCK + Material.BEDROCK, + Material.VAULT, + Material.END_PORTAL_FRAME ); @Description({ " ", "# The item of the parcel item in the collection GUI" }) diff --git a/src/main/java/com/eternalcode/parcellockers/delivery/Delivery.java b/src/main/java/com/eternalcode/parcellockers/delivery/Delivery.java new file mode 100644 index 00000000..035305e1 --- /dev/null +++ b/src/main/java/com/eternalcode/parcellockers/delivery/Delivery.java @@ -0,0 +1,8 @@ +package com.eternalcode.parcellockers.delivery; + +import java.time.Instant; +import java.util.UUID; + +public record Delivery(UUID parcel, Instant deliveryTimestamp) { + +} diff --git a/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryRepository.java b/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryRepository.java new file mode 100644 index 00000000..b31284fd --- /dev/null +++ b/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryRepository.java @@ -0,0 +1,19 @@ +package com.eternalcode.parcellockers.delivery.repository; + +import com.eternalcode.parcellockers.delivery.Delivery; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public interface DeliveryRepository { + + CompletableFuture save(Delivery delivery); + + CompletableFuture> find(UUID parcel); + + CompletableFuture remove(UUID parcel); + + CompletableFuture>> findAll(); +} diff --git a/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryRepositoryOrmLite.java b/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryRepositoryOrmLite.java new file mode 100644 index 00000000..cb17aa78 --- /dev/null +++ b/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryRepositoryOrmLite.java @@ -0,0 +1,49 @@ +package com.eternalcode.parcellockers.delivery.repository; + +import com.eternalcode.commons.scheduler.Scheduler; +import com.eternalcode.parcellockers.database.DatabaseManager; +import com.eternalcode.parcellockers.database.wrapper.AbstractRepositoryOrmLite; +import com.eternalcode.parcellockers.delivery.Delivery; +import com.j256.ormlite.table.TableUtils; + +import java.sql.SQLException; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class DeliveryRepositoryOrmLite extends AbstractRepositoryOrmLite implements DeliveryRepository { + + public DeliveryRepositoryOrmLite(DatabaseManager databaseManager, Scheduler scheduler) { + super(databaseManager, scheduler); + + try { + TableUtils.createTableIfNotExists(databaseManager.connectionSource(), DeliveryWrapper.class); + } catch (SQLException exception) { + exception.printStackTrace(); + } + } + + @Override + public CompletableFuture save(Delivery delivery) { + return this.saveIfNotExist(DeliveryWrapper.class, DeliveryWrapper.from(delivery)).thenApply(dao -> null); + } + + @Override + public CompletableFuture> find(UUID parcel) { + return this.selectSafe(DeliveryWrapper.class, parcel) + .thenApply(optional -> optional.map(DeliveryWrapper::toDelivery)); + } + + @Override + public CompletableFuture>> findAll() { + return this.selectAll(DeliveryWrapper.class).thenApply(parcels -> Optional.of(parcels.stream() + .map(DeliveryWrapper::toDelivery) + .toList())); + } + + @Override + public CompletableFuture remove(UUID parcel) { + return this.deleteById(DeliveryWrapper.class, parcel); + } +} diff --git a/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryWrapper.java b/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryWrapper.java new file mode 100644 index 00000000..c14f72e9 --- /dev/null +++ b/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryWrapper.java @@ -0,0 +1,34 @@ +package com.eternalcode.parcellockers.delivery.repository; + +import com.eternalcode.parcellockers.delivery.Delivery; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +import java.time.Instant; +import java.util.UUID; + +@DatabaseTable(tableName = "deliveries") +class DeliveryWrapper { + + @DatabaseField(id = true) + private UUID parcel; + + @DatabaseField + private Instant deliveryTimestamp; + + DeliveryWrapper() { + } + + DeliveryWrapper(UUID parcel, Instant deliveryTimestamp) { + this.parcel = parcel; + this.deliveryTimestamp = deliveryTimestamp; + } + + public static DeliveryWrapper from(Delivery delivery) { + return new DeliveryWrapper(delivery.parcel(), delivery.deliveryTimestamp()); + } + + Delivery toDelivery() { + return new Delivery(this.parcel, this.deliveryTimestamp); + } +} diff --git a/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/LockerMainGui.java b/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/LockerMainGui.java index 45ec9543..22edc12a 100644 --- a/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/LockerMainGui.java +++ b/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/LockerMainGui.java @@ -8,6 +8,7 @@ import com.eternalcode.parcellockers.notification.NotificationAnnouncer; import com.eternalcode.parcellockers.parcel.ParcelManager; import com.eternalcode.parcellockers.parcel.repository.ParcelRepository; +import com.eternalcode.parcellockers.user.UserManager; import com.eternalcode.parcellockers.user.repository.UserRepository; import dev.rollczi.liteskullapi.SkullAPI; import dev.triumphteam.gui.guis.Gui; @@ -33,6 +34,8 @@ public class LockerMainGui implements GuiView { private final SkullAPI skullAPI; private final ParcelManager parcelManager; + private final UserManager userManager; + public LockerMainGui(Plugin plugin, MiniMessage miniMessage, PluginConfiguration config, @@ -53,6 +56,8 @@ public LockerMainGui(Plugin plugin, this.userRepository = userRepository; this.skullAPI = skullAPI; this.parcelManager = parcelManager; + + this.userManager = new UserManager(this.userRepository); } @Override @@ -71,12 +76,13 @@ public void show(Player player) { //gui.setDefaultClickAction(event -> event.setCancelled(true)); - for (int slot : CORNER_SLOTS) { - gui.setItem(slot, cornerItem); + int size = gui.getRows() * 9; + for (int i = 0; i < size; i++) { + gui.setItem(i, backgroundItem); } - for (int slot : BORDER_SLOTS) { - gui.setItem(slot, backgroundItem); + for (int slot : CORNER_SLOTS) { + gui.setItem(slot, cornerItem); } ParcelCollectionGui collectionGui = new ParcelCollectionGui(this.plugin, @@ -84,12 +90,13 @@ public void show(Player player) { this.plugin.getServer().getScheduler(), this.parcelRepository, this.miniMessage, - this.parcelManager + this.parcelManager, + this.userManager, + this.lockerRepository ); - gui.setItem(20, this.config.guiSettings.parcelLockerCollectItem.toGuiItem(event -> collectionGui.show(player))); - - gui.setItem(22, this.config.guiSettings.parcelLockerSendItem.toGuiItem(event -> new ParcelSendingGui(this.plugin, + gui.setItem(21, this.config.guiSettings.parcelLockerCollectItem.toGuiItem(event -> collectionGui.show(player))); + gui.setItem(23, this.config.guiSettings.parcelLockerSendItem.toGuiItem(event -> new ParcelSendingGui(this.plugin, this.config, this.miniMessage, this.itemStorageRepository, @@ -103,9 +110,7 @@ public void show(Player player) { new ParcelSendingGuiState() ).show(player))); - gui.setItem(24, this.config.guiSettings.parcelLockerStatusItem.toGuiItem()); gui.setItem(49, closeItem); - gui.open(player); } } diff --git a/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelCollectionGui.java b/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelCollectionGui.java index 8718c79d..cb8d085b 100644 --- a/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelCollectionGui.java +++ b/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelCollectionGui.java @@ -3,18 +3,21 @@ import com.eternalcode.parcellockers.configuration.implementation.ConfigItem; import com.eternalcode.parcellockers.configuration.implementation.PluginConfiguration; import com.eternalcode.parcellockers.gui.GuiView; +import com.eternalcode.parcellockers.locker.repository.LockerRepository; import com.eternalcode.parcellockers.parcel.Parcel; import com.eternalcode.parcellockers.parcel.ParcelManager; +import com.eternalcode.parcellockers.parcel.ParcelStatus; import com.eternalcode.parcellockers.parcel.repository.ParcelRepository; +import com.eternalcode.parcellockers.parcel.util.ParcelPlaceholderUtil; import com.eternalcode.parcellockers.shared.Page; import com.eternalcode.parcellockers.shared.SentryExceptionHandler; +import com.eternalcode.parcellockers.user.UserManager; import com.eternalcode.parcellockers.util.InventoryUtil; import dev.triumphteam.gui.guis.Gui; import dev.triumphteam.gui.guis.GuiItem; import dev.triumphteam.gui.guis.PaginatedGui; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; -import org.bukkit.Sound; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.bukkit.scheduler.BukkitScheduler; @@ -31,14 +34,18 @@ public class ParcelCollectionGui implements GuiView { private final ParcelRepository parcelRepository; private final MiniMessage miniMessage; private final ParcelManager parcelManager; + private final UserManager userManager; + private final LockerRepository lockerRepository; - public ParcelCollectionGui(Plugin plugin, PluginConfiguration config, BukkitScheduler scheduler, ParcelRepository parcelRepository, MiniMessage miniMessage, ParcelManager parcelManager) { + public ParcelCollectionGui(Plugin plugin, PluginConfiguration config, BukkitScheduler scheduler, ParcelRepository parcelRepository, MiniMessage miniMessage, ParcelManager parcelManager, UserManager userManager, LockerRepository lockerRepository) { this.plugin = plugin; this.config = config; this.scheduler = scheduler; this.parcelRepository = parcelRepository; this.miniMessage = miniMessage; this.parcelManager = parcelManager; + this.userManager = userManager; + this.lockerRepository = lockerRepository; } @Override @@ -83,7 +90,7 @@ private void show(Player player, Page page) { this.parcelRepository.findByReceiver(player.getUniqueId(), page).thenAccept(result -> { if (result == null || result.parcels().isEmpty()) { - gui.setItem(22, guiSettings.noParcelsItem.toGuiItem(event -> player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_TELEPORT, 0.5F, 1))); + gui.setItem(22, guiSettings.noParcelsItem.toGuiItem(event -> player.playSound(player.getLocation(), this.config.settings.errorSound, this.config.settings.errorSoundVolume, this.config.settings.errorSoundPitch))); } if (result.hasNextPage()) { @@ -95,14 +102,14 @@ private void show(Player player, Page page) { } for (Parcel parcel : result.parcels()) { + + if (parcel.status() != ParcelStatus.DELIVERED) { + continue; + } + ConfigItem item = parcelItem.clone(); item.name = item.name.replace("{NAME}", parcel.name()); - item.lore = item.lore.stream() - .map(line -> line.replace("{UUID}", parcel.uuid().toString())) - .map(line -> line.replace("{DESCRIPTION}", parcel.description() == null ? "" : parcel.description())) - .map(line -> line.replace("{SIZE}", parcel.size().name())) - .map(line -> line.replace("{SENDER}", parcel.sender().toString())) - .toList(); + item.lore = ParcelPlaceholderUtil.replaceParcelPlaceholders(parcel, item.lore, this.userManager, this.lockerRepository); item.setGlow(true); diff --git a/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelItemStorageGui.java b/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelItemStorageGui.java index 16fa3522..544a2682 100644 --- a/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelItemStorageGui.java +++ b/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelItemStorageGui.java @@ -125,7 +125,7 @@ void show(Player player, ParcelSize size) { ItemUtil.giveItem(player, item); gui.removeItem(item); player.playSound(player.getLocation(), Sound.ENTITY_ENDERMAN_AMBIENT, 1, 1); - this.announcer.sendMessage(player, this.config.messages.illegalItemFailedToSend); + this.announcer.sendMessage(player, this.config.messages.illegalItemFailedToSend.replace("{ITEMS}", item.getType().name())); } } diff --git a/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelSendingGui.java b/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelSendingGui.java index 12d64284..c15082a0 100644 --- a/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelSendingGui.java +++ b/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelSendingGui.java @@ -3,7 +3,6 @@ import com.eternalcode.commons.adventure.AdventureUtil; import com.eternalcode.parcellockers.configuration.implementation.ConfigItem; import com.eternalcode.parcellockers.configuration.implementation.PluginConfiguration; -import com.eternalcode.parcellockers.content.ParcelContent; import com.eternalcode.parcellockers.content.repository.ParcelContentRepository; import com.eternalcode.parcellockers.gui.GuiView; import com.eternalcode.parcellockers.itemstorage.repository.ItemStorageRepository; @@ -13,7 +12,6 @@ import com.eternalcode.parcellockers.parcel.ParcelManager; import com.eternalcode.parcellockers.parcel.ParcelSize; import com.eternalcode.parcellockers.parcel.repository.ParcelRepository; -import com.eternalcode.parcellockers.shared.SentryExceptionHandler; import com.eternalcode.parcellockers.user.repository.UserRepository; import de.rapha149.signgui.SignGUI; import de.rapha149.signgui.SignGUIAction; @@ -98,9 +96,8 @@ public void show(Player player) { GuiItem cornerItem = guiSettings.cornerItem.toGuiItem(); ConfigItem nameItem = guiSettings.parcelNameItem.clone(); GuiItem nameGuiItem = nameItem.toGuiItem(event -> { - SignGUI nameSignGui = null; try { - nameSignGui = SignGUI.builder() + SignGUI nameSignGui = SignGUI.builder() .setColor(DyeColor.BLACK) .setType(Material.OAK_SIGN) .setLine(0, "Enter parcel name:") @@ -129,10 +126,11 @@ public void show(Player player) { return List.of(SignGUIAction.runSync((JavaPlugin) this.plugin, () -> this.gui.open(player))); }) .build(); + nameSignGui.open(player); } catch (SignGUIVersionException e) { this.plugin.getLogger().severe("The server version is unsupported by SignGUI API!"); } - nameSignGui.open(player); + }); ConfigItem descriptionItem = guiSettings.parcelDescriptionItem.clone(); @@ -203,34 +201,23 @@ public void show(Player player) { this.itemStorageRepository.find(player.getUniqueId()).thenAccept(result -> { if (result.isEmpty() || result.get().items().isEmpty()) { this.announcer.sendMessage(player, settings.messages.parcelCannotBeEmpty); - this.gui.close(player); + player.playSound(player, this.config.settings.errorSound, this.config.settings.errorSoundVolume, this.config.settings.errorSoundPitch); return; } if (this.state.getReceiver() == null) { this.announcer.sendMessage(player, settings.messages.receiverNotSet); + player.playSound(player, this.config.settings.errorSound, this.config.settings.errorSoundVolume, this.config.settings.errorSoundPitch); return; } Parcel parcel = new Parcel(UUID.randomUUID(), player.getUniqueId(), this.state.getParcelName(), this.state.getParcelDescription(), this.state.isPriority(), this.state.getReceiver(), - this.state.getSize(), this.state.getEntryLocker(), this.state.getDestinationLocker()); - - this.parcelRepository.save(parcel).thenAccept(unused -> { - - this.parcelContentRepository.save(new ParcelContent(parcel.uuid(), result.get().items()) - ).thenAccept(none -> this.itemStorageRepository.remove(player.getUniqueId())); + this.state.getSize(), this.state.getEntryLocker(), this.state.getDestinationLocker(), this.state.getStatus()); - this.announcer.sendMessage(player, settings.messages.parcelSent); - this.gui.close(player); - - }).whenComplete(SentryExceptionHandler.handler() - .andThen((unused, throwable) -> { - if (throwable != null) { - this.announcer.sendMessage(player, settings.messages.parcelFailedToSend); - } - } - )); + this.parcelManager.sendParcel(player, parcel, result.get().items()) + .thenRun(() -> this.itemStorageRepository.remove(player.getUniqueId())); + this.gui.close(player); }).orTimeout(5, TimeUnit.SECONDS)); GuiItem closeItem = guiSettings.closeItem.toGuiItem(event -> @@ -252,14 +239,15 @@ public void show(Player player) { ConfigItem largeButton = guiSettings.largeParcelSizeItem; ConfigItem priorityItem = guiSettings.priorityItem; + + int size = gui.getRows() * 9; + for (int i = 0; i < size; i++) { + gui.setItem(i, backgroundItem); + } for (int slot : CORNER_SLOTS) { this.gui.setItem(slot, cornerItem); } - for (int slot : BORDER_SLOTS) { - this.gui.setItem(slot, backgroundItem); - } - this.gui.setItem(12, smallButton.toGuiItem(event -> this.setSelected(this.gui, ParcelSize.SMALL))); this.gui.setItem(13, mediumButton.toGuiItem(event -> this.setSelected(this.gui, ParcelSize.MEDIUM))); this.gui.setItem(14, largeButton.toGuiItem(event -> this.setSelected(this.gui, ParcelSize.LARGE))); diff --git a/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelSendingGuiState.java b/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelSendingGuiState.java index 638ad52b..eec9471c 100644 --- a/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelSendingGuiState.java +++ b/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ParcelSendingGuiState.java @@ -1,6 +1,7 @@ package com.eternalcode.parcellockers.gui.implementation.locker; import com.eternalcode.parcellockers.parcel.ParcelSize; +import com.eternalcode.parcellockers.parcel.ParcelStatus; import java.util.UUID; @@ -13,6 +14,7 @@ public class ParcelSendingGuiState { private boolean priority; private UUID entryLocker; private UUID destinationLocker; + private ParcelStatus status; public ParcelSendingGuiState() { this.parcelName = null; @@ -22,6 +24,7 @@ public ParcelSendingGuiState() { this.priority = false; this.entryLocker = UUID.randomUUID(); this.destinationLocker = null; + this.status = ParcelStatus.PENDING; } public String getParcelName() { @@ -79,4 +82,12 @@ public UUID getEntryLocker() { public void setEntryLocker(UUID entryLocker) { this.entryLocker = entryLocker; } + + public ParcelStatus getStatus() { + return this.status; + } + + public void setStatus(ParcelStatus status) { + this.status = status; + } } diff --git a/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ReceiverSelectionGui.java b/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ReceiverSelectionGui.java index 99dd3150..49968e22 100644 --- a/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ReceiverSelectionGui.java +++ b/src/main/java/com/eternalcode/parcellockers/gui/implementation/locker/ReceiverSelectionGui.java @@ -123,7 +123,7 @@ private Supplier toItem(Player player, User user, SkullData skullData, : this.config.guiSettings.parcelReceiverNotSetLine; return ItemBuilder.skull() - .texture(skullData.getValue()) + .texture(skullData.getTexture()) .name(this.miniMessage.deserialize(user.name())) .lore(this.miniMessage.deserialize(lore)) .glow(uuid.equals(this.state.getReceiver())) diff --git a/src/main/java/com/eternalcode/parcellockers/gui/implementation/remote/MainGui.java b/src/main/java/com/eternalcode/parcellockers/gui/implementation/remote/MainGui.java index b216d61f..aa7063e8 100644 --- a/src/main/java/com/eternalcode/parcellockers/gui/implementation/remote/MainGui.java +++ b/src/main/java/com/eternalcode/parcellockers/gui/implementation/remote/MainGui.java @@ -57,7 +57,6 @@ public void show(Player player) { GuiItem closeItem = guiSettings.closeItem.toGuiItem(event -> gui.close(player)); GuiItem cornerItem = guiSettings.cornerItem.toGuiItem(); - int size = gui.getRows() * 9; for (int i = 0; i < size; i++) { gui.setItem(i, backgroundItem); diff --git a/src/main/java/com/eternalcode/parcellockers/gui/implementation/remote/SentParcelsGui.java b/src/main/java/com/eternalcode/parcellockers/gui/implementation/remote/SentParcelsGui.java index 3031790f..e581e4b1 100644 --- a/src/main/java/com/eternalcode/parcellockers/gui/implementation/remote/SentParcelsGui.java +++ b/src/main/java/com/eternalcode/parcellockers/gui/implementation/remote/SentParcelsGui.java @@ -3,10 +3,10 @@ import com.eternalcode.parcellockers.configuration.implementation.ConfigItem; import com.eternalcode.parcellockers.configuration.implementation.PluginConfiguration; import com.eternalcode.parcellockers.gui.GuiView; -import com.eternalcode.parcellockers.locker.Locker; import com.eternalcode.parcellockers.locker.repository.LockerRepository; import com.eternalcode.parcellockers.parcel.Parcel; import com.eternalcode.parcellockers.parcel.repository.ParcelRepository; +import com.eternalcode.parcellockers.parcel.util.ParcelPlaceholderUtil; import com.eternalcode.parcellockers.shared.SentryExceptionHandler; import com.eternalcode.parcellockers.user.UserManager; import dev.triumphteam.gui.builder.item.ItemBuilder; @@ -18,15 +18,9 @@ import org.bukkit.Server; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; -import org.jetbrains.annotations.Blocking; -import panda.utilities.text.Formatter; -import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; public class SentParcelsGui implements GuiView { @@ -80,7 +74,7 @@ public void show(Player player) { for (Parcel parcel : parcels) { ItemBuilder item = parcelItem.toBuilder(); - List newLore = this.replaceParcelPlaceholders(parcel, parcelItem.lore) + List newLore = ParcelPlaceholderUtil.replaceParcelPlaceholders(parcel, parcelItem.lore, this.userManager, this.lockerRepository) .stream() .map(line -> this.miniMessage.deserialize(line)) .toList(); @@ -92,52 +86,4 @@ public void show(Player player) { this.server.getScheduler().runTask(this.plugin, () -> gui.open(player)); }).whenComplete(SentryExceptionHandler.handler()); } - - @Blocking - private List replaceParcelPlaceholders(Parcel parcel, List lore) { - if (lore == null || lore.isEmpty()) { - return Collections.emptyList(); - } - - String senderName = this.getName(parcel.sender()).join(); - String receiver = this.getName(parcel.receiver()).join(); - - Formatter formatter = new Formatter() - .register("{UUID}", parcel.uuid().toString()) - .register("{NAME}", parcel.name()) - .register("{SENDER}", senderName) - .register("{RECEIVER}", receiver) - .register("{SIZE}", parcel.size().toString()) - .register("{PRIORITY}", parcel.priority() ? "&aYes" : "&cNo") - .register("{DESCRIPTION}", parcel.description()); - - Optional lockerOptional = this.lockerRepository.findByUUID(parcel.destinationLocker()).join(); - - if (lockerOptional.isPresent()) { - Locker locker = lockerOptional.get(); - formatter.register("{POSITION_X}", locker.position().x()) - .register("{POSITION_Y}", locker.position().y()) - .register("{POSITION_Z}", locker.position().z()); - } else { - formatter.register("{POSITION_X}", "-") - .register("{POSITION_Y}", "-") - .register("{POSITION_Z}", "-"); - } - - List newLore = new ArrayList<>(); - - for (String line : lore) { - newLore.add(formatter.format(line)); - } - - return newLore; - } - - private CompletableFuture getName(UUID userUuid) { - return this.userManager.getUser(userUuid).thenApply(userOptional -> userOptional - .map(user -> user.name()) - .orElse("Unknown") - ); - } - } diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/Parcel.java b/src/main/java/com/eternalcode/parcellockers/parcel/Parcel.java index 8d529dc4..36875f38 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/Parcel.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/Parcel.java @@ -10,6 +10,6 @@ public record Parcel(UUID uuid, UUID receiver, ParcelSize size, UUID entryLocker, - UUID destinationLocker) { - + UUID destinationLocker, + ParcelStatus status) { } diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/ParcelManager.java b/src/main/java/com/eternalcode/parcellockers/parcel/ParcelManager.java index ce8a2156..c58f7abc 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/ParcelManager.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/ParcelManager.java @@ -3,16 +3,25 @@ import com.eternalcode.commons.bukkit.ItemUtil; import com.eternalcode.commons.scheduler.Scheduler; import com.eternalcode.parcellockers.configuration.implementation.PluginConfiguration; +import com.eternalcode.parcellockers.content.ParcelContent; import com.eternalcode.parcellockers.content.repository.ParcelContentRepository; +import com.eternalcode.parcellockers.delivery.Delivery; +import com.eternalcode.parcellockers.delivery.repository.DeliveryRepository; import com.eternalcode.parcellockers.notification.NotificationAnnouncer; import com.eternalcode.parcellockers.parcel.repository.ParcelRepository; +import com.eternalcode.parcellockers.parcel.task.ParcelSendTask; import com.eternalcode.parcellockers.shared.SentryExceptionHandler; import org.bukkit.Sound; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import panda.std.Blank; +import panda.std.Result; +import java.time.Duration; +import java.time.Instant; import java.util.List; +import java.util.concurrent.CompletableFuture; import static com.eternalcode.parcellockers.util.InventoryUtil.freeSlotsInInventory; @@ -21,28 +30,48 @@ public class ParcelManager { private final PluginConfiguration config; private final NotificationAnnouncer announcer; private final ParcelRepository parcelRepository; + private final DeliveryRepository deliveryRepository; private final ParcelContentRepository parcelContentRepository; private final Scheduler scheduler; - public ParcelManager(PluginConfiguration config, NotificationAnnouncer announcer, ParcelRepository parcelRepository, ParcelContentRepository parcelContentRepository, Scheduler scheduler) { + public ParcelManager(PluginConfiguration config, NotificationAnnouncer announcer, ParcelRepository parcelRepository, DeliveryRepository deliveryRepository, ParcelContentRepository parcelContentRepository, Scheduler scheduler) { this.config = config; this.announcer = announcer; this.parcelRepository = parcelRepository; + this.deliveryRepository = deliveryRepository; this.parcelContentRepository = parcelContentRepository; this.scheduler = scheduler; } - public void createParcel(CommandSender sender, Parcel parcel) { - this.parcelRepository.save(parcel) - .whenComplete(SentryExceptionHandler.handler() - .andThen((v, throwable) -> { + public CompletableFuture> sendParcel(CommandSender sender, Parcel parcel, List items) { + Duration delay = parcel.priority() + ? config.settings.priorityParcelSendDuration + : config.settings.parcelSendDuration; + + parcelRepository.save(parcel); + + return parcelContentRepository.save(new ParcelContent(parcel.uuid(), items)) + .handle((content, throwable) -> { if (throwable != null) { - this.announcer.sendMessage(sender, this.config.messages.failedToCreateParcel); - return; + announcer.sendMessage(sender, config.messages.parcelFailedToSend); + return Result.error(throwable); } - this.announcer.sendMessage(sender, this.config.messages.parcelSuccessfullyCreated); - } - )); + + announcer.sendMessage(sender, config.messages.parcelSent); + + scheduler.runLaterAsync( + new ParcelSendTask( + parcel, + new Delivery(parcel.uuid(), Instant.now().plus(delay)), + parcelRepository, + deliveryRepository, + config + ), + delay + ); + + return Result.ok(); + }); } public void deleteParcel(CommandSender sender, Parcel parcel) { @@ -59,21 +88,22 @@ public void deleteParcel(CommandSender sender, Parcel parcel) { public void collectParcel(Player player, Parcel parcel) { this.parcelContentRepository.findByUUID(parcel.uuid()).thenAccept(optional -> { if (optional.isEmpty()) { - player.playSound(player.getLocation(), Sound.ITEM_CHORUS_FRUIT_TELEPORT, 0.5F, 1); + player.playSound(player.getLocation(), this.config.settings.errorSound, this.config.settings.errorSoundVolume, this.config.settings.errorSoundPitch); this.announcer.sendMessage(player, this.config.messages.failedToCollectParcel); return; } List items = optional.get().items(); if (items.size() > freeSlotsInInventory(player)) { - player.playSound(player.getLocation(), Sound.ITEM_CHORUS_FRUIT_TELEPORT, 0.5F, 1); + player.playSound(player.getLocation(), this.config.settings.errorSound, this.config.settings.errorSoundVolume, this.config.settings.errorSoundPitch); this.announcer.sendMessage(player, this.config.messages.notEnoughInventorySpace); return; } - items.forEach(item -> - this.scheduler.run(() -> ItemUtil.giveItem(player, item)) - ); + for (ItemStack item : items) { + this.scheduler.run(() -> ItemUtil.giveItem(player, item)); + } + this.parcelRepository.remove(parcel) .thenCompose(v -> this.parcelContentRepository.remove(optional.get().uniqueId())) .whenComplete(SentryExceptionHandler.handler().andThen((v, throwable) -> { diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/ParcelStatus.java b/src/main/java/com/eternalcode/parcellockers/parcel/ParcelStatus.java new file mode 100644 index 00000000..67f7b1cf --- /dev/null +++ b/src/main/java/com/eternalcode/parcellockers/parcel/ParcelStatus.java @@ -0,0 +1,7 @@ +package com.eternalcode.parcellockers.parcel; + +public enum ParcelStatus { + + PENDING, + DELIVERED +} diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/command/ParcelCommand.java b/src/main/java/com/eternalcode/parcellockers/parcel/command/ParcelCommand.java index 13700b0d..329091ff 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/command/ParcelCommand.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/command/ParcelCommand.java @@ -10,6 +10,7 @@ import com.eternalcode.parcellockers.parcel.util.ParcelPlaceholderUtil; import com.eternalcode.parcellockers.user.UserManager; import dev.rollczi.litecommands.annotations.argument.Arg; +import dev.rollczi.litecommands.annotations.async.Async; import dev.rollczi.litecommands.annotations.command.Command; import dev.rollczi.litecommands.annotations.context.Context; import dev.rollczi.litecommands.annotations.execute.Execute; @@ -45,6 +46,7 @@ void list(@Context Player player) { this.parcelListGUI.show(player); } + @Async @Execute(name = "info") void info(@Context Player player, @Arg Parcel parcel) { List messagesToSend = ParcelPlaceholderUtil.replaceParcelPlaceholders(parcel, this.config.messages.parcelInfoMessages, this.userManager, this.lockerRepository); diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepository.java b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepository.java index 66349217..2fa179e6 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepository.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepository.java @@ -12,6 +12,8 @@ public interface ParcelRepository { CompletableFuture save(Parcel parcel); + CompletableFuture update(Parcel parcel); + CompletableFuture>> findAll(); CompletableFuture> findByUUID(UUID uuid); diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java index 026fd453..c55549ce 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java @@ -38,7 +38,16 @@ public ParcelRepositoryOrmLite(DatabaseManager databaseManager, Scheduler schedu @Override public CompletableFuture save(Parcel parcel) { + return this.saveIfNotExist(ParcelWrapper.class, ParcelWrapper.from(parcel)).thenApply(dao -> { + this.cache.put(parcel); + return null; + }); + } + + @Override + public CompletableFuture update(Parcel parcel) { return this.save(ParcelWrapper.class, ParcelWrapper.from(parcel)).thenApply(dao -> { + this.cache.remove(parcel.uuid()); this.cache.put(parcel); return null; }); diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelWrapper.java b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelWrapper.java index aca705d3..43a8e0cf 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelWrapper.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelWrapper.java @@ -2,6 +2,7 @@ import com.eternalcode.parcellockers.parcel.Parcel; import com.eternalcode.parcellockers.parcel.ParcelSize; +import com.eternalcode.parcellockers.parcel.ParcelStatus; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; @@ -37,10 +38,13 @@ class ParcelWrapper { @DatabaseField(columnName = "destinationLocker", index = true) private UUID destinationLocker; + @DatabaseField(columnName = "status") + private ParcelStatus status; + ParcelWrapper() { } - ParcelWrapper(UUID uuid, UUID sender, String name, String description, boolean priority, UUID receiver, ParcelSize size, UUID entryLocker, UUID destinationLocker) { + ParcelWrapper(UUID uuid, UUID sender, String name, String description, boolean priority, UUID receiver, ParcelSize size, UUID entryLocker, UUID destinationLocker, ParcelStatus status) { this.uuid = uuid; this.sender = sender; this.name = name; @@ -50,13 +54,14 @@ class ParcelWrapper { this.size = size; this.entryLocker = entryLocker; this.destinationLocker = destinationLocker; + this.status = status; } static ParcelWrapper from(Parcel parcel) { - return new ParcelWrapper(parcel.uuid(), parcel.sender(), parcel.name(), parcel.description(), parcel.priority(), parcel.receiver(), parcel.size(), parcel.entryLocker(), parcel.destinationLocker()); + return new ParcelWrapper(parcel.uuid(), parcel.sender(), parcel.name(), parcel.description(), parcel.priority(), parcel.receiver(), parcel.size(), parcel.entryLocker(), parcel.destinationLocker(), parcel.status()); } Parcel toParcel() { - return new Parcel(this.uuid, this.sender, this.name, this.description, this.priority, this.receiver, this.size, this.entryLocker, this.destinationLocker); + return new Parcel(this.uuid, this.sender, this.name, this.description, this.priority, this.receiver, this.size, this.entryLocker, this.destinationLocker, this.status); } } diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/task/ParcelSendTask.java b/src/main/java/com/eternalcode/parcellockers/parcel/task/ParcelSendTask.java index 737e51a2..a8f3bc92 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/task/ParcelSendTask.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/task/ParcelSendTask.java @@ -1,23 +1,46 @@ package com.eternalcode.parcellockers.parcel.task; +import com.eternalcode.parcellockers.configuration.implementation.PluginConfiguration; +import com.eternalcode.parcellockers.delivery.Delivery; +import com.eternalcode.parcellockers.delivery.repository.DeliveryRepository; +import com.eternalcode.parcellockers.parcel.Parcel; +import com.eternalcode.parcellockers.parcel.ParcelStatus; +import com.eternalcode.parcellockers.parcel.repository.ParcelRepository; import org.bukkit.scheduler.BukkitRunnable; public class ParcelSendTask extends BukkitRunnable { - // TODO: Implement ParcelSendTask + private final Parcel parcel; + private final Delivery delivery; + private final ParcelRepository parcelRepository; + private final DeliveryRepository deliveryRepository; + private final PluginConfiguration config; - /** - * ParcelSendTask constructor - * parcel - * save time into db - * assign locker - */ - - public ParcelSendTask() { + public ParcelSendTask(Parcel parcel, Delivery delivery, ParcelRepository parcelRepository, DeliveryRepository deliveryRepository, PluginConfiguration config) { + this.parcel = parcel; + this.delivery = delivery; + this.parcelRepository = parcelRepository; + this.deliveryRepository = deliveryRepository; + this.config = config; } @Override public void run() { + Parcel updated = new Parcel( + parcel.uuid(), + parcel.sender(), + parcel.name(), + parcel.description(), + parcel.priority(), + parcel.receiver(), + parcel.size(), + parcel.entryLocker(), + parcel.destinationLocker(), + ParcelStatus.DELIVERED); + + this.parcelRepository.update(updated); + this.deliveryRepository.remove(updated.uuid()); } + } diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/util/ParcelPlaceholderUtil.java b/src/main/java/com/eternalcode/parcellockers/parcel/util/ParcelPlaceholderUtil.java index 591bca43..1f5de0a9 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/util/ParcelPlaceholderUtil.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/util/ParcelPlaceholderUtil.java @@ -13,6 +13,7 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; public class ParcelPlaceholderUtil { @@ -34,7 +35,9 @@ public static List replaceParcelPlaceholders(Parcel parcel, List .register("{PRIORITY}", parcel.priority() ? "&aYes" : "&cNo") .register("{DESCRIPTION}", parcel.description() != null ? parcel.description() : "-"); - Optional lockerOptional = lockerRepository.findByUUID(parcel.destinationLocker()).join(); + Optional lockerOptional = lockerRepository.findByUUID(parcel.destinationLocker()) + .orTimeout(3, TimeUnit.SECONDS) + .join(); if (lockerOptional.isPresent()) { Locker locker = lockerOptional.get(); diff --git a/src/main/java/com/eternalcode/parcellockers/util/DurationUtil.java b/src/main/java/com/eternalcode/parcellockers/util/DurationUtil.java deleted file mode 100644 index c2c3b36a..00000000 --- a/src/main/java/com/eternalcode/parcellockers/util/DurationUtil.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.eternalcode.parcellockers.util; - -import java.time.Duration; - -public final class DurationUtil { - - private DurationUtil() { - throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); - } - - public static String format(Duration duration) { - return format(duration, true); - } - - public static String format(Duration duration, boolean removeMillis) { - if (removeMillis) { - duration = Duration.ofSeconds(duration.toSeconds()); - } - - return duration.toString() - .substring(2) - .replaceAll("(\\d[HMS])(?!$)", "$1 ") - .toLowerCase(); - } -} diff --git a/src/test/java/com/eternalcode/parcellockers/configuration/composer/DurationComposerTest.java b/src/test/java/com/eternalcode/parcellockers/configuration/composer/DurationComposerTest.java new file mode 100644 index 00000000..6e16bb42 --- /dev/null +++ b/src/test/java/com/eternalcode/parcellockers/configuration/composer/DurationComposerTest.java @@ -0,0 +1,92 @@ +package com.eternalcode.parcellockers.configuration.composer; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import panda.std.Result; + +import java.time.Duration; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class DurationComposerTest { + + private final DurationComposer composer = new DurationComposer(); + + @Test + @DisplayName("Serialize valid ISO-8601 duration") + void testSerializeValidDuration() { + Duration duration = Duration.ofDays(1).plusHours(2).plusMinutes(30).plusSeconds(15); + Result result = composer.serialize(duration); + + assertTrue(result.isOk()); + assertEquals("1d 2h 30m 15s", result.get()); + } + + @Test + @DisplayName("Serialize invalid (null) duration") + void testSerializeNullDuration() { + Result result = composer.serialize(null); + + assertTrue(result.isErr()); + assertEquals("Duration cannot be null", result.getError().getMessage()); + } + + @Test + @DisplayName("Deserialize valid ISO-8601 format") + void testDeserializeValidIso8601Format() { + String input = "PT1H30M"; + Result result = composer.deserialize(input); + + assertTrue(result.isOk()); + assertEquals(Duration.ofHours(1).plusMinutes(30), result.get()); + } + + @Test + @DisplayName("Deserialize valid custom format") + void testDeserializeValidCustomFormat() { + String input = "1d 2h 30m 15s"; + Result result = composer.deserialize(input); + + assertTrue(result.isOk()); + assertEquals(Duration.ofDays(1).plusHours(2).plusMinutes(30).plusSeconds(15), result.get()); + } + + @Test + @DisplayName("Deserialize valid simple number format") + void testDeserializeSimpleNumber() { + String input = "3600"; + Result result = composer.deserialize(input); + + assertTrue(result.isOk()); + assertEquals(Duration.ofSeconds(3600), result.get()); + } + + @Test + @DisplayName("Deserialize invalid format") + void testDeserializeInvalidFormat() { + String input = "invalid format"; + Result result = composer.deserialize(input); + + assertTrue(result.isErr()); + assertEquals("Invalid duration format. Expected format like '1d 2h 3m 4s' or ISO-8601 duration.", result.getError().getMessage()); + } + + @Test + @DisplayName("Deserialize empty input") + void testDeserializeNullInput() { + Result result = composer.deserialize(null); + + assertTrue(result.isErr()); + assertEquals("Input cannot be null or blank", result.getError().getMessage()); + } + + @Test + @DisplayName("Deserialize blank input") + void testDeserializeBlankInput() { + Result result = composer.deserialize(" "); + + assertTrue(result.isErr()); + assertEquals("Input cannot be null or blank", result.getError().getMessage()); + } +} diff --git a/src/test/java/com/eternalcode/parcellockers/database/ParcelDatabaseServiceIntegrationTest.java b/src/test/java/com/eternalcode/parcellockers/database/ParcelDatabaseServiceIntegrationTest.java index 57fb038b..90e50c3b 100644 --- a/src/test/java/com/eternalcode/parcellockers/database/ParcelDatabaseServiceIntegrationTest.java +++ b/src/test/java/com/eternalcode/parcellockers/database/ParcelDatabaseServiceIntegrationTest.java @@ -5,6 +5,7 @@ import com.eternalcode.parcellockers.configuration.implementation.PluginConfiguration; import com.eternalcode.parcellockers.parcel.Parcel; import com.eternalcode.parcellockers.parcel.ParcelSize; +import com.eternalcode.parcellockers.parcel.ParcelStatus; import com.eternalcode.parcellockers.parcel.repository.ParcelCache; import com.eternalcode.parcellockers.parcel.repository.ParcelPageResult; import com.eternalcode.parcellockers.parcel.repository.ParcelRepository; @@ -47,7 +48,7 @@ void test() { UUID destinationLocker = UUID.randomUUID(); parcelRepository.save(new Parcel(uuid, sender, "name", "description", true, receiver, - ParcelSize.SMALL, entryLocker, destinationLocker)); + ParcelSize.SMALL, entryLocker, destinationLocker, ParcelStatus.PENDING)); Optional parcel = this.await(parcelRepository.findByUUID(uuid)); assertTrue(parcel.isPresent());