diff --git a/build.gradle.kts b/build.gradle.kts index 6013616a..261d76f1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { implementation(libs.com.alpsbte.alpslib.alpslib.utils) implementation(libs.org.mariadb.jdbc.mariadb.java.client) implementation(libs.com.zaxxer.hikaricp) + implementation(libs.com.github.querz.nbt) compileOnly(libs.io.papermc.paper.paper.api) implementation(platform(libs.com.intellectualsites.bom.bom.newest)) compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Core") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0af5093b..7b8d7fb4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,6 +18,7 @@ io-papermc-paper-paper-api = "1.21.8-R0.1-SNAPSHOT" li-cinnazeyy-langlibs-api = "1.5.1" org-mariadb-jdbc-mariadb-java-client = "2.7.4" com-intellectualsites-bom-bom-newest = "1.55" # Ref: https://github.com/IntellectualSites/bom +com-github-querz-nbt-newest = "6.1" [libraries] com-alpsbte-alpslib-alpslib-hologram = { module = "com.alpsbte.alpslib:alpslib-hologram", version.ref = "com-alpsbte-alpslib-alpslib-hologram" } @@ -27,6 +28,7 @@ com-alpsbte-canvas = { module = "com.alpsbte:canvas", version.ref = "com-alpsbte com-arcaniax-headdatabase-api = { module = "com.arcaniax:HeadDatabase-API", version.ref = "com-arcaniax-headdatabase-api" } com-github-decentsoftware-eu-decentholograms = { module = "com.github.decentsoftware-eu:decentholograms", version.ref = "com-github-decentsoftware-eu-decentholograms" } com-github-fierioziy-particlenativeapi-particlenativeapi-plugin = { module = "com.github.fierioziy.particlenativeapi:ParticleNativeAPI-plugin", version.ref = "com-github-fierioziy-particlenativeapi-particlenativeapi-plugin" } +com-github-querz-nbt = { module = "com.github.Querz:NBT", version.ref = "com-github-querz-nbt-newest" } multiverse-core = { module = "org.mvplugins.multiverse.core:multiverse-core", version.ref = "multiverse-core" } com-sk89q-worldguard-worldguard-bukkit = { module = "com.sk89q.worldguard:worldguard-bukkit", version.ref = "com-sk89q-worldguard-worldguard-bukkit" } com-zaxxer-hikaricp = { module = "com.zaxxer:HikariCP", version.ref = "com-zaxxer-hikaricp" } @@ -35,4 +37,4 @@ de-oliver-fancynpcs = { module = "de.oliver:FancyNpcs", version.ref = "de-oliver io-papermc-paper-paper-api = { module = "io.papermc.paper:paper-api", version.ref = "io-papermc-paper-paper-api" } li-cinnazeyy-langlibs-api = { module = "li.cinnazeyy:LangLibs-API", version.ref = "li-cinnazeyy-langlibs-api" } org-mariadb-jdbc-mariadb-java-client = { module = "org.mariadb.jdbc:mariadb-java-client", version.ref = "org-mariadb-jdbc-mariadb-java-client" } -com-intellectualsites-bom-bom-newest = { module = "com.intellectualsites.bom:bom-newest", version.ref = "com-intellectualsites-bom-bom-newest" } \ No newline at end of file +com-intellectualsites-bom-bom-newest = { module = "com.intellectualsites.bom:bom-newest", version.ref = "com-intellectualsites-bom-bom-newest" } diff --git a/pom.xml b/pom.xml index bbcc0a99..cb6a44ee 100644 --- a/pom.xml +++ b/pom.xml @@ -169,6 +169,11 @@ 2.17.0 provided + + com.github.Querz + NBT + 6.1 + diff --git a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java index 201a5912..7f2f77a3 100644 --- a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java +++ b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java @@ -12,6 +12,8 @@ import com.alpsbte.plotsystem.core.holograms.HologramRegister; import com.alpsbte.plotsystem.core.system.Builder; import com.alpsbte.plotsystem.core.system.plot.Plot; +import com.alpsbte.plotsystem.core.system.plot.PlotHandler; +import com.alpsbte.plotsystem.core.system.plot.generator.world.SkeletonWorldGenerator; import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; import com.alpsbte.plotsystem.core.system.tutorial.AbstractTutorial; import com.alpsbte.plotsystem.core.system.tutorial.BeginnerTutorial; @@ -28,6 +30,7 @@ import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.generator.ChunkGenerator; import org.bukkit.plugin.java.JavaPlugin; import org.ipvp.canvas.MenuFunctionListener; import org.jetbrains.annotations.NotNull; @@ -117,16 +120,25 @@ public void onEnable() { DecentHologramDisplay.registerPlugin(this); HologramRegister.init(); - PlotUtils.checkPlotsForLastActivity(); Utils.ChatUtils.checkForChatInputExpiry(); PlotUtils.Effects.startTimer(); + // Start task that checks for and abandons inactive plots every hour + Bukkit.getScheduler().runTaskTimerAsynchronously(PlotSystem.getPlugin(), PlotHandler::abandonInactivePlots, 0L, 20 * 60 * 60L); + // Register tutorials if (getConfig().getBoolean(ConfigPaths.TUTORIAL_ENABLE)) { AbstractTutorial.registerTutorials(Collections.singletonList(BeginnerTutorial.class)); Bukkit.getScheduler().runTaskTimerAsynchronously(FancyNpcsPlugin.get().getPlugin(), new TutorialNPCTurnTracker(), 0, 1L); } + // Generate Skeleton World + if (Bukkit.getWorld("Skeleton") == null) { + getComponentLogger().info("No skeleton world found!"); + getComponentLogger().info("Generating skeleton world..."); + new SkeletonWorldGenerator(); + } + pluginEnabled = true; getComponentLogger().info(text("Enabled Plot-System plugin.", DARK_GREEN)); getComponentLogger().info(text("------------------------------------------------------", GOLD)); @@ -196,4 +208,9 @@ public void initDatabase() throws IOException, SQLException, ClassNotFoundExcept s.execute(initScript); } } + + @Override + public ChunkGenerator getDefaultWorldGenerator(@NotNull String worldName, String id) { + return new SkeletonWorldGenerator.EmptyChunkGenerator(); + } } \ No newline at end of file diff --git a/src/main/java/com/alpsbte/plotsystem/commands/admin/CMD_DeletePlot.java b/src/main/java/com/alpsbte/plotsystem/commands/admin/CMD_DeletePlot.java index 72d6539b..45f12f17 100644 --- a/src/main/java/com/alpsbte/plotsystem/commands/admin/CMD_DeletePlot.java +++ b/src/main/java/com/alpsbte/plotsystem/commands/admin/CMD_DeletePlot.java @@ -5,6 +5,7 @@ import com.alpsbte.plotsystem.commands.BaseCommand; import com.alpsbte.plotsystem.core.database.DataProvider; import com.alpsbte.plotsystem.core.system.plot.Plot; +import com.alpsbte.plotsystem.core.system.plot.PlotHandler; import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; import com.alpsbte.plotsystem.utils.Utils; import org.bukkit.Bukkit; @@ -38,10 +39,12 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @N sender.sendMessage(Utils.ChatUtils.getInfoFormat("Deleting plot...")); Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { - if (PlotUtils.Actions.deletePlot(plot)) { - sender.sendMessage(Utils.ChatUtils.getInfoFormat("Successfully deleted plot with the ID §6#" + plotID + "§a!")); - if (getPlayer(sender) != null) getPlayer(sender).playSound(getPlayer(sender).getLocation(), Utils.SoundUtils.DONE_SOUND, 1f, 1f); - } else sender.sendMessage(Utils.ChatUtils.getAlertFormat("An unexpected error has occurred!")); + if (!PlotHandler.deletePlot(plot)) { + sender.sendMessage(Utils.ChatUtils.getAlertFormat("An unexpected error has occurred!")); + return; + } + sender.sendMessage(Utils.ChatUtils.getInfoFormat("Successfully deleted plot with the ID §6#" + plotID + "§a!")); + if (getPlayer(sender) != null) getPlayer(sender).playSound(getPlayer(sender).getLocation(), Utils.SoundUtils.DONE_SOUND, 1f, 1f); }); }); return true; diff --git a/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_Abandon.java b/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_Abandon.java index 7dd10f9a..c06a6b09 100644 --- a/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_Abandon.java +++ b/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_Abandon.java @@ -8,6 +8,7 @@ import com.alpsbte.plotsystem.core.system.Builder; import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; import com.alpsbte.plotsystem.core.system.plot.Plot; +import com.alpsbte.plotsystem.core.system.plot.PlotHandler; import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; import com.alpsbte.plotsystem.utils.Utils; import com.alpsbte.plotsystem.utils.enums.Status; @@ -67,10 +68,9 @@ public void onCommand(CommandSender sender, String[] args) { } Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { - if (PlotUtils.Actions.abandonPlot(plot)) { - sender.sendMessage(Utils.ChatUtils.getInfoFormat(langUtil.get(sender, LangPaths.Message.Info.ABANDONED_PLOT, plot.getId() + ""))); - player.playSound(player.getLocation(), Utils.SoundUtils.ABANDON_PLOT_SOUND, 1, 1); - } + if (!PlotHandler.abandonPlot(plot)) return; + sender.sendMessage(Utils.ChatUtils.getInfoFormat(langUtil.get(sender, LangPaths.Message.Info.ABANDONED_PLOT, plot.getId() + ""))); + player.playSound(player.getLocation(), Utils.SoundUtils.ABANDON_PLOT_SOUND, 1, 1); }); }); } diff --git a/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_Submit.java b/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_Submit.java index dd24a910..605e54e0 100644 --- a/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_Submit.java +++ b/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_Submit.java @@ -8,6 +8,7 @@ import com.alpsbte.plotsystem.core.system.Builder; import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; import com.alpsbte.plotsystem.core.system.plot.Plot; +import com.alpsbte.plotsystem.core.system.plot.PlotHandler; import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; import com.alpsbte.plotsystem.utils.Utils; import com.alpsbte.plotsystem.utils.enums.Status; @@ -75,7 +76,7 @@ public void onCommand(CommandSender sender, String[] args) { Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { - PlotUtils.Actions.submitPlot(plot); + PlotHandler.submitPlot(plot); if (plotMembers.isEmpty()) { // Plot was made alone langUtil.broadcast(LangPaths.Message.Info.FINISHED_PLOT, String.valueOf(plot.getId()), plot.getPlotOwner().getName()); diff --git a/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_Teleport.java b/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_Teleport.java index b4c9033b..741f751c 100644 --- a/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_Teleport.java +++ b/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_Teleport.java @@ -9,7 +9,7 @@ import com.alpsbte.plotsystem.core.system.Builder; import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; import com.alpsbte.plotsystem.core.system.plot.Plot; -import com.alpsbte.plotsystem.core.system.plot.generator.DefaultPlotGenerator; +import com.alpsbte.plotsystem.core.system.plot.PlotHandler; import com.alpsbte.plotsystem.utils.Utils; import com.alpsbte.plotsystem.utils.enums.Status; import com.alpsbte.plotsystem.utils.io.LangPaths; @@ -63,7 +63,7 @@ public void onCommand(CommandSender sender, String[] args) { return; } - Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> new DefaultPlotGenerator(plot, builder)); + PlotHandler.assignAndGeneratePlot(builder, plot); return; } diff --git a/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_UndoSubmit.java b/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_UndoSubmit.java index a9b89bb9..a14013fc 100644 --- a/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_UndoSubmit.java +++ b/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_UndoSubmit.java @@ -8,6 +8,7 @@ import com.alpsbte.plotsystem.core.system.Builder; import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; import com.alpsbte.plotsystem.core.system.plot.Plot; +import com.alpsbte.plotsystem.core.system.plot.PlotHandler; import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; import com.alpsbte.plotsystem.utils.Utils; import com.alpsbte.plotsystem.utils.enums.Status; @@ -69,7 +70,7 @@ public void onCommand(CommandSender sender, String[] args) { } Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { - PlotUtils.Actions.undoSubmit(plot); + PlotHandler.undoSubmit(plot); sender.sendMessage(Utils.ChatUtils.getInfoFormat(langUtil.get(sender, LangPaths.Message.Info.UNDID_SUBMISSION, plot.getId() + ""))); player.playSound(player.getLocation(), Utils.SoundUtils.FINISH_PLOT_SOUND, 1, 1); diff --git a/src/main/java/com/alpsbte/plotsystem/core/EventListener.java b/src/main/java/com/alpsbte/plotsystem/core/EventListener.java index 0d38d00c..a4949e63 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/EventListener.java +++ b/src/main/java/com/alpsbte/plotsystem/core/EventListener.java @@ -7,8 +7,8 @@ import com.alpsbte.plotsystem.core.system.Builder; import com.alpsbte.plotsystem.core.system.CityProject; import com.alpsbte.plotsystem.core.system.plot.Plot; +import com.alpsbte.plotsystem.core.system.plot.PlotHandler; import com.alpsbte.plotsystem.core.system.plot.TutorialPlot; -import com.alpsbte.plotsystem.core.system.plot.generator.DefaultPlotGenerator; import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; import com.alpsbte.plotsystem.core.system.plot.world.PlotWorld; import com.alpsbte.plotsystem.core.system.review.ReviewNotification; @@ -123,7 +123,7 @@ public void onPlayerInteractAtEntity(@NotNull PlayerInteractAtEntityEvent event) public void onPlayerQuitEvent(@NotNull PlayerQuitEvent event) { final World w = event.getPlayer().getWorld(); - DefaultPlotGenerator.playerPlotGenerationHistory.remove(event.getPlayer().getUniqueId()); + PlotHandler.removePlayerFromGenerationHistory(event.getPlayer().getUniqueId()); ChatInput.awaitChatInput.remove(event.getPlayer().getUniqueId()); PlotUtils.Cache.clearCache(event.getPlayer().getUniqueId()); @@ -170,7 +170,7 @@ public void onInventoryClickEvent(@NotNull InventoryClickEvent event) { } @EventHandler - public void onlPlayerItemDropEvent(@NotNull PlayerDropItemEvent event) { + public void onPlayerItemDropEvent(@NotNull PlayerDropItemEvent event) { if (event.getItemDrop().getItemStack().equals(CompanionMenu.getMenuItem(event.getPlayer())) || event.getItemDrop().getItemStack().equals(ReviewMenu.getMenuItem(event.getPlayer()))) { event.setCancelled(true); diff --git a/src/main/java/com/alpsbte/plotsystem/core/menus/companion/CityProjectMenu.java b/src/main/java/com/alpsbte/plotsystem/core/menus/companion/CityProjectMenu.java index e9ab62ab..36e4894f 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/menus/companion/CityProjectMenu.java +++ b/src/main/java/com/alpsbte/plotsystem/core/menus/companion/CityProjectMenu.java @@ -8,7 +8,7 @@ import com.alpsbte.plotsystem.core.system.CityProject; import com.alpsbte.plotsystem.core.system.Country; import com.alpsbte.plotsystem.core.system.plot.Plot; -import com.alpsbte.plotsystem.core.system.plot.generator.DefaultPlotGenerator; +import com.alpsbte.plotsystem.core.system.plot.PlotHandler; import com.alpsbte.plotsystem.utils.Utils; import com.alpsbte.plotsystem.utils.enums.PlotDifficulty; import com.alpsbte.plotsystem.utils.enums.Status; @@ -16,6 +16,9 @@ import com.alpsbte.plotsystem.utils.io.LangPaths; import com.alpsbte.plotsystem.utils.io.LangUtil; import com.alpsbte.plotsystem.utils.items.MenuItems; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.entity.Player; import org.ipvp.canvas.mask.BinaryMask; import org.ipvp.canvas.mask.Mask; @@ -24,6 +27,7 @@ import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class CityProjectMenu extends AbstractPaginatedMenu { @@ -96,6 +100,28 @@ protected void setItemClickEventsAsync() { CompanionMenu.clickEventTutorialItem(getMenu()); } + public void handleCityProjectClick(Player player, CityProject city) { + Builder builder = Builder.byUUID(player.getUniqueId()); + + PlotDifficulty plotDifficultyForCity; + try { + plotDifficultyForCity = selectedPlotDifficulty != null ? selectedPlotDifficulty : Plot.getPlotDifficultyForBuilder(city, builder).get(); + } catch (ExecutionException | InterruptedException ex) { + sqlError(player, ex); + return; + } + + List unclaimedPlots = DataProvider.PLOT.getPlots(city, plotDifficultyForCity, Status.unclaimed); + if (unclaimedPlots.isEmpty()) { + player.sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(player, LangPaths.Message.Error.NO_PLOTS_LEFT))); + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> player.playSound(player.getLocation(), Utils.SoundUtils.ERROR_SOUND, 1, 1)); + return; + } + + Plot plot = unclaimedPlots.get(Utils.getRandom().nextInt(unclaimedPlots.size())); + PlotHandler.assignAndGeneratePlot(builder, plot); + } + public static boolean generateRandomPlot(Player player, @NotNull List items, PlotDifficulty selectedPlotDifficulty) { PlotDifficulty difficulty = selectedPlotDifficulty; if (items.isEmpty()) { @@ -108,14 +134,13 @@ public static boolean generateRandomPlot(Player player, @NotNull List source) { clickPlayer.playSound(clickPlayer.getLocation(), Utils.SoundUtils.ERROR_SOUND, 1, 1); return; } - clickPlayer.closeInventory(); - Builder builder = Builder.byUUID(clickPlayer.getUniqueId()); - - try { - PlotDifficulty plotDifficultyForCity = selectedPlotDifficulty != null ? selectedPlotDifficulty : Plot.getPlotDifficultyForBuilder(city, builder).get(); - List unclaimedPlots = DataProvider.PLOT.getPlots(city, plotDifficultyForCity, Status.unclaimed); - if (unclaimedPlots.isEmpty()) { - clickPlayer.sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(clickPlayer, LangPaths.Message.Error.NO_PLOTS_LEFT))); - clickPlayer.playSound(clickPlayer.getLocation(), Utils.SoundUtils.ERROR_SOUND, 1, 1); - return; - } - - if (selectedPlotDifficulty != null && PlotSystem.getPlugin().getConfig().getBoolean(ConfigPaths.ENABLE_SCORE_REQUIREMENT) && !DataProvider.DIFFICULTY.builderMeetsRequirements(builder, selectedPlotDifficulty)) { - clickPlayer.sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(clickPlayer, LangPaths.Message.Error.PLAYER_NEEDS_HIGHER_SCORE))); - clickPlayer.playSound(clickPlayer.getLocation(), Utils.SoundUtils.ERROR_SOUND, 1, 1); - return; - } - - new DefaultPlotGenerator(city, plotDifficultyForCity, builder); - } catch (ExecutionException | InterruptedException ex) { - sqlError(clickPlayer, ex); - } + + CompletableFuture.runAsync(() -> handleCityProjectClick(clickPlayer, city)); }); slot++; } diff --git a/src/main/java/com/alpsbte/plotsystem/core/menus/review/ReviewPlotTogglesMenu.java b/src/main/java/com/alpsbte/plotsystem/core/menus/review/ReviewPlotTogglesMenu.java index 91e949de..b8f886f1 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/menus/review/ReviewPlotTogglesMenu.java +++ b/src/main/java/com/alpsbte/plotsystem/core/menus/review/ReviewPlotTogglesMenu.java @@ -7,7 +7,7 @@ import com.alpsbte.plotsystem.core.menus.AbstractMenu; import com.alpsbte.plotsystem.core.system.Builder; import com.alpsbte.plotsystem.core.system.plot.Plot; -import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; +import com.alpsbte.plotsystem.core.system.plot.PlotHandler; import com.alpsbte.plotsystem.core.system.review.PlotReview; import com.alpsbte.plotsystem.core.system.review.ReviewRating; import com.alpsbte.plotsystem.core.system.review.ToggleCriteria; @@ -34,6 +34,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutionException; import static net.kyori.adventure.text.Component.text; @@ -131,7 +132,7 @@ private void submitReview() { if(!acceptPlot(review.getScore(), review.getSplitScore())) return; } else { reviewerConfirmationMessage = Utils.ChatUtils.getInfoFormat(LangUtil.getInstance().get(getMenuPlayer(), LangPaths.Message.Info.PLOT_REJECTED, Integer.toString(plot.getId()), getParticipantsString())); - PlotUtils.Actions.undoSubmit(plot); + PlotHandler.undoSubmit(plot); } Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { @@ -172,11 +173,11 @@ private boolean acceptPlot(int score, int splitScore) { getMenuPlayer().sendMessage(Utils.ChatUtils.getInfoFormat(LangUtil.getInstance().get(getMenuPlayer(), LangPaths.Message.Info.SAVING_PLOT))); Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { try { - if (!PlotUtils.savePlotAsSchematic(plot)) { + if (!PlotHandler.savePlotAsSchematic(plot)) { getMenuPlayer().sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(getMenuPlayer(), LangPaths.Message.Error.ERROR_OCCURRED))); PlotSystem.getPlugin().getComponentLogger().warn(text("Could not save finished plot schematic (ID: " + plot.getId() + ")!")); } - } catch (IOException | WorldEditException ex) { + } catch (IOException | WorldEditException | ExecutionException | InterruptedException ex) { PlotSystem.getPlugin().getComponentLogger().error(text("Could not save finished plot schematic (ID: " + plot.getId() + ")!"), ex); } }); diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/Plot.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/Plot.java index eaee4697..62766942 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/Plot.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/Plot.java @@ -6,7 +6,6 @@ import com.alpsbte.plotsystem.core.system.CityProject; import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; import com.alpsbte.plotsystem.core.system.plot.world.CityPlotWorld; -import com.alpsbte.plotsystem.core.system.plot.world.OnePlotWorld; import com.alpsbte.plotsystem.core.system.plot.world.PlotWorld; import com.alpsbte.plotsystem.core.system.review.PlotReview; import com.alpsbte.plotsystem.utils.enums.PlotDifficulty; @@ -127,14 +126,8 @@ public boolean setPlotType(PlotType type) { @SuppressWarnings("unchecked") @Override - public T getWorld() { - if (getVersion() <= 2 || getPlotType().hasOnePlotPerWorld()) { - if (onePlotWorld == null) onePlotWorld = new OnePlotWorld(this); - return (T) onePlotWorld; - } else { - if (cityPlotWorld == null) cityPlotWorld = new CityPlotWorld(this); - return (T) cityPlotWorld; - } + public PlotWorld getWorld() { + return PlotWorld.getByType(getPlotType(), this); } @Override diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java new file mode 100644 index 00000000..c0230592 --- /dev/null +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java @@ -0,0 +1,299 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2021-2025, Alps BTE + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.alpsbte.plotsystem.core.system.plot; + +import com.alpsbte.plotsystem.PlotSystem; +import com.alpsbte.plotsystem.core.database.DataProvider; +import com.alpsbte.plotsystem.core.system.Builder; +import com.alpsbte.plotsystem.core.system.CityProject; +import com.alpsbte.plotsystem.core.system.plot.generator.loader.AbstractPlotLoader; +import com.alpsbte.plotsystem.core.system.plot.generator.loader.DefaultPlotLoader; +import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; +import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; +import com.alpsbte.plotsystem.core.system.plot.world.CityPlotWorld; +import com.alpsbte.plotsystem.core.system.plot.world.OnePlotWorld; +import com.alpsbte.plotsystem.core.system.plot.world.PlotWorld; +import com.alpsbte.plotsystem.utils.Utils; +import com.alpsbte.plotsystem.utils.enums.PlotDifficulty; +import com.alpsbte.plotsystem.utils.enums.Slot; +import com.alpsbte.plotsystem.utils.enums.Status; +import com.alpsbte.plotsystem.utils.io.ConfigPaths; +import com.alpsbte.plotsystem.utils.io.ConfigUtil; +import com.alpsbte.plotsystem.utils.io.LangPaths; +import com.alpsbte.plotsystem.utils.io.LangUtil; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.bukkit.BukkitWorld; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; +import com.sk89q.worldedit.function.operation.ForwardExtentCopy; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.math.transform.AffineTransform; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Polygonal2DRegion; +import org.bukkit.Bukkit; +import org.bukkit.SoundCategory; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static net.kyori.adventure.text.Component.text; + +public class PlotHandler { + private PlotHandler() {} + + private static final Map playerPlotGenerationHistory = new HashMap<>(); + + public static boolean assignPlot(Builder builder, Plot plot) { + Player player = builder.getPlayer(); + + // Score Requirement met? + if (PlotSystem.getPlugin().getConfig().getBoolean(ConfigPaths.ENABLE_SCORE_REQUIREMENT) && !DataProvider.DIFFICULTY.builderMeetsRequirements(builder, plot.getDifficulty())) { + player.sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(player, LangPaths.Message.Error.PLAYER_NEEDS_HIGHER_SCORE))); + player.playSound(player.getLocation(), Utils.SoundUtils.ERROR_SOUND, SoundCategory.MASTER, 1, 1, 0); + return false; + } + + // Slot available? + Slot freeSlot = builder.getFreeSlot(); + if (freeSlot == null) { + player.sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(player, LangPaths.Message.Error.ALL_SLOTS_OCCUPIED))); + player.playSound(player.getLocation(), Utils.SoundUtils.ERROR_SOUND, SoundCategory.MASTER, 1, 1, 0); + return false; + } + + // Assign + if (!builder.setSlot(builder.getFreeSlot(), plot.getId())) return false; + if (!plot.setStatus(Status.unfinished)) return false; + return plot.setPlotOwner(builder); + } + + public static boolean assignAndGeneratePlot(Builder builder, Plot plot) { + PlotType type = builder.getPlotType(); + if (type.equals(PlotType.CITY_INSPIRATION_MODE) && ConfigUtil.getInstance().configs[0].getBoolean(ConfigPaths.DISABLE_CITY_INSPIRATION_MODE)) + type = PlotType.LOCAL_INSPIRATION_MODE; + + boolean successful = assignPlot(builder, plot); + if (successful) generatePlot(builder, plot, type); + + return successful; + } + + public static boolean assignAndGenerateRandomPlot(Builder builder, CityProject city, PlotDifficulty difficulty) { + Plot randomPlot = DataProvider.PLOT.getPlots(city, difficulty, Status.unclaimed) + .get(Utils.getRandom().nextInt(DataProvider.PLOT.getPlots(city, difficulty, Status.unclaimed).size())); + return assignAndGeneratePlot(builder, randomPlot); + } + + public static void generatePlot(Builder builder, Plot plot, PlotType type) { + Player player = builder.getPlayer(); + + // Cooldown + if (playerPlotGenerationHistory.containsKey(builder.getUUID())) { + if (!playerPlotGenerationHistory.get(builder.getUUID()).isBefore(LocalDateTime.now().minusSeconds(10))) { + player.sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(player, LangPaths.Message.Error.PLEASE_WAIT))); + player.playSound(player.getLocation(), Utils.SoundUtils.ERROR_SOUND, SoundCategory.MASTER, 1, 1, 0); + return; + } + playerPlotGenerationHistory.remove(builder.getUUID()); + } + playerPlotGenerationHistory.put(builder.getUUID(), LocalDateTime.now()); + + player.sendMessage(Utils.ChatUtils.getInfoFormat(LangUtil.getInstance().get(player, LangPaths.Message.Info.CREATING_PLOT))); + player.playSound(player.getLocation(), Utils.SoundUtils.CREATE_PLOT_SOUND, SoundCategory.MASTER, 1, 1, 0); + + new DefaultPlotLoader(plot, builder, type, PlotWorld.getByType(type, plot)); + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public static boolean abandonPlot(AbstractPlot plot) { + boolean successfullyAbandoned = plot.getWorld().onAbandon(); + if (!successfullyAbandoned) { + PlotSystem.getPlugin().getComponentLogger().error(text("Failed to abandon plot with the ID " + plot.getId() + "!")); + return false; + } + + CompletableFuture.runAsync(() -> { + if (plot.getPlotType() == PlotType.TUTORIAL) return; + Plot dPlot = (Plot) plot; + boolean successful; + successful = DataProvider.REVIEW.removeAllReviewsOfPlot(dPlot.getId()); + + for (Builder builder : dPlot.getPlotMembers()) { + if (!successful) break; + successful = dPlot.removePlotMember(builder); + } + + if (successful && plot.getPlotOwner() != null) { + PlotUtils.Cache.clearCache(plot.getPlotOwner().getUUID()); + successful = plot.getPlotOwner().setSlot(plot.getPlotOwner().getSlot(dPlot), -1); + } + + if (successful) { + successful = dPlot.setPlotOwner(null) + && dPlot.setLastActivity(true) + && dPlot.setStatus(Status.unclaimed) + && dPlot.setPlotType(PlotType.LOCAL_INSPIRATION_MODE); + } + + successful = successful && DataProvider.PLOT.setCompletedSchematic(plot.getId(), null); + if (!successful) PlotSystem.getPlugin().getComponentLogger().error(text("Failed to abandon plot with the ID " + plot.getId() + "!")); + }); + return true; + } + + public static boolean deletePlot(Plot plot) { + if (!abandonPlot(plot)) { + PlotSystem.getPlugin().getComponentLogger().warn(text("Failed to delete plot with the ID " + plot.getId() + "!")); + return false; + } + CompletableFuture.runAsync(() -> { + if (DataProvider.PLOT.deletePlot(plot.getId())) return; + PlotSystem.getPlugin().getComponentLogger().warn(text("Failed to delete plot with the ID " + plot.getId() + " from the database!")); + }); + return true; + } + + public static void abandonInactivePlots() { + FileConfiguration config = PlotSystem.getPlugin().getConfig(); + long inactivityIntervalDays = config.getLong(ConfigPaths.INACTIVITY_INTERVAL); + long rejectedInactivityIntervalDays = (config.getLong(ConfigPaths.REJECTED_INACTIVITY_INTERVAL) != -1) ? config.getLong(ConfigPaths.REJECTED_INACTIVITY_INTERVAL) : inactivityIntervalDays; + if (inactivityIntervalDays == -2 && rejectedInactivityIntervalDays == -2) return; + + for (Plot plot : DataProvider.PLOT.getPlots(Status.unfinished)) { + LocalDate lastActivity = plot.getLastActivity(); + long interval = plot.isRejected() ? rejectedInactivityIntervalDays : inactivityIntervalDays; + if (interval == -2 || lastActivity == null || lastActivity.plusDays(interval).isAfter(LocalDate.now())) continue; + + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { + if (!abandonPlot(plot)) { + PlotSystem.getPlugin().getComponentLogger().warn(text("An error occurred while abandoning plot #" + plot.getId() + " due to inactivity!")); + return; + } + PlotSystem.getPlugin().getComponentLogger().info(text("Abandoned plot #" + plot.getId() + " due to inactivity!")); + }); + } + } + + public static void submitPlot(@NotNull Plot plot) { + plot.setStatus(Status.unreviewed); + + if (plot.getWorld().isWorldLoaded()) { + for (Player player : plot.getWorld() instanceof OnePlotWorld ? plot.getWorld().getBukkitWorld().getPlayers() : ((CityPlotWorld) plot.getWorld()).getPlayersOnPlot(plot)) { + player.teleport(Utils.getSpawnLocation()); + } + } + + plot.getPermissions().removeBuilderPerms(plot.getPlotOwner().getUUID()).save(); + if (!plot.getPlotMembers().isEmpty()) { + for (Builder builder : plot.getPlotMembers()) { + plot.getPermissions().removeBuilderPerms(builder.getUUID()); + } + } + } + + public static void undoSubmit(@NotNull Plot plot) { + plot.setStatus(Status.unfinished); + + plot.getPermissions().addBuilderPerms(plot.getPlotOwner().getUUID()).save(); + if (!plot.getPlotMembers().isEmpty()) { + for (Builder builder : plot.getPlotMembers()) { + plot.getPermissions().addBuilderPerms(builder.getUUID()); + } + } + } + + public static void removePlayerFromGenerationHistory(UUID playerUuid) { + playerPlotGenerationHistory.remove(playerUuid); + } + + public static boolean savePlotAsSchematic(@NotNull Plot plot) throws IOException, WorldEditException, ExecutionException, InterruptedException { + if (plot.getVersion() < 4) { + PlotSystem.getPlugin().getComponentLogger().error(text("Saving schematics of legacy plots is no longer allowed!")); + return false; + } + + Clipboard clipboard; + ByteArrayInputStream inputStream = new ByteArrayInputStream(plot.getInitialSchematicBytes()); + try (ClipboardReader reader = AbstractPlot.CLIPBOARD_FORMAT.getReader(inputStream)) { + clipboard = reader.read(); + } + if (clipboard == null) return false; + + CuboidRegion cuboidRegion = PlotUtils.getPlotAsRegion(plot); + if (cuboidRegion == null) return false; + + BlockVector3 plotCenter = plot.getCenter(); + + // Get plot outline + List plotOutlines = plot.getOutline(); + + // Load finished plot region as cuboid region + if (!plot.getWorld().loadWorld()) return false; + com.sk89q.worldedit.world.World world = new BukkitWorld(plot.getWorld().getBukkitWorld()); + Polygonal2DRegion region = new Polygonal2DRegion(world, plotOutlines, cuboidRegion.getMinimumPoint().y(), cuboidRegion.getMaximumPoint().y()); + + // Copy and write finished plot clipboard to schematic + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try (Clipboard cb = new BlockArrayClipboard(region)) { + cb.setOrigin(BlockVector3.at(plotCenter.x(), cuboidRegion.getMinimumY(), (double) plotCenter.z())); + + ForwardExtentCopy forwardExtentCopy = new ForwardExtentCopy(Objects.requireNonNull(region.getWorld()), region, cb, region.getMinimumPoint()); + Operations.complete(forwardExtentCopy); + + try (ClipboardWriter writer = AbstractPlot.CLIPBOARD_FORMAT.getWriter(outputStream)) { + double initialY = clipboard.getRegion().getMinimumY(); + double offset = initialY - cuboidRegion.getMinimumY(); + writer.write(cb.transform(new AffineTransform().translate(Vector3.at(0, offset, 0)))); + } + } + + // Set Completed Schematic + boolean successful = DataProvider.PLOT.setCompletedSchematic(plot.getId(), outputStream.toByteArray()); + if (!successful) return false; + + // If plot was created in a void world, copy the result to the city world + if (plot.getPlotType() != PlotType.CITY_INSPIRATION_MODE) { + Utils.runSync(() -> { + AbstractPlotLoader.pasteSchematic(null, outputStream.toByteArray(), new CityPlotWorld(plot), false); + return null; + }).get(); + } + return true; + } +} diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/DefaultPlotGenerator.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/DefaultPlotGenerator.java deleted file mode 100644 index 0a0455e8..00000000 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/DefaultPlotGenerator.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.alpsbte.plotsystem.core.system.plot.generator; - -import com.alpsbte.plotsystem.PlotSystem; -import com.alpsbte.plotsystem.core.database.DataProvider; -import com.alpsbte.plotsystem.core.system.Builder; -import com.alpsbte.plotsystem.core.system.CityProject; -import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; -import com.alpsbte.plotsystem.core.system.plot.Plot; -import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; -import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; -import com.alpsbte.plotsystem.core.system.plot.world.PlotWorld; -import com.alpsbte.plotsystem.utils.Utils; -import com.alpsbte.plotsystem.utils.enums.PlotDifficulty; -import com.alpsbte.plotsystem.utils.enums.Status; -import com.alpsbte.plotsystem.utils.io.LangPaths; -import com.alpsbte.plotsystem.utils.io.LangUtil; -import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.function.mask.BlockTypeMask; -import com.sk89q.worldedit.function.mask.Mask; -import com.sk89q.worldedit.world.block.BlockTypes; -import org.jetbrains.annotations.NotNull; - -import java.io.IOException; -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import static net.kyori.adventure.text.Component.text; - -public class DefaultPlotGenerator extends AbstractPlotGenerator { - public static final Map playerPlotGenerationHistory = new HashMap<>(); - - public DefaultPlotGenerator(CityProject city, PlotDifficulty plotDifficulty, Builder builder) { - this(DataProvider.PLOT.getPlots(city, plotDifficulty, Status.unclaimed).get(Utils.getRandom().nextInt(DataProvider.PLOT.getPlots(city, plotDifficulty, Status.unclaimed).size())), builder); - } - - public DefaultPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder builder) { - super(plot, builder); - } - - public DefaultPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder builder, PlotType plotType) { - super(plot, builder, plotType); - } - - @Override - protected boolean init() { - if (getBuilder().getFreeSlot() != null) { - if (DefaultPlotGenerator.playerPlotGenerationHistory.containsKey(getBuilder().getUUID())) { - if (DefaultPlotGenerator.playerPlotGenerationHistory.get(getBuilder().getUUID()).isBefore(LocalDateTime.now().minusSeconds(10))) { - DefaultPlotGenerator.playerPlotGenerationHistory.remove(getBuilder().getUUID()); - } else { - getBuilder().getPlayer().sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(getBuilder().getPlayer(), LangPaths.Message.Error.PLEASE_WAIT))); - getBuilder().getPlayer().playSound(getBuilder().getPlayer().getLocation(), Utils.SoundUtils.ERROR_SOUND, 1, 1); - return false; - } - } - - DefaultPlotGenerator.playerPlotGenerationHistory.put(getBuilder().getUUID(), LocalDateTime.now()); - getBuilder().getPlayer().sendMessage(Utils.ChatUtils.getInfoFormat(LangUtil.getInstance().get(getBuilder().getPlayer(), LangPaths.Message.Info.CREATING_PLOT))); - getBuilder().getPlayer().playSound(getBuilder().getPlayer().getLocation(), Utils.SoundUtils.CREATE_PLOT_SOUND, 1, 1); - return true; - } else { - getBuilder().getPlayer().sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(getBuilder().getPlayer(), LangPaths.Message.Error.ALL_SLOTS_OCCUPIED))); - getBuilder().getPlayer().playSound(getBuilder().getPlayer().getLocation(), Utils.SoundUtils.ERROR_SOUND, 1, 1); - } - return false; - } - - @Override - protected void generateOutlines() throws IOException, WorldEditException { - if (plot instanceof Plot) { - byte[] completedSchematic = ((Plot) plot).getCompletedSchematic(); - if (completedSchematic != null) { - PlotSystem.getPlugin().getComponentLogger().info("Found completed schematic, pasting only that."); - Mask airMask = new BlockTypeMask(BukkitAdapter.adapt(world.getBukkitWorld()), BlockTypes.AIR); - pasteSchematic(airMask, completedSchematic, world, false); - } else super.generateOutlines(); - } else super.generateOutlines(); - - // If the player is playing in his own world, then additionally generate the plot in the city world - if (PlotWorld.isOnePlotWorld(world.getWorldName()) && plotVersion >= 3 && plot.getStatus() != Status.completed) { - // Generate city plot world if it doesn't exist - new AbstractPlotGenerator(plot, getBuilder(), PlotType.CITY_INSPIRATION_MODE) { - @Override - protected boolean init() { - return true; - } - - @Override - protected void createPlotProtection() {} - - @Override - protected void onComplete(boolean failed, boolean unloadWorld) { - super.onComplete(true, true); - } - - @Override - protected void onException(Throwable ex) { - PlotSystem.getPlugin().getComponentLogger().warn(text("Could not generate plot in city world " + world.getWorldName() + "!"), ex); - } - }; - } - } - - @Override - protected void onComplete(boolean failed, boolean unloadWorld) { - super.onComplete(failed, false); - if (failed) return; - - if (!getBuilder().setSlot(getBuilder().getFreeSlot(), plot.getId())) return; - if (!plot.setStatus(Status.unfinished)) return; - if (!((Plot) plot).setPlotType(plotType)) return; - if (!plot.setPlotOwner(getBuilder())) return; - - PlotUtils.Cache.clearCache(getBuilder().getUUID()); - plot.getWorld().teleportPlayer(getBuilder().getPlayer()); - LangUtil.getInstance().broadcast(LangPaths.Message.Info.CREATED_NEW_PLOT, getBuilder().getName()); - } -} diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/PlotWorldGenerator.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/PlotWorldGenerator.java deleted file mode 100644 index 6dbd2041..00000000 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/PlotWorldGenerator.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.alpsbte.plotsystem.core.system.plot.generator; - -import com.alpsbte.plotsystem.PlotSystem; -import com.alpsbte.plotsystem.utils.DependencyManager; -import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldguard.WorldGuard; -import com.sk89q.worldguard.protection.flags.Flags; -import com.sk89q.worldguard.protection.flags.RegionGroup; -import com.sk89q.worldguard.protection.flags.StateFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; -import com.sk89q.worldguard.protection.managers.storage.StorageException; -import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; -import com.sk89q.worldguard.protection.regions.RegionContainer; -import org.bukkit.Bukkit; -import org.bukkit.Difficulty; -import org.bukkit.GameMode; -import org.bukkit.GameRule; -import org.bukkit.World; -import org.bukkit.WorldCreator; -import org.bukkit.WorldType; -import org.bukkit.entity.SpawnCategory; -import org.bukkit.generator.ChunkGenerator; -import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; -import org.mvplugins.multiverse.core.world.WorldManager; -import org.mvplugins.multiverse.core.world.options.ImportWorldOptions; -import org.mvplugins.multiverse.external.vavr.control.Option; - -import javax.annotation.Nonnull; -import java.util.Objects; -import java.util.Random; - -import static net.kyori.adventure.text.Component.text; - -public class PlotWorldGenerator { - private final WorldManager worldManager = DependencyManager.getMultiverseCore().getWorldManager(); - private WorldCreator worldCreator; - - private final String worldName; - private static final World.Environment environment = World.Environment.NORMAL; - private static final WorldType worldType = WorldType.FLAT; - private static final String generatorSettings = "{\"features\": false,\"layers\": [{\"block\": \"air\", \"height\": 1}],\"biome\":\"plains\"}"; - - public PlotWorldGenerator(String worldName) throws Exception { - this.worldName = worldName; - generateWorld(); - createMultiverseWorld(); - configureWorld(); - createGlobalProtection(); - } - - protected void generateWorld() { - worldCreator = new WorldCreator(worldName); - worldCreator.environment(environment); - worldCreator.type(worldType); - worldCreator.generator(new EmptyChunkGenerator()); - worldCreator.generatorSettings(generatorSettings); - worldCreator.createWorld(); - } - - protected void createMultiverseWorld() throws Exception { - // Check if world creator is configured and add new world to multiverse world manager - if (worldCreator != null) { - if (!worldManager.isLoadedWorld(worldName)) { - worldManager.importWorld(ImportWorldOptions.worldName(worldName) - .environment(environment) - .generator("VoidGen:{\"caves\":false,\"decoration\":false,\"mobs\":false,\"structures\":false}") - .useSpawnAdjust(false) - ); - } - } else { - throw new Exception("World Creator is not configured"); - } - } - - protected void configureWorld() { - World bukkitWorld = Bukkit.getWorld(worldName); - Option mvWorld = worldManager.getLoadedWorld(worldName); - - if (mvWorld.isEmpty()) { - PlotSystem.getPlugin().getComponentLogger().warn(text("Multiverse world" + worldName + " is not loaded! Skipping world configuration...")); - return; - } - - // Set world time to midday - assert bukkitWorld != null; - bukkitWorld.setTime(6000); - - // Set Bukkit world game rules - bukkitWorld.setGameRule(GameRule.RANDOM_TICK_SPEED, 0); - bukkitWorld.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false); - bukkitWorld.setGameRule(GameRule.DO_FIRE_TICK, false); - bukkitWorld.setGameRule(GameRule.DO_WEATHER_CYCLE, false); - bukkitWorld.setGameRule(GameRule.KEEP_INVENTORY, true); - bukkitWorld.setGameRule(GameRule.DO_MOB_SPAWNING, false); - bukkitWorld.setGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, false); - bukkitWorld.setGameRule(GameRule.DO_TILE_DROPS, false); - bukkitWorld.setGameRule(GameRule.DO_MOB_LOOT, false); - - // Configure multiverse world - mvWorld.get().setAllowFlight(true); - mvWorld.get().setGameMode(GameMode.CREATIVE); - mvWorld.get().setAllowWeather(false); - mvWorld.get().setDifficulty(Difficulty.PEACEFUL); - mvWorld.get().getEntitySpawnConfig().getSpawnCategoryConfig(SpawnCategory.ANIMAL).setSpawn(false); - mvWorld.get().getEntitySpawnConfig().getSpawnCategoryConfig(SpawnCategory.MONSTER).setSpawn(false); - mvWorld.get().setAutoLoad(false); - mvWorld.get().setKeepSpawnInMemory(false); - worldManager.saveWorldsConfig(); - } - - protected void createGlobalProtection() throws StorageException { - RegionContainer regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer(); - RegionManager regionManager = regionContainer.get(BukkitAdapter.adapt(Objects.requireNonNull(Bukkit.getWorld(worldName)))); - - if (regionManager != null) { - // Create a protected region for the plot world - String regionName = "__global__"; - GlobalProtectedRegion globalRegion = new GlobalProtectedRegion(regionName); - globalRegion.setFlag(Flags.ENTRY, StateFlag.State.DENY); - globalRegion.setFlag(Flags.ENTRY.getRegionGroupFlag(), RegionGroup.ALL); - globalRegion.setFlag(Flags.PASSTHROUGH, StateFlag.State.DENY); - globalRegion.setFlag(Flags.PASSTHROUGH.getRegionGroupFlag(), RegionGroup.ALL); - globalRegion.setFlag(Flags.TNT, StateFlag.State.DENY); - globalRegion.setFlag(Flags.TNT.getRegionGroupFlag(), RegionGroup.ALL); - if (DependencyManager.isWorldGuardExtraFlagsEnabled()) - globalRegion.setFlag(new StateFlag("worldedit", true, RegionGroup.ALL), StateFlag.State.DENY); - - if (regionManager.hasRegion(regionName)) regionManager.removeRegion(regionName); - regionManager.addRegion(globalRegion); - regionManager.saveChanges(); - } else PlotSystem.getPlugin().getComponentLogger().warn(text("Region Manager is null!")); - } - - public static class EmptyChunkGenerator extends ChunkGenerator { - @Override - @Nonnull - public ChunkData generateChunkData(@Nonnull World world, @Nonnull Random random, int x, int z, @Nonnull BiomeGrid biome) { - return createChunkData(world); - } - } - -} - - diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/AbstractPlotGenerator.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java similarity index 56% rename from src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/AbstractPlotGenerator.java rename to src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java index 6b168e97..7b5946f8 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/AbstractPlotGenerator.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java @@ -1,13 +1,36 @@ -package com.alpsbte.plotsystem.core.system.plot.generator; +/* + * The MIT License (MIT) + * + * Copyright © 2021-2025, Alps BTE + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.alpsbte.plotsystem.core.system.plot.generator.loader; import com.alpsbte.plotsystem.PlotSystem; import com.alpsbte.plotsystem.core.system.Builder; import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; -import com.alpsbte.plotsystem.core.system.plot.Plot; +import com.alpsbte.plotsystem.core.system.plot.PlotHandler; +import com.alpsbte.plotsystem.core.system.plot.generator.world.PlotWorldGenerator; import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; -import com.alpsbte.plotsystem.core.system.plot.world.CityPlotWorld; -import com.alpsbte.plotsystem.core.system.plot.world.OnePlotWorld; import com.alpsbte.plotsystem.core.system.plot.world.PlotWorld; import com.alpsbte.plotsystem.utils.DependencyManager; import com.alpsbte.plotsystem.utils.Utils; @@ -40,7 +63,6 @@ import com.sk89q.worldguard.protection.flags.RegionGroup; import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.managers.RegionManager; -import com.sk89q.worldguard.protection.managers.storage.StorageException; import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.protection.regions.RegionContainer; @@ -56,107 +78,75 @@ import static net.kyori.adventure.text.Component.text; -public abstract class AbstractPlotGenerator { +public abstract class AbstractPlotLoader { protected final AbstractPlot plot; - private final Builder builder; - protected final PlotWorld world; - protected final double plotVersion; protected final PlotType plotType; + protected final PlotWorld plotWorld; - /** - * Generates a new plot in the plot world - * - * @param plot - plot which should be generated - * @param builder - builder of the plot - */ - protected AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder builder) { - this(plot, builder, builder.getPlotType()); - } + protected final Builder builder; - /** - * Generates a new plot in the given world - * - * @param plot - plot which should be generated - * @param builder - builder of the plot - * @param plotType - type of the plot - */ - protected AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder builder, @NotNull PlotType plotType) { - this(plot, builder, plotType, plot.getVersion() <= 2 || plotType.hasOnePlotPerWorld() ? new OnePlotWorld(plot) : new CityPlotWorld((Plot) plot)); - } + protected byte[] schematicBytes = null; - /** - * Generates a new plot in the given world - * - * @param plot - plot which should be generated - * @param builder - builder of the plot - * @param world - world of the plot - */ - private AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder builder, @NotNull PlotType plotType, @NotNull PlotWorld world) { + protected AbstractPlotLoader(@NotNull AbstractPlot plot, Builder builder, PlotType plotType, PlotWorld plotWorld) { this.plot = plot; - this.builder = builder; - this.world = world; - this.plotVersion = plot.getVersion(); this.plotType = plotType; + this.plotWorld = plotWorld; + this.builder = builder; - if (init()) { - Exception exception = null; - try { - if (plotType.hasOnePlotPerWorld() || !world.isWorldGenerated()) { - new PlotWorldGenerator(world.getWorldName()); - } else if (!world.isWorldLoaded() && !world.loadWorld()) throw new Exception("Could not load world"); - generateOutlines(); - createPlotProtection(); - } catch (Exception ex) { - exception = ex; - } - this.onComplete(exception != null, false); - - if (exception != null) { - PlotUtils.Actions.abandonPlot(plot); - onException(exception); - } + PlotSystem.getPlugin().getComponentLogger().info("Loading plot #{}...", plot.getId()); + PlotSystem.getPlugin().getComponentLogger().info("Plot Type: {}", plot.getPlotType().name()); + + boolean successful = true; + try { + generateWorld(); + loadWorld(); + fetchSchematicData(); + createPlotProtection(); + generateStructure(); + } catch (Exception e) { + successful = false; + onException(e); } + + if (successful) onCompletion(); } + protected void generateWorld() throws Exception { + boolean generateWorld = plotType.hasOnePlotPerWorld() || !Utils.supplySync(plotWorld::isWorldGenerated).get(); + if (!generateWorld) return; - /** - * Executed before plot generation - * - * @return true if initialization was successful - */ - protected abstract boolean init(); + new PlotWorldGenerator(plotWorld.getWorldName()); + } + protected void loadWorld() throws Exception { + Utils.runSync(() -> { + if (plotWorld.isWorldLoaded()) return null; - /** - * Generates plot schematic and outlines - */ - protected void generateOutlines() throws IOException { - if (plotVersion >= 3 && plotType.hasEnvironment()) { - pasteSchematic(null, plot.getInitialSchematicBytes(), world, false); - } else { - Mask airMask = new BlockTypeMask(BukkitAdapter.adapt(world.getBukkitWorld()), BlockTypes.AIR); - pasteSchematic(airMask, PlotUtils.getOutlinesSchematicBytes(plot, world.getBukkitWorld()), world, true); - } + boolean successful = plotWorld.loadWorld(); + if (!successful) throw new Exception("Could not load world!"); + return null; + }).get(); } + protected void fetchSchematicData() { + this.schematicBytes = plot.getInitialSchematicBytes(); + } - /** - * Creates plot protection - */ - protected void createPlotProtection() throws StorageException { - RegionContainer regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer(); - RegionManager regionManager = regionContainer.get(BukkitAdapter.adapt(world.getBukkitWorld())); + protected void createPlotProtection() throws Exception { + Utils.runSync(() -> { + RegionContainer regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer(); + RegionManager regionManager = regionContainer.get(BukkitAdapter.adapt(plotWorld.getBukkitWorld())); + if (regionManager == null) { + PlotSystem.getPlugin().getComponentLogger().warn(text("Region Manager is null!")); + return null; + } - if (regionManager != null) { // Create build region for plot from the outline of the plot - ProtectedRegion protectedBuildRegion = new ProtectedPolygonalRegion(world.getRegionName(), plot.getOutline(), PlotWorld.MIN_WORLD_HEIGHT, PlotWorld.MAX_WORLD_HEIGHT); + ProtectedRegion protectedBuildRegion = new ProtectedPolygonalRegion(plotWorld.getRegionName(), plot.getOutline(), PlotWorld.MIN_WORLD_HEIGHT, PlotWorld.MAX_WORLD_HEIGHT); protectedBuildRegion.setPriority(100); // Create protected plot region for plot - World weWorld = new BukkitWorld(world.getBukkitWorld()); - CylinderRegion cylinderRegion = new CylinderRegion(weWorld, plot.getCenter(), Vector2.at(PlotWorld.PLOT_SIZE, PlotWorld.PLOT_SIZE), PlotWorld.MIN_WORLD_HEIGHT, PlotWorld.MAX_WORLD_HEIGHT); - ProtectedRegion protectedRegion = new ProtectedPolygonalRegion(world.getRegionName() + "-1", cylinderRegion.polygonize(-1), PlotWorld.MIN_WORLD_HEIGHT, PlotWorld.MAX_WORLD_HEIGHT); - protectedRegion.setPriority(50); + ProtectedRegion protectedRegion = getProtectedRegion(); // Add plot owner DefaultDomain owner = protectedBuildRegion.getOwners(); @@ -170,12 +160,21 @@ protected void createPlotProtection() throws StorageException { setRegionPermissions(protectedRegion); // Add regions and save changes - if (regionManager.hasRegion(world.getRegionName())) regionManager.removeRegion(world.getRegionName()); - if (regionManager.hasRegion(world.getRegionName() + "-1")) regionManager.removeRegion(world.getRegionName() + "-1"); + if (regionManager.hasRegion(plotWorld.getRegionName())) regionManager.removeRegion(plotWorld.getRegionName()); + if (regionManager.hasRegion(plotWorld.getRegionName() + "-1")) regionManager.removeRegion(plotWorld.getRegionName() + "-1"); regionManager.addRegion(protectedBuildRegion); regionManager.addRegion(protectedRegion); regionManager.saveChanges(); - } else PlotSystem.getPlugin().getComponentLogger().warn(text("Region Manager is null!")); + return null; + }).get(); + } + + private @NotNull ProtectedRegion getProtectedRegion() { + World weWorld = new BukkitWorld(plotWorld.getBukkitWorld()); + CylinderRegion cylinderRegion = new CylinderRegion(weWorld, plot.getCenter(), Vector2.at(PlotWorld.PLOT_SIZE, PlotWorld.PLOT_SIZE), PlotWorld.MIN_WORLD_HEIGHT, PlotWorld.MAX_WORLD_HEIGHT); + ProtectedRegion protectedRegion = new ProtectedPolygonalRegion(plotWorld.getRegionName() + "-1", cylinderRegion.polygonize(-1), PlotWorld.MIN_WORLD_HEIGHT, PlotWorld.MAX_WORLD_HEIGHT); + protectedRegion.setPriority(50); + return protectedRegion; } /** @@ -216,56 +215,20 @@ protected List getBlockedCommands(@NotNull FileConfiguration config) { return blockedCommands; } - /** - * Gets invoked when generation is completed - * - * @param failed - true if generation has failed - * @param unloadWorld - try to unload world after generation - */ - protected void onComplete(boolean failed, boolean unloadWorld) { - // Unload plot world if it is not needed anymore - if (unloadWorld) world.unloadWorld(false); - } - - - /** - * Gets invoked when an exception has occurred - * - * @param ex - caused exception - */ - protected void onException(Throwable ex) { - PlotSystem.getPlugin().getComponentLogger().error(text("An error occurred while generating plot!"), ex); - builder.getPlayer().sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(builder.getPlayer(), LangPaths.Message.Error.ERROR_OCCURRED))); - builder.getPlayer().playSound(builder.getPlayer().getLocation(), Utils.SoundUtils.ERROR_SOUND, 1, 1); - } - - - /** - * @return - plot object - */ - public AbstractPlot getPlot() { - return plot; - } - - - /** - * @return - builder object - */ - public Builder getBuilder() { - return builder; + protected void generateStructure() throws Exception { + Utils.runSync(() -> { + if (plotType.hasEnvironment()) { + pasteSchematic(null, this.schematicBytes, this.plotWorld, false); + } else { + Mask airMask = new BlockTypeMask(BukkitAdapter.adapt(this.plotWorld.getBukkitWorld()), BlockTypes.AIR); + pasteSchematic(airMask, PlotUtils.getOutlinesSchematicBytes(plot, this.schematicBytes, this.plotWorld.getBukkitWorld()), this.plotWorld, true); + } + return null; + }).get(); } - - /** - * Pastes the schematic to the plot center in the given world - * - * @param pasteMask - sets a mask for the paste operation, can be null - * @param schematicFile - plot/environment schematic file - * @param world - world to paste in - * @param clearArea - clears the plot area with air before pasting - */ public static void pasteSchematic(@Nullable Mask pasteMask, byte[] schematicFile, @NotNull PlotWorld world, boolean clearArea) throws IOException { - // load world + // load world if not loaded already if (!world.loadWorld()) return; World weWorld = new BukkitWorld(world.getBukkitWorld()); @@ -295,4 +258,14 @@ public static void pasteSchematic(@Nullable Mask pasteMask, byte[] schematicFile Operations.complete(clipboardHolder); } } + + protected void onException(Exception e) { + PlotHandler.abandonPlot(this.plot); + + PlotSystem.getPlugin().getComponentLogger().error(text("An error occurred while generating plot!"), e); + builder.getPlayer().sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(builder.getPlayer(), LangPaths.Message.Error.ERROR_OCCURRED))); + builder.getPlayer().playSound(builder.getPlayer().getLocation(), Utils.SoundUtils.ERROR_SOUND, 1, 1); + } + + protected abstract void onCompletion(); } diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/DefaultPlotLoader.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/DefaultPlotLoader.java new file mode 100644 index 00000000..feafcc26 --- /dev/null +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/DefaultPlotLoader.java @@ -0,0 +1,95 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2021-2025, Alps BTE + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.alpsbte.plotsystem.core.system.plot.generator.loader; + +import com.alpsbte.plotsystem.PlotSystem; +import com.alpsbte.plotsystem.core.system.Builder; +import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; +import com.alpsbte.plotsystem.core.system.plot.Plot; +import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; +import com.alpsbte.plotsystem.core.system.plot.world.CityPlotWorld; +import com.alpsbte.plotsystem.core.system.plot.world.PlotWorld; +import com.alpsbte.plotsystem.utils.Utils; +import com.alpsbte.plotsystem.utils.enums.Status; +import com.alpsbte.plotsystem.utils.io.LangPaths; +import com.alpsbte.plotsystem.utils.io.LangUtil; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.function.mask.BlockTypeMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.world.block.BlockTypes; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; + +public class DefaultPlotLoader extends AbstractPlotLoader { + public DefaultPlotLoader(@NotNull AbstractPlot plot, Builder builder, PlotType plotType, PlotWorld plotWorld) { + super(plot, builder, plotType, plotWorld); + } + + public DefaultPlotLoader(@NotNull AbstractPlot plot, Builder builder) { + this(plot, builder, builder.getPlotType()); + } + + public DefaultPlotLoader(@NotNull AbstractPlot plot, Builder builder, PlotType plotType) { + this(plot, builder, plotType, PlotWorld.getByType(plotType, (Plot) plot)); + } + + @Override + protected void generateStructure() throws Exception { + if (!(plot instanceof Plot p)) { + super.generateStructure(); + return; + } + + byte[] completedSchematic = p.getCompletedSchematic(); + if (completedSchematic != null) { + Utils.runSync(() -> { + Mask airMask = new BlockTypeMask(BukkitAdapter.adapt(plotWorld.getBukkitWorld()), BlockTypes.AIR); + pasteSchematic(airMask, completedSchematic, plotWorld, true); + return null; + }).get(); + } else super.generateStructure(); + copyToCityWorld(); + } + + @Override + protected void onCompletion() { + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { + plot.getWorld().teleportPlayer(builder.getPlayer()); + LangUtil.getInstance().broadcast(LangPaths.Message.Info.CREATED_NEW_PLOT, builder.getName()); + }); + } + + protected void copyToCityWorld() throws IOException { + assert plot instanceof Plot; + if (plot.getStatus() == Status.completed) return; + if (!PlotWorld.isOnePlotWorld(plotWorld.getWorldName())) return; + + // If the player is playing in his own world, then additionally generate the plot in the city world + CityPlotWorld cityPlotWorld = new CityPlotWorld((Plot) plot); + AbstractPlotLoader.pasteSchematic(null, this.schematicBytes, cityPlotWorld, false); + } +} diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/TutorialPlotGenerator.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/TutorialPlotLoader.java similarity index 71% rename from src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/TutorialPlotGenerator.java rename to src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/TutorialPlotLoader.java index b5662f25..77eb2bee 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/TutorialPlotGenerator.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/TutorialPlotLoader.java @@ -1,12 +1,12 @@ -package com.alpsbte.plotsystem.core.system.plot.generator; +package com.alpsbte.plotsystem.core.system.plot.generator.loader; import com.alpsbte.plotsystem.PlotSystem; import com.alpsbte.plotsystem.core.system.Builder; import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; import com.alpsbte.plotsystem.core.system.plot.TutorialPlot; import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; +import com.alpsbte.plotsystem.core.system.plot.world.OnePlotWorld; import com.alpsbte.plotsystem.utils.DependencyManager; -import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldguard.WorldGuard; import com.sk89q.worldguard.protection.flags.Flags; @@ -16,27 +16,22 @@ import com.sk89q.worldguard.protection.regions.ProtectedRegion; import org.jetbrains.annotations.NotNull; -import java.io.IOException; import java.util.Objects; import static net.kyori.adventure.text.Component.text; -public class TutorialPlotGenerator extends AbstractPlotGenerator { +public class TutorialPlotLoader extends AbstractPlotLoader { private boolean buildingEnabled = false; private boolean worldEditEnabled = false; - public TutorialPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder builder) { - super(plot, builder, PlotType.TUTORIAL); + public TutorialPlotLoader(@NotNull AbstractPlot plot, Builder builder) { + super(plot, builder, PlotType.TUTORIAL, new OnePlotWorld(plot)); } - @Override - protected boolean init() { - return true; - } - - public void generateOutlines(int schematicId) throws IOException, WorldEditException { + public void generateOutlines(int schematicId) throws Exception { ((TutorialPlot) plot).setTutorialSchematic(schematicId); - generateOutlines(); + fetchSchematicData(); + generateStructure(); } @Override @@ -48,24 +43,19 @@ protected void setBuildRegionPermissions(@NotNull ProtectedRegion region) { region.setFlag(new StateFlag("worldedit", false, RegionGroup.OWNERS), isWorldEditEnabled() ? StateFlag.State.ALLOW : StateFlag.State.DENY); try { - Objects.requireNonNull(WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world.getBukkitWorld()))).save(); + Objects.requireNonNull(WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(plotWorld.getBukkitWorld()))).save(); } catch (StorageException ex) { PlotSystem.getPlugin().getComponentLogger().error(text("An error occurred while saving plot tutorial region!"), ex); } } - @Override - protected void onComplete(boolean failed, boolean unloadWorld) { - super.onComplete(failed, false); - } - public boolean isBuildingEnabled() { return buildingEnabled; } public void setBuildingEnabled(boolean buildingEnabled) { this.buildingEnabled = buildingEnabled; - setBuildRegionPermissions(world.getProtectedBuildRegion()); + setBuildRegionPermissions(plotWorld.getProtectedBuildRegion()); } public boolean isWorldEditEnabled() { @@ -74,6 +64,9 @@ public boolean isWorldEditEnabled() { public void setWorldEditEnabled(boolean worldEditEnabled) { this.worldEditEnabled = worldEditEnabled; - setBuildRegionPermissions(world.getProtectedBuildRegion()); + setBuildRegionPermissions(plotWorld.getProtectedBuildRegion()); } + + @Override + protected void onCompletion() {} } diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/PlotWorldGenerator.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/PlotWorldGenerator.java new file mode 100644 index 00000000..024253f3 --- /dev/null +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/PlotWorldGenerator.java @@ -0,0 +1,161 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2021-2025, Alps BTE + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.alpsbte.plotsystem.core.system.plot.generator.world; + +import com.alpsbte.plotsystem.PlotSystem; +import com.alpsbte.plotsystem.utils.DependencyManager; +import com.alpsbte.plotsystem.utils.Utils; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.flags.Flags; +import com.sk89q.worldguard.protection.flags.RegionGroup; +import com.sk89q.worldguard.protection.flags.StateFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.managers.storage.StorageException; +import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; +import com.sk89q.worldguard.protection.regions.RegionContainer; +import net.querz.nbt.io.NBTUtil; +import net.querz.nbt.io.NamedTag; +import net.querz.nbt.tag.CompoundTag; +import org.apache.commons.io.FileUtils; +import org.bukkit.*; +import org.bukkit.entity.SpawnCategory; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.options.ImportWorldOptions; +import org.mvplugins.multiverse.external.vavr.control.Option; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +import static net.kyori.adventure.text.Component.text; + +public class PlotWorldGenerator { + private final WorldManager worldManager = DependencyManager.getMultiverseCore().getWorldManager(); + private final String worldName; + private static final World.Environment environment = World.Environment.NORMAL; + private static final WorldType worldType = WorldType.FLAT; + + public PlotWorldGenerator(String worldName) throws Exception { + long startTime = System.nanoTime(); + this.worldName = worldName; + + // Async Part + generateWorld(); + + // Sync Part + long mainThreadStart = System.nanoTime(); + Utils.runSync(() -> { + createMultiverseWorld(); + configureWorld(); + createGlobalProtection(); + return null; + }).get(); + + PlotSystem.getPlugin().getComponentLogger().info("(PWG) Total time to generate world: {}ms", (System.nanoTime() - startTime) / 1_000_000); + PlotSystem.getPlugin().getComponentLogger().info("(PWG) Total time on main thread: {}ms", (System.nanoTime() - mainThreadStart) / 1_000_000); + } + + protected void generateWorld() throws IOException { + // copy skeleton world with correct world name + Path skeletonPath = Bukkit.getWorldContainer().toPath().resolve("Skeleton"); + Path worldPath = Bukkit.getWorldContainer().toPath().resolve(worldName); + FileUtils.copyDirectory(skeletonPath.toFile(), worldPath.toFile()); + + // delete uid.dat + Files.delete(worldPath.resolve("uid.dat")); + + // rename world name in level.dat + Path levelDat = worldPath.resolve("level.dat"); + NamedTag level = NBTUtil.read(levelDat.toFile()); + CompoundTag tag = (CompoundTag) level.getTag(); + tag.remove("LevelName"); + tag.putString("LevelName", worldName); + level.setTag(tag); + NBTUtil.write(level, levelDat.toFile()); + + // rename world in paper-world.yml + Path paperWorld = worldPath.resolve("paper-world.yml"); + String paperWorldContents = Files.readString(paperWorld); + String updatedContents = paperWorldContents + .replaceAll(SkeletonWorldGenerator.WORLD_NAME, worldName) + .replaceAll(SkeletonWorldGenerator.WORLD_NAME.toLowerCase(), worldName.toLowerCase()); + Files.writeString(paperWorld, updatedContents); + } + + protected void createMultiverseWorld() { + if (!worldManager.isLoadedWorld(worldName)) { + worldManager.importWorld(ImportWorldOptions.worldName(worldName) + .environment(environment) + .generator("VoidGen:{\"caves\":false,\"decoration\":false,\"mobs\":false,\"structures\":false}") + .useSpawnAdjust(false) + ); + } + } + + protected void configureWorld() { + World world = Bukkit.getWorld(worldName); + assert world != null; + Option mvWorld = worldManager.getLoadedWorld(worldName); + + // Configure multiverse world + mvWorld.get().setAllowFlight(true); + mvWorld.get().setGameMode(GameMode.CREATIVE); + mvWorld.get().setAllowWeather(false); + mvWorld.get().setDifficulty(Difficulty.PEACEFUL); + mvWorld.get().getEntitySpawnConfig().getSpawnCategoryConfig(SpawnCategory.ANIMAL).setSpawn(false); + mvWorld.get().getEntitySpawnConfig().getSpawnCategoryConfig(SpawnCategory.MONSTER).setSpawn(false); + mvWorld.get().setAutoLoad(false); + mvWorld.get().setKeepSpawnInMemory(false); + worldManager.saveWorldsConfig(); + } + + protected void createGlobalProtection() throws StorageException { + RegionContainer regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer(); + RegionManager regionManager = regionContainer.get(BukkitAdapter.adapt(Objects.requireNonNull(Bukkit.getWorld(worldName)))); + + if (regionManager != null) { + // Create a protected region for the plot world + String regionName = "__global__"; + GlobalProtectedRegion globalRegion = new GlobalProtectedRegion(regionName); + globalRegion.setFlag(Flags.ENTRY, StateFlag.State.DENY); + globalRegion.setFlag(Flags.ENTRY.getRegionGroupFlag(), RegionGroup.ALL); + globalRegion.setFlag(Flags.PASSTHROUGH, StateFlag.State.DENY); + globalRegion.setFlag(Flags.PASSTHROUGH.getRegionGroupFlag(), RegionGroup.ALL); + globalRegion.setFlag(Flags.TNT, StateFlag.State.DENY); + globalRegion.setFlag(Flags.TNT.getRegionGroupFlag(), RegionGroup.ALL); + if (DependencyManager.isWorldGuardExtraFlagsEnabled()) + globalRegion.setFlag(new StateFlag("worldedit", true, RegionGroup.ALL), StateFlag.State.DENY); + + if (regionManager.hasRegion(regionName)) regionManager.removeRegion(regionName); + regionManager.addRegion(globalRegion); + regionManager.saveChanges(); + } else PlotSystem.getPlugin().getComponentLogger().warn(text("Region Manager is null!")); + } +} + + diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/SkeletonWorldGenerator.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/SkeletonWorldGenerator.java new file mode 100644 index 00000000..acf97e31 --- /dev/null +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/SkeletonWorldGenerator.java @@ -0,0 +1,85 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2021-2025, Alps BTE + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.alpsbte.plotsystem.core.system.plot.generator.world; + +import net.kyori.adventure.util.TriState; +import org.bukkit.GameRule; +import org.bukkit.World; +import org.bukkit.WorldCreator; +import org.bukkit.WorldType; +import org.bukkit.generator.ChunkGenerator; + +public class SkeletonWorldGenerator { + public static final String WORLD_NAME = "Skeleton"; + private static final World.Environment ENVIRONMENT = World.Environment.NORMAL; + private static final WorldType WORLD_TYPE = WorldType.FLAT; + private static final String GENERATOR_SETTINGS = "{\"features\": false,\"layers\": [{\"block\": \"air\", \"height\": 1}],\"biome\":\"plains\"}"; + + private World world; + + public SkeletonWorldGenerator() { + generateWorld(); + configureWorld(); + saveWorld(); + } + + protected void generateWorld() { + WorldCreator worldCreator = new WorldCreator(WORLD_NAME) + .environment(ENVIRONMENT) + .type(WORLD_TYPE) + .generator(new SkeletonWorldGenerator.EmptyChunkGenerator()) + .generatorSettings(GENERATOR_SETTINGS) + .keepSpawnLoaded(TriState.FALSE); + this.world = worldCreator.createWorld(); + } + + protected void configureWorld() { + World bukkitWorld = this.world; + assert bukkitWorld != null; + + // Set game rules + bukkitWorld.setGameRule(GameRule.RANDOM_TICK_SPEED, 0); + bukkitWorld.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false); + bukkitWorld.setGameRule(GameRule.DO_FIRE_TICK, false); + bukkitWorld.setGameRule(GameRule.DO_WEATHER_CYCLE, false); + bukkitWorld.setGameRule(GameRule.KEEP_INVENTORY, true); + bukkitWorld.setGameRule(GameRule.DO_MOB_SPAWNING, false); + bukkitWorld.setGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, false); + bukkitWorld.setGameRule(GameRule.DO_TILE_DROPS, false); + bukkitWorld.setGameRule(GameRule.SPAWN_CHUNK_RADIUS, 0); + + // Set time to noon + bukkitWorld.setTime(6000); + } + + protected void saveWorld() { + assert this.world != null; + this.world.save(); + } + + public static class EmptyChunkGenerator extends ChunkGenerator { + // It should just do nothing + } +} diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/utils/PlotUtils.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/utils/PlotUtils.java index d0d2b5b0..b06a1d87 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/utils/PlotUtils.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/utils/PlotUtils.java @@ -6,10 +6,6 @@ import com.alpsbte.plotsystem.core.system.CityProject; import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; import com.alpsbte.plotsystem.core.system.plot.Plot; -import com.alpsbte.plotsystem.core.system.plot.TutorialPlot; -import com.alpsbte.plotsystem.core.system.plot.generator.AbstractPlotGenerator; -import com.alpsbte.plotsystem.core.system.plot.world.CityPlotWorld; -import com.alpsbte.plotsystem.core.system.plot.world.OnePlotWorld; import com.alpsbte.plotsystem.core.system.plot.world.PlotWorld; import com.alpsbte.plotsystem.core.system.review.PlotReview; import com.alpsbte.plotsystem.core.system.review.ReviewNotification; @@ -23,9 +19,7 @@ import com.github.fierioziy.particlenativeapi.api.ParticleNativeAPI; import com.github.fierioziy.particlenativeapi.api.Particles_1_13; import com.github.fierioziy.particlenativeapi.plugin.ParticleNativePlugin; -import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; @@ -35,20 +29,15 @@ import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; -import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Polygonal2DRegion; -import com.sk89q.worldguard.WorldGuard; -import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import com.sk89q.worldguard.protection.regions.RegionContainer; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Particle; import org.bukkit.World; -import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; import org.jetbrains.annotations.NotNull; @@ -62,13 +51,10 @@ import java.net.URISyntaxException; import java.nio.file.Paths; import java.text.DecimalFormat; -import java.time.LocalDate; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -168,9 +154,10 @@ public static AbstractPlot getCurrentPlot(@NotNull Builder builder, Status... st } public static boolean isPlayerOnPlot(@NotNull AbstractPlot plot, Player player) { - if (plot.getWorld().isWorldLoaded() && plot.getWorld().getBukkitWorld().getPlayers().contains(player)) { + PlotWorld world = plot.getWorld(); + if (world.isWorldLoaded() && world.getBukkitWorld().getPlayers().contains(player)) { Location playerLoc = player.getLocation(); - ProtectedRegion protectedRegion = plot.getWorld().getProtectedRegion(); + ProtectedRegion protectedRegion = world.getProtectedRegion(); return protectedRegion == null || protectedRegion.contains(Vector3.toBlockPoint(playerLoc.getX(), playerLoc.getY(), playerLoc.getZ())); } return false; @@ -195,9 +182,9 @@ public static boolean isPlotWorld(@NotNull World world) { return DependencyManager.getMultiverseCore().getWorldManager().isLoadedWorld(world) && (PlotWorld.isOnePlotWorld(world.getName()) || PlotWorld.isCityPlotWorld(world.getName())); } - public static byte @Nullable [] getOutlinesSchematicBytes(@NotNull AbstractPlot plot, World world) throws IOException { + public static byte @Nullable [] getOutlinesSchematicBytes(@NotNull AbstractPlot plot, byte[] initialSchematic, World world) throws IOException { Clipboard clipboard; - ByteArrayInputStream inputStream = new ByteArrayInputStream(plot.getInitialSchematicBytes()); + ByteArrayInputStream inputStream = new ByteArrayInputStream(initialSchematic); try (ClipboardReader reader = AbstractPlot.CLIPBOARD_FORMAT.getReader(inputStream)) { clipboard = reader.read(); } @@ -231,58 +218,6 @@ public static boolean isPlotWorld(@NotNull World world) { return Paths.get(PlotSystem.getPlugin().getDataFolder().getAbsolutePath(), "schematics") + File.separator; } - public static boolean savePlotAsSchematic(@NotNull Plot plot) throws IOException, WorldEditException { - if (plot.getVersion() < 4) { - PlotSystem.getPlugin().getComponentLogger().error(text("Saving schematics of legacy plots is no longer allowed!")); - return false; - } - - Clipboard clipboard; - ByteArrayInputStream inputStream = new ByteArrayInputStream(plot.getInitialSchematicBytes()); - try (ClipboardReader reader = AbstractPlot.CLIPBOARD_FORMAT.getReader(inputStream)) { - clipboard = reader.read(); - } - if (clipboard == null) return false; - - CuboidRegion cuboidRegion = getPlotAsRegion(plot); - if (cuboidRegion == null) return false; - - BlockVector3 plotCenter = plot.getCenter(); - - // Get plot outline - List plotOutlines = plot.getOutline(); - - // Load finished plot region as cuboid region - if (!plot.getWorld().loadWorld()) return false; - com.sk89q.worldedit.world.World world = new BukkitWorld(plot.getWorld().getBukkitWorld()); - Polygonal2DRegion region = new Polygonal2DRegion(world, plotOutlines, cuboidRegion.getMinimumPoint().y(), cuboidRegion.getMaximumPoint().y()); - - // Copy and write finished plot clipboard to schematic - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try (Clipboard cb = new BlockArrayClipboard(region)) { - cb.setOrigin(BlockVector3.at(plotCenter.x(), cuboidRegion.getMinimumY(), (double) plotCenter.z())); - - ForwardExtentCopy forwardExtentCopy = new ForwardExtentCopy(Objects.requireNonNull(region.getWorld()), region, cb, region.getMinimumPoint()); - Operations.complete(forwardExtentCopy); - - try (ClipboardWriter writer = AbstractPlot.CLIPBOARD_FORMAT.getWriter(outputStream)) { - double initialY = clipboard.getRegion().getMinimumY(); - double offset = initialY - cuboidRegion.getMinimumY(); - writer.write(cb.transform(new AffineTransform().translate(Vector3.at(0, offset, 0)))); - } - } - - // Set Completed Schematic - boolean successful = DataProvider.PLOT.setCompletedSchematic(plot.getId(), outputStream.toByteArray()); - if (!successful) return false; - - // If plot was created in a void world, copy the result to the city world - if (plot.getPlotType() != PlotType.CITY_INSPIRATION_MODE) { - AbstractPlotGenerator.pasteSchematic(null, outputStream.toByteArray(), new CityPlotWorld(plot), false); - } - return true; - } - public static @Nullable CompletableFuture convertTerraToPlotXZ(@NotNull AbstractPlot plot, double[] terraCoords) throws IOException { // Load plot outlines schematic as clipboard Clipboard clipboard; @@ -309,39 +244,17 @@ public static boolean savePlotAsSchematic(@NotNull Plot plot) throws IOException }; // Return coordinates if they are in the schematic plot region - ProtectedRegion protectedPlotRegion = plot.getWorld().getProtectedRegion() != null - ? plot.getWorld().getProtectedRegion() - : plot.getWorld().getProtectedBuildRegion(); - if (protectedPlotRegion.contains(BlockVector3.at((int) plotCoords[0], plot.getWorld().getPlotHeightCentered(), (int) plotCoords[1]))) { + PlotWorld world = plot.getWorld(); + ProtectedRegion protectedPlotRegion = world.getProtectedRegion() != null + ? world.getProtectedRegion() + : world.getProtectedBuildRegion(); + if (protectedPlotRegion.contains(BlockVector3.at((int) plotCoords[0], world.getPlotHeightCentered(), (int) plotCoords[1]))) { return CompletableFuture.completedFuture(plotCoords); } return null; } - public static void checkPlotsForLastActivity() { - Bukkit.getScheduler().runTaskTimerAsynchronously(PlotSystem.getPlugin(), () -> { - List plots = DataProvider.PLOT.getPlots(Status.unfinished); - FileConfiguration config = PlotSystem.getPlugin().getConfig(); - long inactivityIntervalDays = config.getLong(ConfigPaths.INACTIVITY_INTERVAL); - long rejectedInactivityIntervalDays = (config.getLong(ConfigPaths.REJECTED_INACTIVITY_INTERVAL) != -1) ? config.getLong(ConfigPaths.REJECTED_INACTIVITY_INTERVAL) : inactivityIntervalDays; - if (inactivityIntervalDays == -2 && rejectedInactivityIntervalDays == -2) return; - for (Plot plot : plots) { - LocalDate lastActivity = plot.getLastActivity(); - long interval = plot.isRejected() ? rejectedInactivityIntervalDays : inactivityIntervalDays; - if (interval == -2 || lastActivity == null || lastActivity.plusDays(interval).isAfter(LocalDate.now())) continue; - - Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { - if (Actions.abandonPlot(plot)) { - PlotSystem.getPlugin().getComponentLogger().info(text("Abandoned plot #" + plot.getId() + " due to inactivity!")); - } else { - PlotSystem.getPlugin().getComponentLogger().warn(text("An error occurred while abandoning plot #" + plot.getId() + " due to inactivity!")); - } - }); - } - }, 0L, 20 * 60 * 60L); // Check every hour - } - public static void informPlayerAboutUnfinishedPlots(@NotNull Player player, Builder builder) { try { List plots = Cache.getCachedInProgressPlots(builder); @@ -361,119 +274,6 @@ public static void startUnfinishedPlotReminderTimer(Player player) { 20L * 60 * interval)); } - public static final class Actions { - private Actions() {} - - public static void submitPlot(@NotNull Plot plot) { - plot.setStatus(Status.unreviewed); - - if (plot.getWorld().isWorldLoaded()) { - for (Player player : plot.getWorld() instanceof OnePlotWorld ? plot.getWorld().getBukkitWorld().getPlayers() : ((CityPlotWorld) plot.getWorld()).getPlayersOnPlot()) { - player.teleport(Utils.getSpawnLocation()); - } - } - - plot.getPermissions().removeBuilderPerms(plot.getPlotOwner().getUUID()).save(); - if (!plot.getPlotMembers().isEmpty()) { - for (Builder builder : plot.getPlotMembers()) { - plot.getPermissions().removeBuilderPerms(builder.getUUID()); - } - } - } - - public static void undoSubmit(@NotNull Plot plot) { - plot.setStatus(Status.unfinished); - - plot.getPermissions().addBuilderPerms(plot.getPlotOwner().getUUID()).save(); - if (!plot.getPlotMembers().isEmpty()) { - for (Builder builder : plot.getPlotMembers()) { - plot.getPermissions().addBuilderPerms(builder.getUUID()); - } - } - } - - public static boolean abandonPlot(@NotNull AbstractPlot plot) { - try { - if (plot.getWorld() instanceof OnePlotWorld) { - if (plot.getWorld().isWorldGenerated()) { - if (plot.getWorld().isWorldLoaded()) { - for (Player player : plot.getWorld().getBukkitWorld().getPlayers()) { - player.teleport(Utils.getSpawnLocation()); - } - } - if (!plot.getWorld().deleteWorld()) PlotSystem.getPlugin().getComponentLogger().warn(text("Could not delete plot world " + plot.getWorld().getWorldName() + "!")); - } - } else if (!(plot instanceof TutorialPlot)) { - RegionContainer regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer(); - - if (plot.getWorld().loadWorld()) { - CityPlotWorld world = plot.getWorld(); - List playersToTeleport = new ArrayList<>(world.getPlayersOnPlot()); - - RegionManager regionManager = regionContainer.get(BukkitAdapter.adapt(world.getBukkitWorld())); - if (regionManager != null) { - for (Builder builder : ((Plot) plot).getPlotMembers()) { - ((Plot) plot).removePlotMember(builder); - } - - if (regionManager.hasRegion(world.getRegionName())) regionManager.removeRegion(world.getRegionName()); - if (regionManager.hasRegion(world.getRegionName() + "-1")) regionManager.removeRegion(world.getRegionName() + "-1"); - - AbstractPlotGenerator.pasteSchematic(null, getOutlinesSchematicBytes(plot, world.getBukkitWorld()), world, true); - } else PlotSystem.getPlugin().getComponentLogger().warn(text("Region Manager is null!")); - - playersToTeleport.forEach(p -> p.teleport(Utils.getSpawnLocation())); - if (plot.getWorld().isWorldLoaded()) plot.getWorld().unloadWorld(false); - } - } - } catch (IOException | WorldEditException ex) { - PlotSystem.getPlugin().getComponentLogger().error(text("Failed to abandon plot with the ID " + plot.getId() + "!"), ex); - return false; - } - - CompletableFuture.runAsync(() -> { - if (plot.getPlotType() == PlotType.TUTORIAL) return; - Plot dPlot = (Plot) plot; - boolean successful; - successful = DataProvider.REVIEW.removeAllReviewsOfPlot(dPlot.getId()); - - for (Builder builder : dPlot.getPlotMembers()) { - if (!successful) break; - successful = dPlot.removePlotMember(builder); - } - - if (successful && plot.getPlotOwner() != null) { - Cache.clearCache(plot.getPlotOwner().getUUID()); - successful = plot.getPlotOwner().setSlot(plot.getPlotOwner().getSlot(dPlot), -1); - } - - if (successful) { - successful = dPlot.setPlotOwner(null) - && dPlot.setLastActivity(true) - && dPlot.setStatus(Status.unclaimed) - && dPlot.setPlotType(PlotType.LOCAL_INSPIRATION_MODE); - } - - successful = successful && DataProvider.PLOT.setCompletedSchematic(plot.getId(), null); - - if (!successful) PlotSystem.getPlugin().getComponentLogger().error(text("Failed to abandon plot with the ID " + plot.getId() + "!")); - }); - return true; - } - - public static boolean deletePlot(Plot plot) { - if (abandonPlot(plot)) { - CompletableFuture.runAsync(() -> { - if (DataProvider.PLOT.deletePlot(plot.getId())) return; - PlotSystem.getPlugin().getComponentLogger().warn(text("Failed to abandon plot with the ID " + plot.getId() + "!")); - }); - return true; - } - PlotSystem.getPlugin().getComponentLogger().warn(text("Failed to abandon plot with the ID " + plot.getId() + "!")); - return false; - } - } - public static final class Cache { private Cache() {} @@ -555,19 +355,19 @@ public static void showOutlines() { List points = plot.getBlockOutline(); - for (BlockVector2 point : points) - if (point.distanceSq(playerPos2D) < 50 * 50) { - if (!particleAPIEnabled) { - player.spawnParticle(Particle.FLAME, point.x(), player.getLocation().getY() + 1, point.z(), 1, 0.0, 0.0, 0.0, 0); - } else { - Location loc = new Location(player.getWorld(), point.x(), player.getLocation().getY() + 1, point.z()); - // create a particle packet - Object packet = particles.FLAME().packet(true, loc); - - // send this packet to player - particles.sendPacket(player, packet); - } + for (BlockVector2 point : points) { + if (point.distanceSq(playerPos2D) >= 50 * 50) continue; + if (!particleAPIEnabled) { + player.spawnParticle(Particle.FLAME, point.x(), player.getLocation().getY() + 1, point.z(), 1, 0.0, 0.0, 0.0, 0); + continue; } + Location loc = new Location(player.getWorld(), point.x(), player.getLocation().getY() + 1, point.z()); + // create a particle packet + Object packet = particles.FLAME().packet(true, loc); + + // send this packet to player + particles.sendPacket(player, packet); + } } } } diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/CityPlotWorld.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/CityPlotWorld.java index d37dcccc..d4124697 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/CityPlotWorld.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/CityPlotWorld.java @@ -1,14 +1,21 @@ package com.alpsbte.plotsystem.core.system.plot.world; +import com.alpsbte.plotsystem.PlotSystem; +import com.alpsbte.plotsystem.core.system.Builder; import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; import com.alpsbte.plotsystem.core.system.plot.Plot; +import com.alpsbte.plotsystem.core.system.plot.generator.loader.AbstractPlotLoader; import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; import com.alpsbte.plotsystem.utils.Utils; import com.alpsbte.plotsystem.utils.io.LangPaths; import com.alpsbte.plotsystem.utils.io.LangUtil; import com.google.common.annotations.Beta; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.RegionContainer; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -17,6 +24,8 @@ import java.util.ArrayList; import java.util.List; +import static net.kyori.adventure.text.Component.text; + public class CityPlotWorld extends PlotWorld { public CityPlotWorld(@NotNull Plot plot) { super("C-" + plot.getCityProject().getId(), plot); @@ -24,37 +33,35 @@ public CityPlotWorld(@NotNull Plot plot) { @Override public boolean teleportPlayer(@NotNull Player player) { - if (super.teleportPlayer(player)) { - player.playSound(player.getLocation(), Utils.SoundUtils.TELEPORT_SOUND, 1, 1); - player.setAllowFlight(true); - player.setFlying(true); + if (!super.teleportPlayer(player)) return false; - if (getPlot() != null) { - player.sendMessage(Utils.ChatUtils.getInfoFormat(LangUtil.getInstance().get(player, LangPaths.Message.Info.TELEPORTING_PLOT, String.valueOf(getPlot().getId())))); + player.playSound(player.getLocation(), Utils.SoundUtils.TELEPORT_SOUND, 1, 1); + player.setAllowFlight(true); + player.setFlying(true); - Utils.updatePlayerInventorySlots(player); - PlotUtils.ChatFormatting.sendLinkMessages(getPlot(), player); + if (plot == null) return true; - if (getPlot().getPlotOwner().getUUID().equals(player.getUniqueId())) { - getPlot().setLastActivity(false); - } - } + player.sendMessage(Utils.ChatUtils.getInfoFormat(LangUtil.getInstance().get(player, LangPaths.Message.Info.TELEPORTING_PLOT, String.valueOf(plot.getId())))); - return true; - } - return false; + Utils.updatePlayerInventorySlots(player); + PlotUtils.ChatFormatting.sendLinkMessages(plot, player); + + if (!plot.getPlotOwner().getUUID().equals(player.getUniqueId())) return true; + plot.setLastActivity(false); + + return true; } @Override public String getRegionName() { - return super.getRegionName() + "-" + getPlot().getId(); + return super.getRegionName() + "-" + plot.getId(); } @Beta @Override public int getPlotHeight() throws IOException { - return getPlot().getVersion() >= 3 ? MIN_WORLD_HEIGHT + getWorldHeight() : getPlotHeightCentered(); + return plot.getVersion() >= 3 ? MIN_WORLD_HEIGHT + getWorldHeight() : getPlotHeightCentered(); } @Beta @@ -72,12 +79,13 @@ public int getPlotHeightCentered() throws IOException { @Beta public int getWorldHeight() throws IOException { Clipboard clipboard; - ByteArrayInputStream inputStream = new ByteArrayInputStream(getPlot().getInitialSchematicBytes()); + ByteArrayInputStream inputStream = new ByteArrayInputStream(plot.getInitialSchematicBytes()); try (ClipboardReader reader = AbstractPlot.CLIPBOARD_FORMAT.getReader(inputStream)) { clipboard = reader.read(); } - if (clipboard != null) { - int plotHeight = clipboard.getMinimumPoint().y(); + if (clipboard == null) throw new IOException("A Plot's Outline schematic fails to load, cannot get clipboard."); + + int plotHeight = clipboard.getMinimumPoint().y(); // Minimum building height for a plot (this should be configurable depending on minecraft build limit) // This is in the case that a plot is created at y level 300 where the max build limit is 318, @@ -88,13 +96,11 @@ public int getWorldHeight() throws IOException { // Additional ground layer the plot use to save as schematic need to be included for plot's y-level int groundLayer = 64; - // Plots created outside of vanilla build limit or the build-able height is too small - if (plotHeight + groundLayer < MIN_WORLD_HEIGHT + groundLayer - || plotHeight + groundLayer + minBuildingHeight > MAX_WORLD_HEIGHT + groundLayer) - return 0; // throw new IOException("Plot height is out of range."); - return plotHeight; - } - throw new IOException("A Plot's Outline schematic fails to load, cannot get clipboard."); + // Plots created outside of vanilla build limit or the build-able height is too small + if (plotHeight + groundLayer < MIN_WORLD_HEIGHT + groundLayer + || plotHeight + groundLayer + minBuildingHeight > MAX_WORLD_HEIGHT + groundLayer) + return 0; // throw new IOException("Plot height is out of range."); + return plotHeight; } /** @@ -102,14 +108,48 @@ public int getWorldHeight() throws IOException { * * @return a list of players located on the plot */ - public List getPlayersOnPlot() { + public List getPlayersOnPlot(AbstractPlot plot) { List players = new ArrayList<>(); - if (getPlot() != null && getPlot().getWorld().isWorldLoaded() && !getPlot().getWorld().getBukkitWorld().getPlayers().isEmpty()) { - for (Player player : getPlot().getWorld().getBukkitWorld().getPlayers()) { - if (PlotUtils.isPlayerOnPlot(getPlot(), player)) players.add(player); - } - return players; + if (plot == null || !plot.getWorld().isWorldLoaded() || plot.getWorld().getBukkitWorld().getPlayers().isEmpty()) return players; + + for (Player player : plot.getWorld().getBukkitWorld().getPlayers()) { + if (PlotUtils.isPlayerOnPlot(plot, player)) players.add(player); } return players; } + + @Override + public boolean onAbandon() { + RegionContainer regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer(); + if (!loadWorld()) { + PlotSystem.getPlugin().getComponentLogger().warn(text("Could not load world!")); + return false; + } + + RegionManager regionManager = regionContainer.get(BukkitAdapter.adapt(getBukkitWorld())); + if (regionManager == null) { + PlotSystem.getPlugin().getComponentLogger().warn(text("Region Manager is null!")); + return false; + } + + for (Builder builder : ((Plot) plot).getPlotMembers()) { + ((Plot) plot).removePlotMember(builder); + } + + if (regionManager.hasRegion(getRegionName())) regionManager.removeRegion(getRegionName()); + if (regionManager.hasRegion(getRegionName() + "-1")) regionManager.removeRegion(getRegionName() + "-1"); + + // paste initial schematic to reset plot + try { + AbstractPlotLoader.pasteSchematic(null, PlotUtils.getOutlinesSchematicBytes(plot, plot.getInitialSchematicBytes(), getBukkitWorld()), this, true); + } catch (IOException e) { + PlotSystem.getPlugin().getComponentLogger().error(text("Could not paste schematic!"), e); + } + + List playersToTeleport = new ArrayList<>(getPlayersOnPlot(plot)); + playersToTeleport.forEach(p -> p.teleport(Utils.getSpawnLocation())); + + if (isWorldLoaded()) unloadWorld(false); + return super.onAbandon(); + } } \ No newline at end of file diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/IWorld.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/IWorld.java index bf5cf74a..ea6a1a0e 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/IWorld.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/IWorld.java @@ -1,6 +1,7 @@ package com.alpsbte.plotsystem.core.system.plot.world; -import com.alpsbte.plotsystem.core.system.plot.generator.AbstractPlotGenerator; +import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; +import com.alpsbte.plotsystem.core.system.plot.generator.loader.AbstractPlotLoader; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import org.bukkit.Location; @@ -10,6 +11,9 @@ import java.io.IOException; +/** + * Represents the world of a given plot. There can be multiple instances of this interface per actual bukkit world. + */ public interface IWorld { /** * Generates the plot world with the required configurations and schematic @@ -17,7 +21,7 @@ public interface IWorld { * @param generator generator type as class * @return true if world was generated successfully */ - boolean generateWorld(@NotNull Class generator); + boolean generateWorld(@NotNull Class generator); /** * Regenerates the current plot with an optional new generator type @@ -25,7 +29,7 @@ public interface IWorld { * @param generator generator type as class * @return true if world was regenerated successfully */ - boolean regenWorld(@NotNull Class generator); + boolean regenWorld(@NotNull Class generator); /** * Deletes the world file and entry in the config file @@ -123,4 +127,18 @@ public interface IWorld { * @return true if world is generated */ boolean isWorldGenerated(); + + /** + * Returns the plot associated with this world object + * + * @return the plot associated with this world object + */ + AbstractPlot getPlot(); + + /** + * Executes all World specific side effects needed to abandon a plot + * + * @return true if successfully executed + */ + boolean onAbandon(); } diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/OnePlotWorld.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/OnePlotWorld.java index 2f439bec..4eb4dfbb 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/OnePlotWorld.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/OnePlotWorld.java @@ -1,3 +1,27 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2023, Alps BTE + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package com.alpsbte.plotsystem.core.system.plot.world; import com.alpsbte.plotsystem.PlotSystem; @@ -5,9 +29,9 @@ import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; import com.alpsbte.plotsystem.core.system.plot.Plot; import com.alpsbte.plotsystem.core.system.plot.TutorialPlot; -import com.alpsbte.plotsystem.core.system.plot.generator.AbstractPlotGenerator; -import com.alpsbte.plotsystem.core.system.plot.generator.DefaultPlotGenerator; -import com.alpsbte.plotsystem.core.system.plot.generator.TutorialPlotGenerator; +import com.alpsbte.plotsystem.core.system.plot.generator.loader.AbstractPlotLoader; +import com.alpsbte.plotsystem.core.system.plot.generator.loader.DefaultPlotLoader; +import com.alpsbte.plotsystem.core.system.plot.generator.loader.TutorialPlotLoader; import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; import com.alpsbte.plotsystem.utils.Utils; @@ -22,47 +46,38 @@ public class OnePlotWorld extends PlotWorld { private final Builder plotOwner; + private final AbstractPlot plot; public OnePlotWorld(@NotNull AbstractPlot plot) { super((plot instanceof TutorialPlot ? "T-" : "P-") + plot.getId(), plot); + this.plot = plot; this.plotOwner = plot.getPlotOwner(); } @Override - public boolean generateWorld(@NotNull Class generator) { + public boolean generateWorld(@NotNull Class generator) { if (isWorldGenerated()) return false; - if (generator.isAssignableFrom(DefaultPlotGenerator.class)) { - new DefaultPlotGenerator(getPlot(), plotOwner); - } else if (generator.isAssignableFrom(TutorialPlotGenerator.class)) { - new TutorialPlotGenerator(getPlot(), plotOwner); + if (generator.isAssignableFrom(DefaultPlotLoader.class)) { + new DefaultPlotLoader(plot, plotOwner); + } else if (generator.isAssignableFrom(TutorialPlotLoader.class)) { + new TutorialPlotLoader(plot, plotOwner); } else return false; return true; } @Override public boolean loadWorld() { - if (getPlot() == null || isWorldGenerated()) return super.loadWorld(); + if (plot == null || isWorldGenerated()) return super.loadWorld(); // Generate plot if it doesn't exist - if (getPlot().getPlotType() == PlotType.TUTORIAL || ((Plot) getPlot()).getCompletedSchematic() == null) - generateWorld(TutorialPlotGenerator.class); - - new DefaultPlotGenerator(getPlot(), plotOwner, getPlot().getPlotType()) { - @Override - protected boolean init() { - return true; - } - - @Override - protected void onComplete(boolean failed, boolean unloadWorld) { - getPlot().getPermissions().clearAllPerms(); - super.onComplete(true, false); - } - }; + if (plot.getPlotType() == PlotType.TUTORIAL || ((Plot) plot).getCompletedSchematic() == null) + generateWorld(TutorialPlotLoader.class); + + new DefaultPlotLoader(plot, plotOwner, plot.getPlotType(), this); if (!isWorldGenerated() || !isWorldLoaded()) { - PlotSystem.getPlugin().getComponentLogger().warn(text("Could not regenerate world " + getWorldName() + " for plot " + getPlot().getId() + "!")); + PlotSystem.getPlugin().getComponentLogger().warn(text("Could not regenerate world " + getWorldName() + " for plot " + plot.getId() + "!")); return false; } return true; @@ -71,13 +86,10 @@ protected void onComplete(boolean failed, boolean unloadWorld) { @Override public boolean unloadWorld(boolean movePlayers) { boolean isTutorialPlot; - isTutorialPlot = getPlot().getPlotType() == PlotType.TUTORIAL; + isTutorialPlot = plot.getPlotType() == PlotType.TUTORIAL; - if (getPlot() != null) { - if (isTutorialPlot) return deleteWorld(); - else return super.unloadWorld(movePlayers); - } - return false; + if (isTutorialPlot) return deleteWorld(); + else return super.unloadWorld(movePlayers); } @Override @@ -88,15 +100,15 @@ public boolean teleportPlayer(@NotNull Player player) { player.setAllowFlight(true); player.setFlying(true); - if (getPlot() == null) return true; - if (getPlot().getPlotType() != PlotType.TUTORIAL) { - player.sendMessage(Utils.ChatUtils.getInfoFormat(LangUtil.getInstance().get(player, LangPaths.Message.Info.TELEPORTING_PLOT, String.valueOf(getPlot().getId())))); - PlotUtils.ChatFormatting.sendLinkMessages(getPlot(), player); + if (plot == null) return true; + if (plot.getPlotType() != PlotType.TUTORIAL) { + player.sendMessage(Utils.ChatUtils.getInfoFormat(LangUtil.getInstance().get(player, LangPaths.Message.Info.TELEPORTING_PLOT, String.valueOf(plot.getId())))); + PlotUtils.ChatFormatting.sendLinkMessages(plot, player); } Utils.updatePlayerInventorySlots(player); - if (!getPlot().getPlotOwner().getUUID().equals(player.getUniqueId())) return true; - getPlot().setLastActivity(false); + if (!plot.getPlotOwner().getUUID().equals(player.getUniqueId())) return true; + plot.setLastActivity(false); return true; } @@ -110,4 +122,17 @@ public int getPlotHeight() { public int getPlotHeightCentered() throws IOException { return MIN_WORLD_HEIGHT + super.getPlotHeightCentered(); } + + @Override + public boolean onAbandon() { + if (!isWorldGenerated()) return super.onAbandon(); + if (isWorldLoaded()) { + for (Player player : getBukkitWorld().getPlayers()) player.teleport(Utils.getSpawnLocation()); + } + if (!deleteWorld()) { + PlotSystem.getPlugin().getComponentLogger().warn(text("Could not delete plot world " + getWorldName() + "!")); + return false; + } + return super.onAbandon(); + } } diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/PlotWorld.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/PlotWorld.java index 87375756..5eb2c7cd 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/PlotWorld.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/world/PlotWorld.java @@ -1,13 +1,40 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2021-2025, Alps BTE + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package com.alpsbte.plotsystem.core.system.plot.world; import com.alpsbte.alpslib.utils.AlpsUtils; import com.alpsbte.plotsystem.PlotSystem; import com.alpsbte.plotsystem.core.database.DataProvider; import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; +import com.alpsbte.plotsystem.core.system.plot.Plot; import com.alpsbte.plotsystem.core.system.plot.TutorialPlot; -import com.alpsbte.plotsystem.core.system.plot.generator.AbstractPlotGenerator; +import com.alpsbte.plotsystem.core.system.plot.generator.loader.AbstractPlotLoader; +import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; import com.alpsbte.plotsystem.utils.DependencyManager; import com.alpsbte.plotsystem.utils.Utils; +import com.alpsbte.plotsystem.utils.io.ConfigPaths; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; @@ -40,7 +67,7 @@ public class PlotWorld implements IWorld { private final MultiverseCoreApi mvCore = DependencyManager.getMultiverseCore(); private final String worldName; - private final AbstractPlot plot; + protected final AbstractPlot plot; public PlotWorld(@NotNull String worldName, @Nullable AbstractPlot plot) { this.worldName = worldName; @@ -48,61 +75,61 @@ public PlotWorld(@NotNull String worldName, @Nullable AbstractPlot plot) { } @Override - public boolean generateWorld(@NotNull Class generator) { + public boolean generateWorld(@NotNull Class generator) { throw new UnsupportedOperationException("No world generator set for world " + getWorldName()); } @Override - public boolean regenWorld(@NotNull Class generator) { + public boolean regenWorld(@NotNull Class generator) { return deleteWorld() && generateWorld(generator); } @Override public boolean deleteWorld() { - if (isWorldGenerated() && loadWorld()) { - if (Boolean.TRUE.equals(mvCore.getWorldManager().getWorld(getWorldName()) - .map(world -> mvCore.getWorldManager().deleteWorld(DeleteWorldOptions.world(world)).isSuccess()) - .getOrElse(false)) && mvCore.getWorldManager().saveWorldsConfig().isSuccess()) { - try { - File multiverseInventoriesConfig = new File(DependencyManager.getMultiverseInventoriesConfigPath(getWorldName())); - File worldGuardConfig = new File(DependencyManager.getWorldGuardConfigPath(getWorldName())); - if (multiverseInventoriesConfig.exists()) FileUtils.deleteDirectory(multiverseInventoriesConfig); - if (worldGuardConfig.exists()) FileUtils.deleteDirectory(worldGuardConfig); - } catch (IOException ex) { - PlotSystem.getPlugin().getComponentLogger().warn(text("Could not delete config files for world " + getWorldName() + "!")); - return false; - } - return true; - } else PlotSystem.getPlugin().getComponentLogger().warn(text("Could not delete world " + getWorldName() + "!")); + if (!isWorldGenerated() || !loadWorld()) return false; + if (!Boolean.TRUE.equals(mvCore.getWorldManager().getWorld(getWorldName()) + .map(world -> mvCore.getWorldManager().deleteWorld(DeleteWorldOptions.world(world)).isSuccess()) + .getOrElse(false)) || !mvCore.getWorldManager().saveWorldsConfig().isSuccess()) { + PlotSystem.getPlugin().getComponentLogger().warn(text("Could not delete world " + getWorldName() + "!")); + return false; } - return false; + try { + File multiverseInventoriesConfig = new File(DependencyManager.getMultiverseInventoriesConfigPath(getWorldName())); + File worldGuardConfig = new File(DependencyManager.getWorldGuardConfigPath(getWorldName())); + if (multiverseInventoriesConfig.exists()) FileUtils.deleteDirectory(multiverseInventoriesConfig); + if (worldGuardConfig.exists()) FileUtils.deleteDirectory(worldGuardConfig); + } catch (IOException ex) { + PlotSystem.getPlugin().getComponentLogger().warn(text("Could not delete config files for world " + getWorldName() + "!")); + return false; + } + return true; } @Override public boolean loadWorld() { - if (isWorldGenerated()) { - if (isWorldLoaded()) { - return true; - } else return mvCore.getWorldManager().loadWorld(getWorldName()).isSuccess() || isWorldLoaded(); - } else PlotSystem.getPlugin().getComponentLogger().warn(text("Could not load world " + worldName + " because it is not generated!")); - return false; + if (!isWorldGenerated()) { + PlotSystem.getPlugin().getComponentLogger().warn(text("Could not load world " + worldName + " because it is not generated!")); + return false; + } + + return isWorldLoaded() || mvCore.getWorldManager().loadWorld(getWorldName()).isSuccess(); } @Override public boolean unloadWorld(boolean movePlayers) { - if (isWorldGenerated()) { - if (isWorldLoaded()) { - if (movePlayers && !getBukkitWorld().getPlayers().isEmpty()) { - for (Player player : getBukkitWorld().getPlayers()) { - player.teleport(Utils.getSpawnLocation()); - } - } - - return Bukkit.unloadWorld(getBukkitWorld(), true); + if (!isWorldGenerated()) { + PlotSystem.getPlugin().getComponentLogger().warn(text("Could not unload world " + worldName + " because it is not generated!")); + return false; + } + if (!isWorldLoaded()) return true; + + if (movePlayers && !getBukkitWorld().getPlayers().isEmpty()) { + for (Player player : getBukkitWorld().getPlayers()) { + player.teleport(Utils.getSpawnLocation()); } - return true; - } else PlotSystem.getPlugin().getComponentLogger().warn(text("Could not unload world " + worldName + " because it is not generated!")); - return false; + } + + return Bukkit.unloadWorld(getBukkitWorld(), true); } @Override @@ -110,25 +137,21 @@ public boolean teleportPlayer(@NotNull Player player) { if (loadWorld() && plot != null) { player.teleport(getSpawnPoint(plot instanceof TutorialPlot ? null : plot.getCenter())); return true; - } else PlotSystem.getPlugin().getComponentLogger().warn(text("Could not teleport player " + player.getName() + " to world " + worldName + "!")); + } + PlotSystem.getPlugin().getComponentLogger().warn(text("Could not teleport player " + player.getName() + " to world " + worldName + "!")); return false; } @Override public Location getSpawnPoint(BlockVector3 plotVector) { - if (isWorldGenerated() && loadWorld()) { - Location spawnLocation; - if (plotVector == null) { - spawnLocation = getBukkitWorld().getSpawnLocation(); - } else { - spawnLocation = new Location(getBukkitWorld(), plotVector.x(), plotVector.y(), plotVector.z()); - } + if (!isWorldGenerated() || !loadWorld()) return null; + Location spawnLocation = plotVector == null + ? getBukkitWorld().getSpawnLocation() + : new Location(getBukkitWorld(), plotVector.x(), plotVector.y(), plotVector.z()); - // Set spawn point 1 block above the highest block at the spawn location - spawnLocation.setY(getBukkitWorld().getHighestBlockYAt((int) spawnLocation.getX(), (int) spawnLocation.getZ()) + 1d); - return spawnLocation; - } - return null; + // Set spawn point 1 block above the highest block at the spawn location + spawnLocation.setY(getBukkitWorld().getHighestBlockYAt((int) spawnLocation.getX(), (int) spawnLocation.getZ()) + 1d); + return spawnLocation; } @Override @@ -138,17 +161,15 @@ public int getPlotHeight() throws IOException { @Override public int getPlotHeightCentered() throws IOException { - if (plot != null) { - Clipboard clipboard; - ByteArrayInputStream inputStream = new ByteArrayInputStream(plot.getInitialSchematicBytes()); - try (ClipboardReader reader = AbstractPlot.CLIPBOARD_FORMAT.getReader(inputStream)) { - clipboard = reader.read(); - } - if (clipboard != null) { - return (int) clipboard.getRegion().getCenter().y() - clipboard.getMinimumPoint().y(); - } + if (plot == null) return 0; + + Clipboard clipboard; + ByteArrayInputStream inputStream = new ByteArrayInputStream(plot.getInitialSchematicBytes()); + try (ClipboardReader reader = AbstractPlot.CLIPBOARD_FORMAT.getReader(inputStream)) { + clipboard = reader.read(); } - return 0; + if (clipboard == null) return 0; + return (int) clipboard.getRegion().getCenter().y() - clipboard.getMinimumPoint().y(); } @Override @@ -186,21 +207,26 @@ public boolean isWorldGenerated() { return mvCore.getWorldManager().getWorld(worldName).isDefined(); } - private @Nullable ProtectedRegion getRegion(String regionName) { - RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer(); - if (loadWorld()) { - RegionManager regionManager = container.get(BukkitAdapter.adapt(getBukkitWorld())); - if (regionManager != null) { - return regionManager.getRegion(regionName); - } else PlotSystem.getPlugin().getComponentLogger().warn(text("Region manager is null!")); - } - return null; - } - + @Override public AbstractPlot getPlot() { return plot; } + @Override + public boolean onAbandon() { + return true; + } + + private @Nullable ProtectedRegion getRegion(String regionName) { + if (!loadWorld()) return null; + + RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer(); + RegionManager regionManager = container.get(BukkitAdapter.adapt(getBukkitWorld())); + if (regionManager != null) return regionManager.getRegion(regionName); + + PlotSystem.getPlugin().getComponentLogger().warn(text("Region manager is null!")); + return null; + } /** * @param worldName - the name of the world @@ -226,12 +252,19 @@ public static boolean isCityPlotWorld(@NotNull String worldName) { * @return - plot world */ public static @Nullable PlotWorld getPlotWorldByName(String worldName) { - if (isOnePlotWorld(worldName) || isCityPlotWorld(worldName)) { - Integer id = AlpsUtils.tryParseInt(worldName.substring(2)); - if (id == null) return new PlotWorld(worldName, null); - AbstractPlot plot = worldName.toLowerCase().startsWith("t-") ? DataProvider.TUTORIAL_PLOT.getById(id).orElse(null) : DataProvider.PLOT.getPlotById(id); - return plot == null ? null : plot.getWorld(); - } - return null; + // TODO: rework + if (!isOnePlotWorld(worldName) && !isCityPlotWorld(worldName)) return null; + + Integer id = AlpsUtils.tryParseInt(worldName.substring(2)); + if (id == null) return new PlotWorld(worldName, null); + + AbstractPlot plot = worldName.toLowerCase().startsWith("t-") ? DataProvider.TUTORIAL_PLOT.getById(id).orElse(null) : DataProvider.PLOT.getPlotById(id); + return plot == null ? null : plot.getWorld(); + } + + public static PlotWorld getByType(PlotType type, Plot plot) { + // TODO: rework + boolean disableCIM = PlotSystem.getPlugin().getConfig().getBoolean(ConfigPaths.DISABLE_CITY_INSPIRATION_MODE); + return disableCIM || type.hasOnePlotPerWorld() ? new OnePlotWorld(plot) : new CityPlotWorld(plot); } } diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/AbstractPlotTutorial.java b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/AbstractPlotTutorial.java index c6b6fcab..9bb7d777 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/AbstractPlotTutorial.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/AbstractPlotTutorial.java @@ -6,7 +6,7 @@ import com.alpsbte.plotsystem.core.database.providers.TutorialPlotProvider; import com.alpsbte.plotsystem.core.system.Builder; import com.alpsbte.plotsystem.core.system.plot.TutorialPlot; -import com.alpsbte.plotsystem.core.system.plot.generator.TutorialPlotGenerator; +import com.alpsbte.plotsystem.core.system.plot.generator.loader.TutorialPlotLoader; import com.alpsbte.plotsystem.core.system.tutorial.stage.AbstractPlotStage; import com.alpsbte.plotsystem.core.system.tutorial.stage.AbstractStage; import com.alpsbte.plotsystem.core.system.tutorial.utils.TutorialNPC; @@ -22,7 +22,6 @@ import org.bukkit.ChatColor; import org.bukkit.entity.Player; -import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.Optional; import java.util.UUID; @@ -40,7 +39,7 @@ public abstract class AbstractPlotTutorial extends AbstractTutorial implements PlotTutorial { protected TutorialPlot tutorialPlot; - private TutorialPlotGenerator plotGenerator; + private TutorialPlotLoader plotGenerator; private boolean isPasteSchematic; protected AbstractPlotTutorial(Player player, int tutorialId, int stageId) { @@ -91,7 +90,7 @@ public void setStage(int stageId) { } @Override - public void onPlotSchematicPaste(UUID playerUUID, int schematicId) throws IOException { + public void onPlotSchematicPaste(UUID playerUUID, int schematicId) throws Exception { if (!getPlayerUUID().toString().equals(playerUUID.toString())) return; if (plotGenerator != null && tutorialPlot.getWorld().isWorldGenerated() && tutorialPlot.getWorld().isWorldLoaded()) { plotGenerator.generateOutlines(schematicId); @@ -119,7 +118,7 @@ protected void prepareStage(PrepareStageAction action) { if (isPasteSchematic) { try { onPlotSchematicPaste(getPlayerUUID(), ((AbstractPlotStage) currentStage).getInitSchematicId()); - } catch (IOException ex) { + } catch (Exception ex) { onException(ex); return; } @@ -151,11 +150,11 @@ public void saveTutorial(int stageId) { @Override public void onSwitchWorld(UUID playerUUID, int tutorialWorldIndex) { if (!getPlayerUUID().toString().equals(playerUUID.toString())) return; - if (tutorialWorldIndex == 1 && (plotGenerator == null || !plotGenerator.getPlot().getWorld().isWorldGenerated())) { - plotGenerator = new TutorialPlotGenerator(tutorialPlot, Builder.byUUID(playerUUID)); + if (tutorialWorldIndex == 1 && (plotGenerator == null || !tutorialPlot.getWorld().isWorldGenerated())) { + plotGenerator = new TutorialPlotLoader(tutorialPlot, Builder.byUUID(playerUUID)); try { onPlotSchematicPaste(playerUUID, ((AbstractPlotStage) currentStage).getInitSchematicId()); - } catch (IOException ex) { + } catch (Exception ex) { onException(ex); return; } diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/PlotTutorial.java b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/PlotTutorial.java index 261e52fc..43f281c4 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/PlotTutorial.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/PlotTutorial.java @@ -1,6 +1,5 @@ package com.alpsbte.plotsystem.core.system.tutorial; -import java.io.IOException; import java.util.UUID; public interface PlotTutorial extends Tutorial { @@ -11,7 +10,7 @@ public interface PlotTutorial extends Tutorial { * @param playerUUID uuid of the player * @param schematicId The schematic id */ - void onPlotSchematicPaste(UUID playerUUID, int schematicId) throws IOException; + void onPlotSchematicPaste(UUID playerUUID, int schematicId) throws Exception; /** * This method is called when the building and WorldEdit permissions on the plot need to be changed. diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/PlotSchematicPasteTask.java b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/PlotSchematicPasteTask.java index bc7e7e10..e1975aaa 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/PlotSchematicPasteTask.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/PlotSchematicPasteTask.java @@ -4,8 +4,6 @@ import com.alpsbte.plotsystem.core.system.tutorial.PlotTutorial; import org.bukkit.entity.Player; -import java.io.IOException; - public class PlotSchematicPasteTask extends AbstractTask { private final int schematicId; @@ -20,7 +18,7 @@ public void performTask() { if (tutorial != null) { try { tutorial.onPlotSchematicPaste(player.getUniqueId(), schematicId); - } catch (IOException ex) { + } catch (Exception ex) { tutorial.onException(ex); return; } diff --git a/src/main/java/com/alpsbte/plotsystem/utils/Utils.java b/src/main/java/com/alpsbte/plotsystem/utils/Utils.java index d2f9dbaf..9cbd1d47 100644 --- a/src/main/java/com/alpsbte/plotsystem/utils/Utils.java +++ b/src/main/java/com/alpsbte/plotsystem/utils/Utils.java @@ -40,6 +40,9 @@ import java.util.Objects; import java.util.Random; import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import static com.alpsbte.plotsystem.core.system.tutorial.utils.TutorialUtils.TEXT_HIGHLIGHT_END; import static com.alpsbte.plotsystem.core.system.tutorial.utils.TutorialUtils.TEXT_HIGHLIGHT_START; @@ -103,6 +106,34 @@ public static ItemStack getConfiguredItem(@NotNull String material, Object custo return builder.build(); } + public static CompletableFuture runSync(Callable task) { + CompletableFuture future = new CompletableFuture<>(); + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { + try { + var result = task.call(); + future.complete(result); + } catch (Exception e) { + future.completeExceptionally(e); + } + }); + + return future; + } + + public static CompletableFuture supplySync(Callable task) { + CompletableFuture future = new CompletableFuture<>(); + Executor executor = Bukkit.getScheduler().getMainThreadExecutor(PlotSystem.getPlugin()); + executor.execute(() -> { + try { + var result = task.call(); + future.complete(result); + } catch (Exception e) { + future.completeExceptionally(e); + } + }); + return future; + } + public static class SoundUtils { private SoundUtils() {}