From 77f41e2a95ad1ea8fc14f3aff1a4c3ceaacf0d0c Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Mon, 14 Jul 2025 04:17:26 +0200 Subject: [PATCH 01/21] move world generation to SkeletonWorldGenerator and copy skeleton data in PlotWorldGenerator --- pom.xml | 5 + .../plot/generator/AbstractPlotGenerator.java | 1 + .../{ => world}/PlotWorldGenerator.java | 94 +++++++++---------- .../world/SkeletonWorldGenerator.java | 83 ++++++++++++++++ 4 files changed, 133 insertions(+), 50 deletions(-) rename src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/{ => world}/PlotWorldGenerator.java (64%) create mode 100644 src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/SkeletonWorldGenerator.java diff --git a/pom.xml b/pom.xml index 77e855ea..9fcb2ca6 100644 --- a/pom.xml +++ b/pom.xml @@ -172,6 +172,11 @@ 1.5 provided + + com.github.Querz + NBT + 6.1 + 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/AbstractPlotGenerator.java index f85c53ac..27c7b9d8 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/AbstractPlotGenerator.java @@ -28,6 +28,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.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; 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/world/PlotWorldGenerator.java similarity index 64% rename from src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/PlotWorldGenerator.java rename to src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/PlotWorldGenerator.java index 2482bb4a..f7926991 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/PlotWorldGenerator.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/PlotWorldGenerator.java @@ -22,7 +22,7 @@ * SOFTWARE. */ -package com.alpsbte.plotsystem.core.system.plot.generator; +package com.alpsbte.plotsystem.core.system.plot.generator.world; import com.alpsbte.plotsystem.PlotSystem; import com.alpsbte.plotsystem.utils.DependencyManager; @@ -37,25 +37,28 @@ 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.generator.ChunkGenerator; -import javax.annotation.Nonnull; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Objects; -import java.util.Random; import static net.kyori.adventure.text.Component.text; public class PlotWorldGenerator { private final MVWorldManager worldManager = DependencyManager.getMultiverseCore().getMVWorldManager(); - 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 { + private World world = null; + + public PlotWorldGenerator(String worldName) throws IOException, StorageException { this.worldName = worldName; generateWorld(); createMultiverseWorld(); @@ -63,44 +66,44 @@ public PlotWorldGenerator(String worldName) throws Exception { 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 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()); + + // 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); + + // load world + this.world = Bukkit.getWorld(worldName); } - protected void createMultiverseWorld() throws Exception { - // Check if world creator is configured and add new world to multiverse world manager - if (worldCreator != null) { - if (!worldManager.isMVWorld(worldName)) - worldManager.addWorld(worldName, environment, null, worldType, false, - "VoidGen:{\"caves\":false,\"decoration\":false,\"mobs\":false,\"structures\":false}", false); - } else { - throw new Exception("World Creator is not configured"); - } + protected void createMultiverseWorld() { + assert this.world != null; + if (worldManager.isMVWorld(worldName)) return; + + worldManager.addWorld(worldName, environment, null, worldType, false, + "VoidGen:{\"caves\":false,\"decoration\":false,\"mobs\":false,\"structures\":false}", false); } protected void configureWorld() { - World bukkitWorld = Bukkit.getWorld(worldName); - MultiverseWorld mvWorld = worldManager.getMVWorld(bukkitWorld); - - // 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); + assert this.world != null; + MultiverseWorld mvWorld = worldManager.getMVWorld(this.world); // Configure multiverse world mvWorld.setAllowFlight(true); @@ -136,15 +139,6 @@ protected void createGlobalProtection() throws StorageException { 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/world/SkeletonWorldGenerator.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/SkeletonWorldGenerator.java new file mode 100644 index 00000000..43454daa --- /dev/null +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/SkeletonWorldGenerator.java @@ -0,0 +1,83 @@ +/* + * 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.*; +import org.bukkit.generator.ChunkGenerator; + +import javax.annotation.Nonnull; +import java.util.Random; + +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(); + } + + 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); + } + + 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); + } + } +} From 92632068de619f798407aa0f5eea24b4c79e7a56 Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Mon, 14 Jul 2025 13:18:59 +0200 Subject: [PATCH 02/21] generate skeleton on startup if not exists. --- src/main/java/com/alpsbte/plotsystem/PlotSystem.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java index cacd4c3b..08c18085 100644 --- a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java +++ b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java @@ -36,6 +36,7 @@ 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.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; @@ -145,6 +146,13 @@ public void onEnable() { 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)); From 573884f618ad6c004ac573de4079740cca425ceb Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Mon, 14 Jul 2025 13:19:46 +0200 Subject: [PATCH 03/21] save skeleton to generate level.dat and remove uid.dat for multiverse loading --- .../system/plot/generator/world/PlotWorldGenerator.java | 3 +++ .../system/plot/generator/world/SkeletonWorldGenerator.java | 6 ++++++ 2 files changed, 9 insertions(+) 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 index f7926991..cd91466a 100644 --- 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 @@ -72,6 +72,9 @@ protected void generateWorld() throws IOException { 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()); 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 index 43454daa..7ced0a65 100644 --- 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 @@ -42,6 +42,7 @@ public class SkeletonWorldGenerator { public SkeletonWorldGenerator() { generateWorld(); configureWorld(); + saveWorld(); } protected void generateWorld() { @@ -73,6 +74,11 @@ protected void configureWorld() { bukkitWorld.setTime(6000); } + protected void saveWorld() { + assert this.world != null; + this.world.save(); + } + public static class EmptyChunkGenerator extends ChunkGenerator { @Override @Nonnull From d0b817396d71345f08554168e913e2f2d5281873 Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Sun, 20 Jul 2025 21:24:13 +0200 Subject: [PATCH 04/21] fix async issues and cleanup generation related classes --- .../plotsystem/core/system/plot/Plot.java | 11 +- .../plot/generator/AbstractPlotGenerator.java | 53 ++++--- .../plot/generator/DefaultPlotGenerator.java | 37 +++-- .../generator/world/PlotWorldGenerator.java | 23 +-- .../core/system/plot/world/CityPlotWorld.java | 65 ++++----- .../core/system/plot/world/PlotWorld.java | 138 +++++++++--------- 6 files changed, 170 insertions(+), 157 deletions(-) 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 387ebd5a..e67948d0 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 @@ -30,7 +30,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; @@ -151,14 +150,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/generator/AbstractPlotGenerator.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/AbstractPlotGenerator.java index 27c7b9d8..1a69faa8 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/AbstractPlotGenerator.java @@ -31,8 +31,6 @@ 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; @@ -69,6 +67,7 @@ import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.protection.regions.RegionContainer; +import org.bukkit.Bukkit; import org.bukkit.configuration.file.FileConfiguration; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -78,6 +77,7 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.concurrent.CompletableFuture; import static net.kyori.adventure.text.Component.text; @@ -87,7 +87,6 @@ public abstract class AbstractPlotGenerator { protected final PlotWorld world; protected final double plotVersion; protected final PlotType plotType; - /** * Generates a new plot in the plot world * @@ -106,7 +105,7 @@ protected AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder bui * @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)); + this(plot, builder, plotType, PlotWorld.getByType(plotType, (Plot) plot)); } /** @@ -123,24 +122,42 @@ private AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder build this.plotVersion = plot.getVersion(); this.plotType = plotType; - if (init()) { - Exception exception = null; + PlotSystem.getPlugin().getComponentLogger().info("plotType: " + plotType.getId()); + + if (!init()) return; + boolean generateWorld = plotType.hasOnePlotPerWorld() || !world.isWorldGenerated(); + boolean isWorldLoaded = world.isWorldLoaded(); + + CompletableFuture.runAsync(() -> { + final Exception[] exception = new Exception[]{null}; try { - if (plotType.hasOnePlotPerWorld() || !world.isWorldGenerated()) { + if (generateWorld) { new PlotWorldGenerator(world.getWorldName()); - } else if (!world.isWorldLoaded() && !world.loadWorld()) throw new Exception("Could not load world"); - generateOutlines(); - createPlotProtection(); + } else if (!isWorldLoaded) { + final boolean[] successful = new boolean[1]; + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> successful[0] = world.loadWorld()); + if (!successful[0]) throw new Exception("Could not load world"); + } + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { + try { + createPlotProtection(); + generateOutlines(); + } catch (StorageException | IOException e) { + exception[0] = e; + } + }); } catch (Exception ex) { - exception = ex; + exception[0] = ex; } - this.onComplete(exception != null, false); - - if (exception != null) { - PlotUtils.Actions.abandonPlot(plot); - onException(exception); - } - } + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { + this.onComplete(exception[0] != null, false); + + if (exception[0] != null) { + PlotUtils.Actions.abandonPlot(plot); + onException(exception[0]); + } + }); + }); } 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 index 6ada921c..002f0680 100644 --- 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 @@ -70,32 +70,31 @@ public DefaultPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder builder @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 { + if (getBuilder().getFreeSlot() == null) { 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; } - return false; + + if (DefaultPlotGenerator.playerPlotGenerationHistory.containsKey(getBuilder().getUUID())) { + if (!DefaultPlotGenerator.playerPlotGenerationHistory.get(getBuilder().getUUID()).isBefore(LocalDateTime.now().minusSeconds(10))) { + 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.remove(getBuilder().getUUID()); + } + + 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; } @Override protected void generateOutlines() throws IOException, WorldEditException { - if (plot instanceof Plot) { - byte[] completedSchematic = ((Plot) plot).getCompletedSchematic(); + if (plot instanceof Plot p) { + byte[] completedSchematic = p.getCompletedSchematic(); if (completedSchematic != null) { Mask airMask = new BlockTypeMask(BukkitAdapter.adapt(world.getBukkitWorld()), BlockTypes.AIR); pasteSchematic(airMask, completedSchematic, world, true); 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 index cd91466a..60532ddf 100644 --- 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 @@ -58,12 +58,21 @@ public class PlotWorldGenerator { private World world = null; - public PlotWorldGenerator(String worldName) throws IOException, StorageException { + public PlotWorldGenerator(String worldName) throws Exception { this.worldName = worldName; generateWorld(); - createMultiverseWorld(); - configureWorld(); - createGlobalProtection(); + + final Exception[] exception = {null}; + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { + try { + createMultiverseWorld(); + configureWorld(); + createGlobalProtection(); + } catch (Exception e) { + exception[0] = e; + } + }); + if (exception[0] != null) throw exception[0]; } protected void generateWorld() throws IOException { @@ -91,20 +100,16 @@ protected void generateWorld() throws IOException { .replaceAll(SkeletonWorldGenerator.WORLD_NAME, worldName) .replaceAll(SkeletonWorldGenerator.WORLD_NAME.toLowerCase(), worldName.toLowerCase()); Files.writeString(paperWorld, updatedContents); - - // load world - this.world = Bukkit.getWorld(worldName); } protected void createMultiverseWorld() { - assert this.world != null; if (worldManager.isMVWorld(worldName)) return; - worldManager.addWorld(worldName, environment, null, worldType, false, "VoidGen:{\"caves\":false,\"decoration\":false,\"mobs\":false,\"structures\":false}", false); } protected void configureWorld() { + this.world = Bukkit.getWorld(worldName); assert this.world != null; MultiverseWorld mvWorld = worldManager.getMVWorld(this.world); 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 b3152dc0..9d2d0599 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 @@ -48,25 +48,23 @@ 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 (getPlot() == 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(getPlot().getID())))); - return true; - } - return false; + Utils.updatePlayerInventorySlots(player); + PlotUtils.ChatFormatting.sendLinkMessages(getPlot(), player); + + if (!getPlot().getPlotOwner().getUUID().equals(player.getUniqueId())) return true; + getPlot().setLastActivity(false); + + return true; } @Override @@ -100,25 +98,24 @@ public int getWorldHeight() throws IOException { try (ClipboardReader reader = AbstractPlot.CLIPBOARD_FORMAT.getReader(inputStream)) { clipboard = reader.read(); } - if (clipboard != null) { - 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, - /// so you don't want builder to only be able to build for 18 blocks - int minBuildingHeight = 128; - - /// Negative y level of the current minecraft version (1.21) - /// 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."); + 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, + /// so you don't want builder to only be able to build for 18 blocks + int minBuildingHeight = 128; + + /// Negative y level of the current minecraft version (1.21) + /// 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; } /** 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 ce0ccadb..261d8ae6 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 @@ -28,10 +28,13 @@ 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.utils.PlotType; import com.alpsbte.plotsystem.utils.DependencyManager; import com.alpsbte.plotsystem.utils.Utils; +import com.alpsbte.plotsystem.utils.io.ConfigPaths; import com.onarandombox.MultiverseCore.MultiverseCore; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.extent.clipboard.Clipboard; @@ -82,48 +85,48 @@ public boolean regenWorld(@NotNull Class ge @Override public boolean deleteWorld() { - if (isWorldGenerated() && loadWorld()) { - if (mvCore.getMVWorldManager().deleteWorld(getWorldName(), true, true) && mvCore.saveWorldConfig()) { - 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 (!mvCore.getMVWorldManager().deleteWorld(getWorldName(), true, true) || !mvCore.saveWorldConfig()) { + 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.getMVWorldManager().loadWorld(getWorldName()) || 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.getMVWorldManager().loadWorld(getWorldName()); } @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 @@ -131,25 +134,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 @@ -159,17 +158,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 @@ -208,13 +205,13 @@ public boolean isWorldGenerated() { } private @Nullable ProtectedRegion getRegion(String regionName) { + if (!loadWorld()) return null; + 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!")); - } + 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; } @@ -247,12 +244,17 @@ 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; + 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) { + boolean disableCIM = PlotSystem.getPlugin().getConfig().getBoolean(ConfigPaths.DISABLE_CITY_INSPIRATION_MODE); + return disableCIM || type.hasOnePlotPerWorld() ? new OnePlotWorld(plot) : new CityPlotWorld(plot); } } From 16f0b39a6cb506d08788aa494a7d93a01af93a4e Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Mon, 4 Aug 2025 00:18:57 +0200 Subject: [PATCH 05/21] add util methods for passing up exceptions in bukkit scheduler --- .../com/alpsbte/plotsystem/utils/Utils.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/main/java/com/alpsbte/plotsystem/utils/Utils.java b/src/main/java/com/alpsbte/plotsystem/utils/Utils.java index 1bd10227..d1cbef3c 100644 --- a/src/main/java/com/alpsbte/plotsystem/utils/Utils.java +++ b/src/main/java/com/alpsbte/plotsystem/utils/Utils.java @@ -62,6 +62,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; @@ -118,6 +121,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() {} From 779b31a31692075d3f9752a72cf6a203769498af Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Thu, 7 Aug 2025 02:53:52 +0200 Subject: [PATCH 06/21] - pass schematic bytes into generateOutlines method of PlotGenerators - measure old generator performance - add new AbstractPlotLoader, DefaultPlotLoader and PlotHandler --- .../plotsystem/core/EventListener.java | 2 +- .../core/menus/companion/CityProjectMenu.java | 57 ++-- .../core/system/plot/PlotHandler.java | 93 ++++++ .../plot/generator/AbstractPlotGenerator.java | 24 +- .../plot/generator/DefaultPlotGenerator.java | 12 +- .../plot/generator/TutorialPlotGenerator.java | 2 +- .../generator/loader/AbstractPlotLoader.java | 270 ++++++++++++++++++ .../generator/loader/DefaultPlotLoader.java | 82 ++++++ .../generator/world/PlotWorldGenerator.java | 34 +-- .../core/system/plot/utils/PlotUtils.java | 6 +- 10 files changed, 531 insertions(+), 51 deletions(-) create mode 100644 src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java create mode 100644 src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java create mode 100644 src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/DefaultPlotLoader.java diff --git a/src/main/java/com/alpsbte/plotsystem/core/EventListener.java b/src/main/java/com/alpsbte/plotsystem/core/EventListener.java index 1f1d5b15..1397769c 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/EventListener.java +++ b/src/main/java/com/alpsbte/plotsystem/core/EventListener.java @@ -183,7 +183,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 0bccffc8..0a6930a3 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 @@ -33,15 +33,19 @@ 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.PlotHandler; import com.alpsbte.plotsystem.core.system.plot.generator.DefaultPlotGenerator; +import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; 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.ConfigPaths; +import com.alpsbte.plotsystem.utils.io.ConfigUtil; 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; @@ -50,6 +54,7 @@ import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class CityProjectMenu extends AbstractPaginatedMenu { @@ -126,6 +131,34 @@ 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())); + + 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 = PlotHandler.assignPlot(builder, plot); + if (successful) PlotHandler.generatePlot(builder, plot, type); + } + public static boolean generateRandomPlot(Player player, @NotNull List items, PlotDifficulty selectedPlotDifficulty) { PlotDifficulty difficulty = selectedPlotDifficulty; if (items.isEmpty()) { @@ -205,29 +238,9 @@ protected void setPaginatedItemClickEventsAsync(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/system/plot/PlotHandler.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java new file mode 100644 index 00000000..88734796 --- /dev/null +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java @@ -0,0 +1,93 @@ +/* + * 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.plot.generator.loader.DefaultPlotLoader; +import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; +import com.alpsbte.plotsystem.core.system.plot.world.PlotWorld; +import com.alpsbte.plotsystem.utils.Utils; +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.LangPaths; +import com.alpsbte.plotsystem.utils.io.LangUtil; +import org.bukkit.SoundCategory; +import org.bukkit.entity.Player; + +import java.time.LocalDateTime; +import java.util.*; + +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 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)); + } +} 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/AbstractPlotGenerator.java index 1a69faa8..b4f6a94d 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/AbstractPlotGenerator.java @@ -124,28 +124,43 @@ private AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder build PlotSystem.getPlugin().getComponentLogger().info("plotType: " + plotType.getId()); + long initStartTime = System.nanoTime(); if (!init()) return; + PlotSystem.getPlugin().getComponentLogger().info("(APG) Init time: {}ms", (System.nanoTime() - initStartTime) / 1_000_000); + boolean generateWorld = plotType.hasOnePlotPerWorld() || !world.isWorldGenerated(); boolean isWorldLoaded = world.isWorldLoaded(); CompletableFuture.runAsync(() -> { final Exception[] exception = new Exception[]{null}; try { + // Generate and/or load world if (generateWorld) { + long worldGenerationStartTime = System.nanoTime(); new PlotWorldGenerator(world.getWorldName()); + PlotSystem.getPlugin().getComponentLogger().info("(APG) Total PlotWorldGenerator time: {}ms", (System.nanoTime() - worldGenerationStartTime) / 1_000_000); } else if (!isWorldLoaded) { final boolean[] successful = new boolean[1]; Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> successful[0] = world.loadWorld()); if (!successful[0]) throw new Exception("Could not load world"); } + + long schemFetchTime = System.nanoTime(); + // Fetch initial plot schematic + byte[] initialSchematic = plot.getInitialSchematicBytes(); + PlotSystem.getPlugin().getComponentLogger().info("(APG) schem fetching time: {}ms", (System.nanoTime() - schemFetchTime) / 1_000_000); + + long generateOutlinesTime = System.nanoTime(); Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { try { createPlotProtection(); - generateOutlines(); + generateOutlines(initialSchematic); } catch (StorageException | IOException e) { exception[0] = e; } }); + PlotSystem.getPlugin().getComponentLogger().info("(APG) generate outlines time (main thread): {}ms", (System.nanoTime() - generateOutlinesTime) / 1_000_000); + } catch (Exception ex) { exception[0] = ex; } @@ -157,6 +172,7 @@ private AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder build onException(exception[0]); } }); + PlotSystem.getPlugin().getComponentLogger().info("(APG) total time: {}ms", (System.nanoTime() - initStartTime) / 1_000_000); }); } @@ -172,12 +188,12 @@ private AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder build /** * Generates plot schematic and outlines */ - protected void generateOutlines() throws IOException { + protected void generateOutlines(byte[] initialSchematic) throws IOException { if (plotVersion >= 3 && plotType.hasEnvironment()) { - pasteSchematic(null, plot.getInitialSchematicBytes(), world, false); + pasteSchematic(null, initialSchematic, world, false); } else { Mask airMask = new BlockTypeMask(BukkitAdapter.adapt(world.getBukkitWorld()), BlockTypes.AIR); - pasteSchematic(airMask, PlotUtils.getOutlinesSchematicBytes(plot, world.getBukkitWorld()), world, true); + pasteSchematic(airMask, PlotUtils.getOutlinesSchematicBytes(plot, initialSchematic, world.getBukkitWorld()), world, 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 index 002f0680..0dcd7ae5 100644 --- 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 @@ -92,14 +92,16 @@ protected boolean init() { } @Override - protected void generateOutlines() throws IOException, WorldEditException { - if (plot instanceof Plot p) { + protected void generateOutlines(byte[] schematic) throws IOException, WorldEditException { + long startTime = System.nanoTime(); + if (!(plot instanceof Plot p)) super.generateOutlines(schematic); + else { byte[] completedSchematic = p.getCompletedSchematic(); if (completedSchematic != null) { Mask airMask = new BlockTypeMask(BukkitAdapter.adapt(world.getBukkitWorld()), BlockTypes.AIR); pasteSchematic(airMask, completedSchematic, world, true); - } else super.generateOutlines(); - } else super.generateOutlines(); + } else super.generateOutlines(schematic); + } // 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) { @@ -124,6 +126,8 @@ protected void onException(Throwable ex) { } }; } + + PlotSystem.getPlugin().getComponentLogger().info("(DPG) Generate Outlines time: {}ms", (System.nanoTime() - startTime) / 1_000_000); } @Override 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/TutorialPlotGenerator.java index 1b3028e2..de105ac2 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/TutorialPlotGenerator.java @@ -60,7 +60,7 @@ protected boolean init() { public void generateOutlines(int schematicId) throws IOException, WorldEditException { ((TutorialPlot) plot).setTutorialSchematic(schematicId); - generateOutlines(); + generateOutlines(getPlot().getInitialSchematicBytes()); } @Override diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java new file mode 100644 index 00000000..d239e980 --- /dev/null +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java @@ -0,0 +1,270 @@ +/* + * 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.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.PlotWorld; +import com.alpsbte.plotsystem.utils.DependencyManager; +import com.alpsbte.plotsystem.utils.Utils; +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.EditSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.BukkitWorld; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; +import com.sk89q.worldedit.function.mask.BlockTypeMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.RegionMask; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector2; +import com.sk89q.worldedit.regions.CylinderRegion; +import com.sk89q.worldedit.regions.Polygonal2DRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.domains.DefaultDomain; +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.regions.ProtectedPolygonalRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.protection.regions.RegionContainer; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; + +import static net.kyori.adventure.text.Component.text; + +public abstract class AbstractPlotLoader { + protected final AbstractPlot plot; + protected final PlotType plotType; + protected final PlotWorld plotWorld; + + protected final Builder builder; + + protected byte[] schematicBytes = null; + + protected AbstractPlotLoader(@NotNull AbstractPlot plot, Builder builder, PlotType plotType, PlotWorld plotWorld) { + this.plot = plot; + this.plotType = plotType; + this.plotWorld = plotWorld; + this.builder = builder; + + 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; + + new PlotWorldGenerator(plotWorld.getWorldName()); + } + + protected void loadWorld() throws Exception { + Utils.runSync(() -> { + if (plotWorld.isWorldLoaded()) return null; + + boolean successful = plotWorld.loadWorld(); + if (!successful) throw new Exception("Could not load world!"); + return null; + }).get(); + } + + protected void fetchSchematicData() { + this.schematicBytes = plot.getInitialSchematicBytes(); + } + + 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; + } + + // Create build region for plot from the outline of the plot + 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 + ProtectedRegion protectedRegion = getProtectedRegion(); + + // Add plot owner + DefaultDomain owner = protectedBuildRegion.getOwners(); + owner.addPlayer(builder.getUUID()); + protectedBuildRegion.setOwners(owner); + + // Set protected build region permissions + setBuildRegionPermissions(protectedBuildRegion); + + // Set protected region permissions + setRegionPermissions(protectedRegion); + + // Add regions and save changes + 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(); + 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; + } + + /** + * Sets the permissions for the plot build region only + * + * @param region build region + */ + protected void setBuildRegionPermissions(@NotNull ProtectedRegion region) { + region.setFlag(Flags.BUILD, StateFlag.State.ALLOW); + region.setFlag(Flags.BUILD.getRegionGroupFlag(), RegionGroup.OWNERS); + if (DependencyManager.isWorldGuardExtraFlagsEnabled()) + region.setFlag(new StateFlag("worldedit", true, RegionGroup.OWNERS), StateFlag.State.ALLOW); + } + + /** + * Sets the permissions for the whole plot region + * + * @param region plot region + */ + protected void setRegionPermissions(@NotNull ProtectedRegion region) { + region.setFlag(Flags.ENTRY, StateFlag.State.ALLOW); + region.setFlag(Flags.ENTRY.getRegionGroupFlag(), RegionGroup.ALL); + + FileConfiguration config = ConfigUtil.getInstance().configs[1]; + region.setFlag(Flags.BLOCKED_CMDS, new HashSet<>(getBlockedCommands(config))); + region.setFlag(Flags.BLOCKED_CMDS.getRegionGroupFlag(), RegionGroup.ALL); + } + + /** + * Reads the blocked commands for the plot region from the config + * + * @param config commands.yml config + * @return list of blocked commands + */ + protected List getBlockedCommands(@NotNull FileConfiguration config) { + List blockedCommands = config.getStringList(ConfigPaths.BLOCKED_COMMANDS_BUILDERS); + blockedCommands.removeIf(c -> c.equals("/cmd1")); + return blockedCommands; + } + + 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(); + } + + public static void pasteSchematic(@Nullable Mask pasteMask, byte[] schematicFile, @NotNull PlotWorld world, boolean clearArea) throws IOException { + // load world if not loaded already + if (!world.loadWorld()) return; + World weWorld = new BukkitWorld(world.getBukkitWorld()); + + // set outline region with air + if (clearArea) { + try (EditSession editSession = WorldEdit.getInstance().newEditSession(BukkitAdapter.adapt(world.getBukkitWorld()))) { + Polygonal2DRegion polyRegion = new Polygonal2DRegion(weWorld, world.getPlot().getOutline(), 0, PlotWorld.MAX_WORLD_HEIGHT); + editSession.setMask(new RegionMask(polyRegion)); + editSession.setBlocks((Region) polyRegion, Objects.requireNonNull(BlockTypes.AIR).getDefaultState()); + } + } + + // load schematic + Clipboard clipboard; + ByteArrayInputStream inputStream = new ByteArrayInputStream(schematicFile); + try (ClipboardReader reader = AbstractPlot.CLIPBOARD_FORMAT.getReader(inputStream)) { + clipboard = reader.read(); + } + + // paste schematic + try (EditSession editSession = WorldEdit.getInstance().newEditSession(BukkitAdapter.adapt(world.getBukkitWorld()))) { + if (pasteMask != null) editSession.setMask(pasteMask); + Operation clipboardHolder = new ClipboardHolder(clipboard) + .createPaste(editSession) + .to(BlockVector3.at(world.getPlot().getCenter().x(), world.getPlotHeight(), world.getPlot().getCenter().z())) + .build(); + Operations.complete(clipboardHolder); + } + } + + protected void onException(Exception e) { + // TODO: abandon 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..4e581177 --- /dev/null +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/DefaultPlotLoader.java @@ -0,0 +1,82 @@ +/* + * 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.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; + +public class DefaultPlotLoader extends AbstractPlotLoader { + public DefaultPlotLoader(@NotNull AbstractPlot plot, Builder builder, PlotType plotType, PlotWorld plotWorld) { + super(plot, builder, plotType, plotWorld); + } + + @Override + protected void generateStructure() throws Exception { + if (!(plot instanceof Plot p)) super.generateStructure(); + else { + 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() { + 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 + // TODO: generate plot on city world + // TODO: unload CIM world when done + } +} 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 index 60532ddf..80b39e34 100644 --- 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 @@ -26,6 +26,7 @@ import com.alpsbte.plotsystem.PlotSystem; import com.alpsbte.plotsystem.utils.DependencyManager; +import com.alpsbte.plotsystem.utils.Utils; import com.onarandombox.MultiverseCore.api.MVWorldManager; import com.onarandombox.MultiverseCore.api.MultiverseWorld; import com.sk89q.worldedit.bukkit.BukkitAdapter; @@ -56,23 +57,24 @@ public class PlotWorldGenerator { private static final World.Environment environment = World.Environment.NORMAL; private static final WorldType worldType = WorldType.FLAT; - private World world = null; - public PlotWorldGenerator(String worldName) throws Exception { + long startTime = System.nanoTime(); this.worldName = worldName; + + // Async Part generateWorld(); - final Exception[] exception = {null}; - Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { - try { - createMultiverseWorld(); - configureWorld(); - createGlobalProtection(); - } catch (Exception e) { - exception[0] = e; - } - }); - if (exception[0] != null) throw exception[0]; + // 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 { @@ -109,9 +111,9 @@ protected void createMultiverseWorld() { } protected void configureWorld() { - this.world = Bukkit.getWorld(worldName); - assert this.world != null; - MultiverseWorld mvWorld = worldManager.getMVWorld(this.world); + World world = Bukkit.getWorld(worldName); + assert world != null; + MultiverseWorld mvWorld = worldManager.getMVWorld(world); // Configure multiverse world mvWorld.setAllowFlight(true); 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 6838ce05..5c52ee29 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 @@ -176,9 +176,9 @@ public static boolean isPlotWorld(World world) { return DependencyManager.getMultiverseCore().getMVWorldManager().isMVWorld(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(); } @@ -397,7 +397,7 @@ public static boolean abandonPlot(@NotNull AbstractPlot plot) { 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); + AbstractPlotGenerator.pasteSchematic(null, getOutlinesSchematicBytes(plot, plot.getInitialSchematicBytes(), world.getBukkitWorld()), world, true); } else PlotSystem.getPlugin().getComponentLogger().warn(text("Region Manager is null!")); playersToTeleport.forEach(p -> p.teleport(Utils.getSpawnLocation())); From fb9fd471c044f9cffa4f05c19d0c70191072c81a Mon Sep 17 00:00:00 2001 From: Zoriot Date: Mon, 25 Aug 2025 03:49:03 +0200 Subject: [PATCH 07/21] =?UTF-8?q?feat(gen):=20=E2=9C=A8=20update=20emptych?= =?UTF-8?q?unkgenerator=20and=20provide=20it=20outside=20of=20plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/alpsbte/plotsystem/PlotSystem.java | 6 ++++++ .../generator/world/SkeletonWorldGenerator.java | 14 +++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java index 08c18085..19fe3581 100644 --- a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java +++ b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java @@ -53,6 +53,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; @@ -222,4 +223,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/core/system/plot/generator/world/SkeletonWorldGenerator.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/SkeletonWorldGenerator.java index 7ced0a65..acf97e31 100644 --- 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 @@ -25,12 +25,12 @@ package com.alpsbte.plotsystem.core.system.plot.generator.world; import net.kyori.adventure.util.TriState; -import org.bukkit.*; +import org.bukkit.GameRule; +import org.bukkit.World; +import org.bukkit.WorldCreator; +import org.bukkit.WorldType; import org.bukkit.generator.ChunkGenerator; -import javax.annotation.Nonnull; -import java.util.Random; - public class SkeletonWorldGenerator { public static final String WORLD_NAME = "Skeleton"; private static final World.Environment ENVIRONMENT = World.Environment.NORMAL; @@ -80,10 +80,6 @@ protected void saveWorld() { } 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); - } + // It should just do nothing } } From 198e0140f105f16c3b98de2c5a97ba7f9bcea9ef Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Sat, 1 Nov 2025 17:29:19 +0100 Subject: [PATCH 08/21] Implement plot abandon functionality in the PlotHandler and rework Plot Worlds to handle world specific abandon logic --- .../com/alpsbte/plotsystem/PlotSystem.java | 5 +- .../commands/admin/CMD_DeletePlot.java | 11 +- .../commands/plot/CMD_Plot_Abandon.java | 8 +- .../commands/plot/CMD_Plot_Submit.java | 3 +- .../commands/plot/CMD_Plot_UndoSubmit.java | 3 +- .../menus/review/ReviewPlotTogglesMenu.java | 3 +- .../core/system/plot/PlotHandler.java | 108 +++++++++++ .../plot/generator/AbstractPlotGenerator.java | 3 +- .../core/system/plot/utils/PlotUtils.java | 182 ++---------------- .../core/system/plot/world/CityPlotWorld.java | 71 +++++-- .../core/system/plot/world/IWorld.java | 18 ++ .../core/system/plot/world/OnePlotWorld.java | 60 +++--- .../core/system/plot/world/PlotWorld.java | 19 +- 13 files changed, 270 insertions(+), 224 deletions(-) diff --git a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java index 19fe3581..3b105e5e 100644 --- a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java +++ b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java @@ -36,6 +36,7 @@ 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; @@ -137,10 +138,12 @@ 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)); 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 530b72f5..02bb7005 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 @@ -29,6 +29,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; @@ -62,10 +63,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 1cb48d46..dba38cb4 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 @@ -32,6 +32,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; @@ -91,10 +92,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 e93a3284..74dacc99 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 @@ -32,6 +32,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; @@ -99,7 +100,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_UndoSubmit.java b/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_UndoSubmit.java index b4a6bebc..8d907992 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 @@ -32,6 +32,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; @@ -93,7 +94,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/menus/review/ReviewPlotTogglesMenu.java b/src/main/java/com/alpsbte/plotsystem/core/menus/review/ReviewPlotTogglesMenu.java index 38a8ea22..cad95ae1 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 @@ -31,6 +31,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.PlotHandler; import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; import com.alpsbte.plotsystem.core.system.review.PlotReview; import com.alpsbte.plotsystem.core.system.review.ReviewRating; @@ -156,7 +157,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(), () -> { 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 index 88734796..8621fead 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java @@ -29,6 +29,9 @@ import com.alpsbte.plotsystem.core.system.Builder; 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.Slot; @@ -36,11 +39,18 @@ import com.alpsbte.plotsystem.utils.io.ConfigPaths; import com.alpsbte.plotsystem.utils.io.LangPaths; import com.alpsbte.plotsystem.utils.io.LangUtil; +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.time.LocalDate; import java.time.LocalDateTime; import java.util.*; +import java.util.concurrent.CompletableFuture; + +import static net.kyori.adventure.text.Component.text; public class PlotHandler { private PlotHandler() {} @@ -90,4 +100,102 @@ public static void generatePlot(Builder builder, Plot plot, PlotType type) { 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()); + } + } + } } 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/AbstractPlotGenerator.java index b4f6a94d..a38f242b 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/AbstractPlotGenerator.java @@ -28,6 +28,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.generator.world.PlotWorldGenerator; import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; @@ -168,7 +169,7 @@ private AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder build this.onComplete(exception[0] != null, false); if (exception[0] != null) { - PlotUtils.Actions.abandonPlot(plot); + PlotHandler.abandonPlot(plot); onException(exception[0]); } }); 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 edffdb36..a53a0247 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 @@ -30,10 +30,8 @@ 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; @@ -62,17 +60,13 @@ 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; @@ -86,8 +80,6 @@ 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; @@ -161,9 +153,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; @@ -302,39 +295,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); @@ -354,119 +325,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, plot.getInitialSchematicBytes(), 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() {} @@ -548,19 +406,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 9d2d0599..8f061aa8 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 @@ -24,15 +24,22 @@ 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; @@ -41,6 +48,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); @@ -54,29 +63,29 @@ public boolean teleportPlayer(@NotNull Player player) { player.setAllowFlight(true); player.setFlying(true); - if (getPlot() == null) return true; + if (plot == null) return true; - player.sendMessage(Utils.ChatUtils.getInfoFormat(LangUtil.getInstance().get(player, LangPaths.Message.Info.TELEPORTING_PLOT, String.valueOf(getPlot().getID())))); + player.sendMessage(Utils.ChatUtils.getInfoFormat(LangUtil.getInstance().get(player, LangPaths.Message.Info.TELEPORTING_PLOT, String.valueOf(plot.getID())))); Utils.updatePlayerInventorySlots(player); - PlotUtils.ChatFormatting.sendLinkMessages(getPlot(), player); + PlotUtils.ChatFormatting.sendLinkMessages(plot, 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; } @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 @@ -94,7 +103,7 @@ 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(); } @@ -123,14 +132,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 bca5b9bf..a1543b2d 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 @@ -24,6 +24,7 @@ package com.alpsbte.plotsystem.core.system.plot.world; +import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; import com.alpsbte.plotsystem.core.system.plot.generator.AbstractPlotGenerator; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldguard.protection.regions.ProtectedRegion; @@ -34,6 +35,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 @@ -147,4 +151,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 94a1a1cb..4fdfb9cf 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 @@ -31,6 +31,7 @@ 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.TutorialPlotGenerator; +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.generator.DefaultPlotGenerator; import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; @@ -46,9 +47,11 @@ 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(); } @@ -57,36 +60,25 @@ public boolean generateWorld(@NotNull Class if (isWorldGenerated()) return false; if (generator.isAssignableFrom(DefaultPlotGenerator.class)) { - new DefaultPlotGenerator(getPlot(), plotOwner); + new DefaultPlotGenerator(plot, plotOwner); } else if (generator.isAssignableFrom(TutorialPlotGenerator.class)) { - new TutorialPlotGenerator(getPlot(), plotOwner); + new TutorialPlotGenerator(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) + if (plot.getPlotType() == PlotType.TUTORIAL || ((Plot) plot).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); - } - }; + 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; @@ -95,13 +87,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 @@ -112,15 +101,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; } @@ -134,4 +123,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 261d8ae6..f7d9fc92 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 @@ -66,7 +66,7 @@ public class PlotWorld implements IWorld { private final MultiverseCore 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; @@ -204,6 +204,16 @@ public boolean isWorldGenerated() { return mvCore.getMVWorldManager().getMVWorld(worldName) != null || mvCore.getMVWorldManager().getUnloadedWorlds().contains(worldName); } + @Override + public AbstractPlot getPlot() { + return plot; + } + + @Override + public boolean onAbandon() { + return true; + } + private @Nullable ProtectedRegion getRegion(String regionName) { if (!loadWorld()) return null; @@ -215,11 +225,6 @@ public boolean isWorldGenerated() { return null; } - public AbstractPlot getPlot() { - return plot; - } - - /** * @param worldName - the name of the world * @return - true if the world is a plot world @@ -244,6 +249,7 @@ public static boolean isCityPlotWorld(@NotNull String worldName) { * @return - plot world */ public static @Nullable PlotWorld getPlotWorldByName(String worldName) { + // TODO: rework if (!isOnePlotWorld(worldName) && !isCityPlotWorld(worldName)) return null; Integer id = AlpsUtils.tryParseInt(worldName.substring(2)); @@ -254,6 +260,7 @@ public static boolean isCityPlotWorld(@NotNull String worldName) { } 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); } From a87a7e375bb1ffbdffc68fe1326377a976314f01 Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Mon, 14 Jul 2025 04:17:26 +0200 Subject: [PATCH 09/21] move world generation to SkeletonWorldGenerator and copy skeleton data in PlotWorldGenerator --- pom.xml | 5 + .../plot/generator/AbstractPlotGenerator.java | 1 + .../{ => world}/PlotWorldGenerator.java | 136 +++++++++--------- .../world/SkeletonWorldGenerator.java | 83 +++++++++++ 4 files changed, 160 insertions(+), 65 deletions(-) rename src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/{ => world}/PlotWorldGenerator.java (52%) create mode 100644 src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/SkeletonWorldGenerator.java 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/core/system/plot/generator/AbstractPlotGenerator.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/AbstractPlotGenerator.java index 6b168e97..1634f8d7 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/AbstractPlotGenerator.java @@ -4,6 +4,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.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; 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/world/PlotWorldGenerator.java similarity index 52% rename from src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/PlotWorldGenerator.java rename to src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/PlotWorldGenerator.java index 6dbd2041..50f07032 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/PlotWorldGenerator.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/PlotWorldGenerator.java @@ -1,4 +1,28 @@ -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.world; import com.alpsbte.plotsystem.PlotSystem; import com.alpsbte.plotsystem.utils.DependencyManager; @@ -11,36 +35,33 @@ 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 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.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.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; 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 { + private World world = null; + + public PlotWorldGenerator(String worldName) throws IOException, StorageException { this.worldName = worldName; generateWorld(); createMultiverseWorld(); @@ -48,54 +69,48 @@ public PlotWorldGenerator(String worldName) throws Exception { 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 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()); + + // 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); + + // load world + this.world = Bukkit.getWorld(worldName); } - 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 createMultiverseWorld() { + assert this.world != null; + 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 bukkitWorld = Bukkit.getWorld(worldName); + assert this.world != null; 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); @@ -130,15 +145,6 @@ protected void createGlobalProtection() throws StorageException { 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/world/SkeletonWorldGenerator.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/SkeletonWorldGenerator.java new file mode 100644 index 00000000..43454daa --- /dev/null +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/SkeletonWorldGenerator.java @@ -0,0 +1,83 @@ +/* + * 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.*; +import org.bukkit.generator.ChunkGenerator; + +import javax.annotation.Nonnull; +import java.util.Random; + +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(); + } + + 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); + } + + 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); + } + } +} From 73e9def21ffb61af3b609a636d7e3ccba95890d7 Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Mon, 14 Jul 2025 13:18:59 +0200 Subject: [PATCH 10/21] generate skeleton on startup if not exists. --- src/main/java/com/alpsbte/plotsystem/PlotSystem.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java index 201a5912..94c41885 100644 --- a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java +++ b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java @@ -12,6 +12,7 @@ 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.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; @@ -127,6 +128,13 @@ public void onEnable() { 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)); From 0783dc6402bc51f36d9cfea50aa404fa31cacbfd Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Mon, 14 Jul 2025 13:19:46 +0200 Subject: [PATCH 11/21] save skeleton to generate level.dat and remove uid.dat for multiverse loading --- .../system/plot/generator/world/PlotWorldGenerator.java | 3 +++ .../system/plot/generator/world/SkeletonWorldGenerator.java | 6 ++++++ 2 files changed, 9 insertions(+) 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 index 50f07032..569ec666 100644 --- 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 @@ -75,6 +75,9 @@ protected void generateWorld() throws IOException { 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()); 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 index 43454daa..7ced0a65 100644 --- 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 @@ -42,6 +42,7 @@ public class SkeletonWorldGenerator { public SkeletonWorldGenerator() { generateWorld(); configureWorld(); + saveWorld(); } protected void generateWorld() { @@ -73,6 +74,11 @@ protected void configureWorld() { bukkitWorld.setTime(6000); } + protected void saveWorld() { + assert this.world != null; + this.world.save(); + } + public static class EmptyChunkGenerator extends ChunkGenerator { @Override @Nonnull From 65716700eec5aae3b37d60d51cb6a6c9723c060d Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Sun, 20 Jul 2025 21:24:13 +0200 Subject: [PATCH 12/21] fix async issues and cleanup generation related classes --- .../plotsystem/core/system/plot/Plot.java | 11 +- .../plot/generator/AbstractPlotGenerator.java | 53 ++++-- .../plot/generator/DefaultPlotGenerator.java | 37 ++-- .../generator/world/PlotWorldGenerator.java | 22 ++- .../core/system/plot/world/CityPlotWorld.java | 65 ++++--- .../core/system/plot/world/PlotWorld.java | 167 ++++++++++-------- 6 files changed, 197 insertions(+), 158 deletions(-) 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 cd5f2265..5e47a827 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/generator/AbstractPlotGenerator.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/AbstractPlotGenerator.java index 1634f8d7..2aedf1be 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/AbstractPlotGenerator.java @@ -7,8 +7,6 @@ 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; @@ -45,6 +43,7 @@ import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.protection.regions.RegionContainer; +import org.bukkit.Bukkit; import org.bukkit.configuration.file.FileConfiguration; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -54,6 +53,7 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.concurrent.CompletableFuture; import static net.kyori.adventure.text.Component.text; @@ -63,7 +63,6 @@ public abstract class AbstractPlotGenerator { protected final PlotWorld world; protected final double plotVersion; protected final PlotType plotType; - /** * Generates a new plot in the plot world * @@ -82,7 +81,7 @@ protected AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder bui * @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)); + this(plot, builder, plotType, PlotWorld.getByType(plotType, (Plot) plot)); } /** @@ -99,24 +98,42 @@ private AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder build this.plotVersion = plot.getVersion(); this.plotType = plotType; - if (init()) { - Exception exception = null; + PlotSystem.getPlugin().getComponentLogger().info("plotType: " + plotType.getId()); + + if (!init()) return; + boolean generateWorld = plotType.hasOnePlotPerWorld() || !world.isWorldGenerated(); + boolean isWorldLoaded = world.isWorldLoaded(); + + CompletableFuture.runAsync(() -> { + final Exception[] exception = new Exception[]{null}; try { - if (plotType.hasOnePlotPerWorld() || !world.isWorldGenerated()) { + if (generateWorld) { new PlotWorldGenerator(world.getWorldName()); - } else if (!world.isWorldLoaded() && !world.loadWorld()) throw new Exception("Could not load world"); - generateOutlines(); - createPlotProtection(); + } else if (!isWorldLoaded) { + final boolean[] successful = new boolean[1]; + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> successful[0] = world.loadWorld()); + if (!successful[0]) throw new Exception("Could not load world"); + } + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { + try { + createPlotProtection(); + generateOutlines(); + } catch (StorageException | IOException e) { + exception[0] = e; + } + }); } catch (Exception ex) { - exception = ex; + exception[0] = ex; } - this.onComplete(exception != null, false); - - if (exception != null) { - PlotUtils.Actions.abandonPlot(plot); - onException(exception); - } - } + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { + this.onComplete(exception[0] != null, false); + + if (exception[0] != null) { + PlotUtils.Actions.abandonPlot(plot); + onException(exception[0]); + } + }); + }); } 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 index 3071a704..e95e77c5 100644 --- 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 @@ -46,32 +46,31 @@ public DefaultPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder builder @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 { + if (getBuilder().getFreeSlot() == null) { 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; } - return false; + + if (DefaultPlotGenerator.playerPlotGenerationHistory.containsKey(getBuilder().getUUID())) { + if (!DefaultPlotGenerator.playerPlotGenerationHistory.get(getBuilder().getUUID()).isBefore(LocalDateTime.now().minusSeconds(10))) { + 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.remove(getBuilder().getUUID()); + } + + 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; } @Override protected void generateOutlines() throws IOException, WorldEditException { - if (plot instanceof Plot) { - byte[] completedSchematic = ((Plot) plot).getCompletedSchematic(); + if (plot instanceof Plot p) { + byte[] completedSchematic = p.getCompletedSchematic(); if (completedSchematic != null) { PlotSystem.getPlugin().getComponentLogger().info("Found completed schematic, pasting only that."); Mask airMask = new BlockTypeMask(BukkitAdapter.adapt(world.getBukkitWorld()), BlockTypes.AIR); 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 index 569ec666..1315c3f3 100644 --- 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 @@ -61,12 +61,21 @@ public class PlotWorldGenerator { private World world = null; - public PlotWorldGenerator(String worldName) throws IOException, StorageException { + public PlotWorldGenerator(String worldName) throws Exception { this.worldName = worldName; generateWorld(); - createMultiverseWorld(); - configureWorld(); - createGlobalProtection(); + + final Exception[] exception = {null}; + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { + try { + createMultiverseWorld(); + configureWorld(); + createGlobalProtection(); + } catch (Exception e) { + exception[0] = e; + } + }); + if (exception[0] != null) throw exception[0]; } protected void generateWorld() throws IOException { @@ -94,13 +103,9 @@ protected void generateWorld() throws IOException { .replaceAll(SkeletonWorldGenerator.WORLD_NAME, worldName) .replaceAll(SkeletonWorldGenerator.WORLD_NAME.toLowerCase(), worldName.toLowerCase()); Files.writeString(paperWorld, updatedContents); - - // load world - this.world = Bukkit.getWorld(worldName); } protected void createMultiverseWorld() { - assert this.world != null; if (!worldManager.isLoadedWorld(worldName)) { worldManager.importWorld(ImportWorldOptions.worldName(worldName) .environment(environment) @@ -111,6 +116,7 @@ protected void createMultiverseWorld() { } protected void configureWorld() { + this.world = Bukkit.getWorld(worldName); assert this.world != null; Option mvWorld = worldManager.getLoadedWorld(worldName); 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 446d2710..1dcd22bf 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 @@ -24,25 +24,23 @@ 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 (getPlot() == 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(getPlot().getID())))); - return true; - } - return false; + Utils.updatePlayerInventorySlots(player); + PlotUtils.ChatFormatting.sendLinkMessages(getPlot(), player); + + if (!getPlot().getPlotOwner().getUUID().equals(player.getUniqueId())) return true; + getPlot().setLastActivity(false); + + return true; } @Override @@ -76,25 +74,24 @@ public int getWorldHeight() throws IOException { try (ClipboardReader reader = AbstractPlot.CLIPBOARD_FORMAT.getReader(inputStream)) { clipboard = reader.read(); } - if (clipboard != null) { - 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, - /// so you don't want builder to only be able to build for 18 blocks - int minBuildingHeight = 128; - - /// Negative y level of the current minecraft version (1.21) - /// 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."); + 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, + /// so you don't want builder to only be able to build for 18 blocks + int minBuildingHeight = 128; + + /// Negative y level of the current minecraft version (1.21) + /// 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; } /** 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..162a4d59 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,41 @@ +/* + * 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.utils.PlotType; import com.alpsbte.plotsystem.utils.DependencyManager; import com.alpsbte.plotsystem.utils.Utils; +import com.alpsbte.plotsystem.utils.io.ConfigPaths; +import com.onarandombox.MultiverseCore.MultiverseCore; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; @@ -59,50 +87,50 @@ public boolean regenWorld(@NotNull Class ge @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 +138,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 +162,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 @@ -187,13 +209,13 @@ public boolean isWorldGenerated() { } private @Nullable ProtectedRegion getRegion(String regionName) { + if (!loadWorld()) return null; + 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!")); - } + 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; } @@ -226,12 +248,17 @@ 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; + 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) { + boolean disableCIM = PlotSystem.getPlugin().getConfig().getBoolean(ConfigPaths.DISABLE_CITY_INSPIRATION_MODE); + return disableCIM || type.hasOnePlotPerWorld() ? new OnePlotWorld(plot) : new CityPlotWorld(plot); } } From 030be1fbe3779ce501798a360e1fd146a8a4a6d7 Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Mon, 4 Aug 2025 00:18:57 +0200 Subject: [PATCH 13/21] add util methods for passing up exceptions in bukkit scheduler --- .../com/alpsbte/plotsystem/utils/Utils.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/main/java/com/alpsbte/plotsystem/utils/Utils.java b/src/main/java/com/alpsbte/plotsystem/utils/Utils.java index 32bf9812..a59997ab 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; @@ -102,6 +105,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() {} From fc403d631f6a130c60826e160eb09341e9ee67f0 Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Thu, 7 Aug 2025 02:53:52 +0200 Subject: [PATCH 14/21] - pass schematic bytes into generateOutlines method of PlotGenerators - measure old generator performance - add new AbstractPlotLoader, DefaultPlotLoader and PlotHandler --- .../plotsystem/core/EventListener.java | 2 +- .../core/menus/companion/CityProjectMenu.java | 57 ++-- .../core/system/plot/PlotHandler.java | 93 ++++++ .../plot/generator/AbstractPlotGenerator.java | 24 +- .../plot/generator/DefaultPlotGenerator.java | 36 ++- .../plot/generator/TutorialPlotGenerator.java | 2 +- .../generator/loader/AbstractPlotLoader.java | 270 ++++++++++++++++++ .../generator/loader/DefaultPlotLoader.java | 82 ++++++ .../generator/world/PlotWorldGenerator.java | 32 ++- .../core/system/plot/utils/PlotUtils.java | 6 +- 10 files changed, 554 insertions(+), 50 deletions(-) create mode 100644 src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java create mode 100644 src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java create mode 100644 src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/DefaultPlotLoader.java diff --git a/src/main/java/com/alpsbte/plotsystem/core/EventListener.java b/src/main/java/com/alpsbte/plotsystem/core/EventListener.java index c117c199..7110f866 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/EventListener.java +++ b/src/main/java/com/alpsbte/plotsystem/core/EventListener.java @@ -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 a705108a..355a649c 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 @@ -9,15 +9,19 @@ 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.PlotHandler; import com.alpsbte.plotsystem.core.system.plot.generator.DefaultPlotGenerator; +import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; 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.ConfigPaths; +import com.alpsbte.plotsystem.utils.io.ConfigUtil; 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; @@ -26,6 +30,7 @@ import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class CityProjectMenu extends AbstractPaginatedMenu { @@ -102,6 +107,34 @@ 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())); + + 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 = PlotHandler.assignPlot(builder, plot); + if (successful) PlotHandler.generatePlot(builder, plot, type); + } + public static boolean generateRandomPlot(Player player, @NotNull List items, PlotDifficulty selectedPlotDifficulty) { PlotDifficulty difficulty = selectedPlotDifficulty; if (items.isEmpty()) { @@ -181,29 +214,9 @@ protected void setPaginatedItemClickEventsAsync(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/system/plot/PlotHandler.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java new file mode 100644 index 00000000..88734796 --- /dev/null +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java @@ -0,0 +1,93 @@ +/* + * 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.plot.generator.loader.DefaultPlotLoader; +import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; +import com.alpsbte.plotsystem.core.system.plot.world.PlotWorld; +import com.alpsbte.plotsystem.utils.Utils; +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.LangPaths; +import com.alpsbte.plotsystem.utils.io.LangUtil; +import org.bukkit.SoundCategory; +import org.bukkit.entity.Player; + +import java.time.LocalDateTime; +import java.util.*; + +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 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)); + } +} 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/AbstractPlotGenerator.java index 2aedf1be..a8dc2f34 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/AbstractPlotGenerator.java @@ -100,28 +100,43 @@ private AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder build PlotSystem.getPlugin().getComponentLogger().info("plotType: " + plotType.getId()); + long initStartTime = System.nanoTime(); if (!init()) return; + PlotSystem.getPlugin().getComponentLogger().info("(APG) Init time: {}ms", (System.nanoTime() - initStartTime) / 1_000_000); + boolean generateWorld = plotType.hasOnePlotPerWorld() || !world.isWorldGenerated(); boolean isWorldLoaded = world.isWorldLoaded(); CompletableFuture.runAsync(() -> { final Exception[] exception = new Exception[]{null}; try { + // Generate and/or load world if (generateWorld) { + long worldGenerationStartTime = System.nanoTime(); new PlotWorldGenerator(world.getWorldName()); + PlotSystem.getPlugin().getComponentLogger().info("(APG) Total PlotWorldGenerator time: {}ms", (System.nanoTime() - worldGenerationStartTime) / 1_000_000); } else if (!isWorldLoaded) { final boolean[] successful = new boolean[1]; Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> successful[0] = world.loadWorld()); if (!successful[0]) throw new Exception("Could not load world"); } + + long schemFetchTime = System.nanoTime(); + // Fetch initial plot schematic + byte[] initialSchematic = plot.getInitialSchematicBytes(); + PlotSystem.getPlugin().getComponentLogger().info("(APG) schem fetching time: {}ms", (System.nanoTime() - schemFetchTime) / 1_000_000); + + long generateOutlinesTime = System.nanoTime(); Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { try { createPlotProtection(); - generateOutlines(); + generateOutlines(initialSchematic); } catch (StorageException | IOException e) { exception[0] = e; } }); + PlotSystem.getPlugin().getComponentLogger().info("(APG) generate outlines time (main thread): {}ms", (System.nanoTime() - generateOutlinesTime) / 1_000_000); + } catch (Exception ex) { exception[0] = ex; } @@ -133,6 +148,7 @@ private AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder build onException(exception[0]); } }); + PlotSystem.getPlugin().getComponentLogger().info("(APG) total time: {}ms", (System.nanoTime() - initStartTime) / 1_000_000); }); } @@ -148,12 +164,12 @@ private AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder build /** * Generates plot schematic and outlines */ - protected void generateOutlines() throws IOException { + protected void generateOutlines(byte[] initialSchematic) throws IOException { if (plotVersion >= 3 && plotType.hasEnvironment()) { - pasteSchematic(null, plot.getInitialSchematicBytes(), world, false); + pasteSchematic(null, initialSchematic, world, false); } else { Mask airMask = new BlockTypeMask(BukkitAdapter.adapt(world.getBukkitWorld()), BlockTypes.AIR); - pasteSchematic(airMask, PlotUtils.getOutlinesSchematicBytes(plot, world.getBukkitWorld()), world, true); + pasteSchematic(airMask, PlotUtils.getOutlinesSchematicBytes(plot, initialSchematic, world.getBukkitWorld()), world, 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 index e95e77c5..997682a1 100644 --- 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 @@ -1,3 +1,27 @@ +/* + * The MIT License (MIT) + * + * Copyright © 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; import com.alpsbte.plotsystem.PlotSystem; @@ -68,15 +92,17 @@ protected boolean init() { } @Override - protected void generateOutlines() throws IOException, WorldEditException { - if (plot instanceof Plot p) { + protected void generateOutlines(byte[] schematic) throws IOException, WorldEditException { + long startTime = System.nanoTime(); + if (!(plot instanceof Plot p)) super.generateOutlines(schematic); + else { byte[] completedSchematic = p.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(); + } else super.generateOutlines(schematic); + } // 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) { @@ -101,6 +127,8 @@ protected void onException(Throwable ex) { } }; } + + PlotSystem.getPlugin().getComponentLogger().info("(DPG) Generate Outlines time: {}ms", (System.nanoTime() - startTime) / 1_000_000); } @Override 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/TutorialPlotGenerator.java index b5662f25..ba9889d0 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/TutorialPlotGenerator.java @@ -36,7 +36,7 @@ protected boolean init() { public void generateOutlines(int schematicId) throws IOException, WorldEditException { ((TutorialPlot) plot).setTutorialSchematic(schematicId); - generateOutlines(); + generateOutlines(getPlot().getInitialSchematicBytes()); } @Override diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java new file mode 100644 index 00000000..d239e980 --- /dev/null +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java @@ -0,0 +1,270 @@ +/* + * 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.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.PlotWorld; +import com.alpsbte.plotsystem.utils.DependencyManager; +import com.alpsbte.plotsystem.utils.Utils; +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.EditSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.BukkitWorld; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; +import com.sk89q.worldedit.function.mask.BlockTypeMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.RegionMask; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector2; +import com.sk89q.worldedit.regions.CylinderRegion; +import com.sk89q.worldedit.regions.Polygonal2DRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.domains.DefaultDomain; +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.regions.ProtectedPolygonalRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.protection.regions.RegionContainer; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; + +import static net.kyori.adventure.text.Component.text; + +public abstract class AbstractPlotLoader { + protected final AbstractPlot plot; + protected final PlotType plotType; + protected final PlotWorld plotWorld; + + protected final Builder builder; + + protected byte[] schematicBytes = null; + + protected AbstractPlotLoader(@NotNull AbstractPlot plot, Builder builder, PlotType plotType, PlotWorld plotWorld) { + this.plot = plot; + this.plotType = plotType; + this.plotWorld = plotWorld; + this.builder = builder; + + 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; + + new PlotWorldGenerator(plotWorld.getWorldName()); + } + + protected void loadWorld() throws Exception { + Utils.runSync(() -> { + if (plotWorld.isWorldLoaded()) return null; + + boolean successful = plotWorld.loadWorld(); + if (!successful) throw new Exception("Could not load world!"); + return null; + }).get(); + } + + protected void fetchSchematicData() { + this.schematicBytes = plot.getInitialSchematicBytes(); + } + + 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; + } + + // Create build region for plot from the outline of the plot + 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 + ProtectedRegion protectedRegion = getProtectedRegion(); + + // Add plot owner + DefaultDomain owner = protectedBuildRegion.getOwners(); + owner.addPlayer(builder.getUUID()); + protectedBuildRegion.setOwners(owner); + + // Set protected build region permissions + setBuildRegionPermissions(protectedBuildRegion); + + // Set protected region permissions + setRegionPermissions(protectedRegion); + + // Add regions and save changes + 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(); + 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; + } + + /** + * Sets the permissions for the plot build region only + * + * @param region build region + */ + protected void setBuildRegionPermissions(@NotNull ProtectedRegion region) { + region.setFlag(Flags.BUILD, StateFlag.State.ALLOW); + region.setFlag(Flags.BUILD.getRegionGroupFlag(), RegionGroup.OWNERS); + if (DependencyManager.isWorldGuardExtraFlagsEnabled()) + region.setFlag(new StateFlag("worldedit", true, RegionGroup.OWNERS), StateFlag.State.ALLOW); + } + + /** + * Sets the permissions for the whole plot region + * + * @param region plot region + */ + protected void setRegionPermissions(@NotNull ProtectedRegion region) { + region.setFlag(Flags.ENTRY, StateFlag.State.ALLOW); + region.setFlag(Flags.ENTRY.getRegionGroupFlag(), RegionGroup.ALL); + + FileConfiguration config = ConfigUtil.getInstance().configs[1]; + region.setFlag(Flags.BLOCKED_CMDS, new HashSet<>(getBlockedCommands(config))); + region.setFlag(Flags.BLOCKED_CMDS.getRegionGroupFlag(), RegionGroup.ALL); + } + + /** + * Reads the blocked commands for the plot region from the config + * + * @param config commands.yml config + * @return list of blocked commands + */ + protected List getBlockedCommands(@NotNull FileConfiguration config) { + List blockedCommands = config.getStringList(ConfigPaths.BLOCKED_COMMANDS_BUILDERS); + blockedCommands.removeIf(c -> c.equals("/cmd1")); + return blockedCommands; + } + + 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(); + } + + public static void pasteSchematic(@Nullable Mask pasteMask, byte[] schematicFile, @NotNull PlotWorld world, boolean clearArea) throws IOException { + // load world if not loaded already + if (!world.loadWorld()) return; + World weWorld = new BukkitWorld(world.getBukkitWorld()); + + // set outline region with air + if (clearArea) { + try (EditSession editSession = WorldEdit.getInstance().newEditSession(BukkitAdapter.adapt(world.getBukkitWorld()))) { + Polygonal2DRegion polyRegion = new Polygonal2DRegion(weWorld, world.getPlot().getOutline(), 0, PlotWorld.MAX_WORLD_HEIGHT); + editSession.setMask(new RegionMask(polyRegion)); + editSession.setBlocks((Region) polyRegion, Objects.requireNonNull(BlockTypes.AIR).getDefaultState()); + } + } + + // load schematic + Clipboard clipboard; + ByteArrayInputStream inputStream = new ByteArrayInputStream(schematicFile); + try (ClipboardReader reader = AbstractPlot.CLIPBOARD_FORMAT.getReader(inputStream)) { + clipboard = reader.read(); + } + + // paste schematic + try (EditSession editSession = WorldEdit.getInstance().newEditSession(BukkitAdapter.adapt(world.getBukkitWorld()))) { + if (pasteMask != null) editSession.setMask(pasteMask); + Operation clipboardHolder = new ClipboardHolder(clipboard) + .createPaste(editSession) + .to(BlockVector3.at(world.getPlot().getCenter().x(), world.getPlotHeight(), world.getPlot().getCenter().z())) + .build(); + Operations.complete(clipboardHolder); + } + } + + protected void onException(Exception e) { + // TODO: abandon 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..4e581177 --- /dev/null +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/DefaultPlotLoader.java @@ -0,0 +1,82 @@ +/* + * 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.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; + +public class DefaultPlotLoader extends AbstractPlotLoader { + public DefaultPlotLoader(@NotNull AbstractPlot plot, Builder builder, PlotType plotType, PlotWorld plotWorld) { + super(plot, builder, plotType, plotWorld); + } + + @Override + protected void generateStructure() throws Exception { + if (!(plot instanceof Plot p)) super.generateStructure(); + else { + 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() { + 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 + // TODO: generate plot on city world + // TODO: unload CIM world when done + } +} 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 index 1315c3f3..4812153e 100644 --- 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 @@ -26,6 +26,7 @@ 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; @@ -59,23 +60,24 @@ public class PlotWorldGenerator { private static final World.Environment environment = World.Environment.NORMAL; private static final WorldType worldType = WorldType.FLAT; - private World world = null; - public PlotWorldGenerator(String worldName) throws Exception { + long startTime = System.nanoTime(); this.worldName = worldName; + + // Async Part generateWorld(); - final Exception[] exception = {null}; - Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { - try { - createMultiverseWorld(); - configureWorld(); - createGlobalProtection(); - } catch (Exception e) { - exception[0] = e; - } - }); - if (exception[0] != null) throw exception[0]; + // 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 { @@ -116,8 +118,8 @@ protected void createMultiverseWorld() { } protected void configureWorld() { - this.world = Bukkit.getWorld(worldName); - assert this.world != null; + World world = Bukkit.getWorld(worldName); + assert world != null; Option mvWorld = worldManager.getLoadedWorld(worldName); // Configure multiverse world 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 45905dfa..4960432f 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 @@ -164,9 +164,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(); } @@ -388,7 +388,7 @@ public static boolean abandonPlot(@NotNull AbstractPlot plot) { 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); + AbstractPlotGenerator.pasteSchematic(null, getOutlinesSchematicBytes(plot, plot.getInitialSchematicBytes(), world.getBukkitWorld()), world, true); } else PlotSystem.getPlugin().getComponentLogger().warn(text("Region Manager is null!")); playersToTeleport.forEach(p -> p.teleport(Utils.getSpawnLocation())); From 35931a8741a7d1ac216eac2057e1779692d3f386 Mon Sep 17 00:00:00 2001 From: Zoriot Date: Mon, 25 Aug 2025 03:49:03 +0200 Subject: [PATCH 15/21] =?UTF-8?q?feat(gen):=20=E2=9C=A8=20update=20emptych?= =?UTF-8?q?unkgenerator=20and=20provide=20it=20outside=20of=20plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/alpsbte/plotsystem/PlotSystem.java | 6 ++++++ .../generator/world/SkeletonWorldGenerator.java | 14 +++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java index 94c41885..3881b1d6 100644 --- a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java +++ b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java @@ -29,6 +29,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; @@ -204,4 +205,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/core/system/plot/generator/world/SkeletonWorldGenerator.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/world/SkeletonWorldGenerator.java index 7ced0a65..acf97e31 100644 --- 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 @@ -25,12 +25,12 @@ package com.alpsbte.plotsystem.core.system.plot.generator.world; import net.kyori.adventure.util.TriState; -import org.bukkit.*; +import org.bukkit.GameRule; +import org.bukkit.World; +import org.bukkit.WorldCreator; +import org.bukkit.WorldType; import org.bukkit.generator.ChunkGenerator; -import javax.annotation.Nonnull; -import java.util.Random; - public class SkeletonWorldGenerator { public static final String WORLD_NAME = "Skeleton"; private static final World.Environment ENVIRONMENT = World.Environment.NORMAL; @@ -80,10 +80,6 @@ protected void saveWorld() { } 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); - } + // It should just do nothing } } From 91e97ad3594712a28fa092e2455533756a2c04a6 Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Sat, 1 Nov 2025 17:29:19 +0100 Subject: [PATCH 16/21] Implement plot abandon functionality in the PlotHandler and rework Plot Worlds to handle world specific abandon logic --- .../com/alpsbte/plotsystem/PlotSystem.java | 5 +- .../commands/admin/CMD_DeletePlot.java | 11 +- .../commands/plot/CMD_Plot_Abandon.java | 8 +- .../commands/plot/CMD_Plot_Submit.java | 3 +- .../commands/plot/CMD_Plot_UndoSubmit.java | 3 +- .../menus/review/ReviewPlotTogglesMenu.java | 3 +- .../core/system/plot/PlotHandler.java | 108 +++++++++++ .../plot/generator/AbstractPlotGenerator.java | 3 +- .../core/system/plot/utils/PlotUtils.java | 182 ++---------------- .../core/system/plot/world/CityPlotWorld.java | 71 +++++-- .../core/system/plot/world/IWorld.java | 18 ++ .../core/system/plot/world/OnePlotWorld.java | 60 +++--- .../core/system/plot/world/PlotWorld.java | 19 +- 13 files changed, 270 insertions(+), 224 deletions(-) diff --git a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java index 3881b1d6..7f2f77a3 100644 --- a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java +++ b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java @@ -12,6 +12,7 @@ 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; @@ -119,10 +120,12 @@ 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)); 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 0a9f0fbb..b1abfd10 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 0dfc8834..82a48421 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_UndoSubmit.java b/src/main/java/com/alpsbte/plotsystem/commands/plot/CMD_Plot_UndoSubmit.java index d73e2006..5f556948 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/menus/review/ReviewPlotTogglesMenu.java b/src/main/java/com/alpsbte/plotsystem/core/menus/review/ReviewPlotTogglesMenu.java index 68e96739..d1a712b2 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,6 +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.PlotHandler; import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; import com.alpsbte.plotsystem.core.system.review.PlotReview; import com.alpsbte.plotsystem.core.system.review.ReviewRating; @@ -132,7 +133,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(), () -> { 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 index 88734796..8621fead 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java @@ -29,6 +29,9 @@ import com.alpsbte.plotsystem.core.system.Builder; 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.Slot; @@ -36,11 +39,18 @@ import com.alpsbte.plotsystem.utils.io.ConfigPaths; import com.alpsbte.plotsystem.utils.io.LangPaths; import com.alpsbte.plotsystem.utils.io.LangUtil; +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.time.LocalDate; import java.time.LocalDateTime; import java.util.*; +import java.util.concurrent.CompletableFuture; + +import static net.kyori.adventure.text.Component.text; public class PlotHandler { private PlotHandler() {} @@ -90,4 +100,102 @@ public static void generatePlot(Builder builder, Plot plot, PlotType type) { 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()); + } + } + } } 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/AbstractPlotGenerator.java index a8dc2f34..bd85fb4e 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/AbstractPlotGenerator.java @@ -4,6 +4,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.generator.world.PlotWorldGenerator; import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; import com.alpsbte.plotsystem.core.system.plot.utils.PlotUtils; @@ -144,7 +145,7 @@ private AbstractPlotGenerator(@NotNull AbstractPlot plot, @NotNull Builder build this.onComplete(exception[0] != null, false); if (exception[0] != null) { - PlotUtils.Actions.abandonPlot(plot); + PlotHandler.abandonPlot(plot); onException(exception[0]); } }); 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 4960432f..45f1d2ed 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,8 @@ 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; @@ -38,17 +36,13 @@ 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,8 +56,6 @@ 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; @@ -137,9 +129,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; @@ -278,39 +271,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); @@ -330,119 +301,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, plot.getInitialSchematicBytes(), 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() {} @@ -524,19 +382,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 1dcd22bf..4110f815 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); @@ -30,29 +39,29 @@ public boolean teleportPlayer(@NotNull Player player) { player.setAllowFlight(true); player.setFlying(true); - if (getPlot() == null) return true; + if (plot == null) return true; - player.sendMessage(Utils.ChatUtils.getInfoFormat(LangUtil.getInstance().get(player, LangPaths.Message.Info.TELEPORTING_PLOT, String.valueOf(getPlot().getID())))); + player.sendMessage(Utils.ChatUtils.getInfoFormat(LangUtil.getInstance().get(player, LangPaths.Message.Info.TELEPORTING_PLOT, String.valueOf(plot.getID())))); Utils.updatePlayerInventorySlots(player); - PlotUtils.ChatFormatting.sendLinkMessages(getPlot(), player); + PlotUtils.ChatFormatting.sendLinkMessages(plot, 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; } @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 @@ -70,7 +79,7 @@ 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(); } @@ -99,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..12fdc078 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,5 +1,6 @@ package com.alpsbte.plotsystem.core.system.plot.world; +import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; import com.alpsbte.plotsystem.core.system.plot.generator.AbstractPlotGenerator; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldguard.protection.regions.ProtectedRegion; @@ -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 @@ -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 8ea21905..08163e63 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 @@ -8,6 +8,7 @@ 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.DefaultPlotLoader; 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,9 +23,11 @@ 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(); } @@ -33,36 +36,25 @@ public boolean generateWorld(@NotNull Class if (isWorldGenerated()) return false; if (generator.isAssignableFrom(DefaultPlotGenerator.class)) { - new DefaultPlotGenerator(getPlot(), plotOwner); + new DefaultPlotGenerator(plot, plotOwner); } else if (generator.isAssignableFrom(TutorialPlotGenerator.class)) { - new TutorialPlotGenerator(getPlot(), plotOwner); + new TutorialPlotGenerator(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) + if (plot.getPlotType() == PlotType.TUTORIAL || ((Plot) plot).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); - } - }; + 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 +63,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 +77,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 +99,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 162a4d59..ec27b98e 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 @@ -68,7 +68,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; @@ -208,6 +208,16 @@ public boolean isWorldGenerated() { return mvCore.getWorldManager().getWorld(worldName).isDefined(); } + @Override + public AbstractPlot getPlot() { + return plot; + } + + @Override + public boolean onAbandon() { + return true; + } + private @Nullable ProtectedRegion getRegion(String regionName) { if (!loadWorld()) return null; @@ -219,11 +229,6 @@ public boolean isWorldGenerated() { return null; } - public AbstractPlot getPlot() { - return plot; - } - - /** * @param worldName - the name of the world * @return - true if the world is a plot world @@ -248,6 +253,7 @@ public static boolean isCityPlotWorld(@NotNull String worldName) { * @return - plot world */ public static @Nullable PlotWorld getPlotWorldByName(String worldName) { + // TODO: rework if (!isOnePlotWorld(worldName) && !isCityPlotWorld(worldName)) return null; Integer id = AlpsUtils.tryParseInt(worldName.substring(2)); @@ -258,6 +264,7 @@ public static boolean isCityPlotWorld(@NotNull String worldName) { } 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); } From 73db5729480a89571b5d3506ad090d2bb67fc651 Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Sat, 1 Nov 2025 17:59:48 +0100 Subject: [PATCH 17/21] fix incorrectly rebased imports --- .../core/system/plot/generator/world/PlotWorldGenerator.java | 1 + .../com/alpsbte/plotsystem/core/system/plot/world/PlotWorld.java | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) 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 index 4812153e..40220f2b 100644 --- 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 @@ -41,6 +41,7 @@ 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; 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 ec27b98e..3b94a713 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 @@ -35,7 +35,6 @@ import com.alpsbte.plotsystem.utils.DependencyManager; import com.alpsbte.plotsystem.utils.Utils; import com.alpsbte.plotsystem.utils.io.ConfigPaths; -import com.onarandombox.MultiverseCore.MultiverseCore; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; From a53c9f76fdd2bed838522cc037246893b43b980d Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Tue, 25 Nov 2025 13:42:33 +0100 Subject: [PATCH 18/21] Implement TutorialPlotLoader.java. move savePlotAsSchematic to PlotHandler. create methods for both assigning and generating in PlotHandler. remove all old PlotGenerator files and replace every usage with the new PlotLoaders --- .../commands/plot/CMD_Plot_Teleport.java | 4 +- .../plotsystem/core/EventListener.java | 4 +- .../core/menus/companion/CityProjectMenu.java | 16 +- .../menus/review/ReviewPlotTogglesMenu.java | 5 +- .../core/system/plot/PlotHandler.java | 98 ++++++ .../plot/generator/AbstractPlotGenerator.java | 333 ------------------ .../plot/generator/DefaultPlotGenerator.java | 148 -------- .../generator/loader/AbstractPlotLoader.java | 3 +- .../generator/loader/DefaultPlotLoader.java | 39 +- .../TutorialPlotLoader.java} | 35 +- .../core/system/plot/utils/PlotUtils.java | 58 --- .../core/system/plot/world/IWorld.java | 6 +- .../core/system/plot/world/OnePlotWorld.java | 17 +- .../core/system/plot/world/PlotWorld.java | 6 +- .../system/tutorial/AbstractPlotTutorial.java | 15 +- .../core/system/tutorial/PlotTutorial.java | 3 +- .../stage/tasks/PlotSchematicPasteTask.java | 4 +- 17 files changed, 174 insertions(+), 620 deletions(-) delete mode 100644 src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/AbstractPlotGenerator.java delete mode 100644 src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/DefaultPlotGenerator.java rename src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/{TutorialPlotGenerator.java => loader/TutorialPlotLoader.java} (70%) 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/core/EventListener.java b/src/main/java/com/alpsbte/plotsystem/core/EventListener.java index 7110f866..cd7fa485 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()); 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 355a649c..bb612808 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 @@ -10,7 +10,6 @@ import com.alpsbte.plotsystem.core.system.Country; import com.alpsbte.plotsystem.core.system.plot.Plot; import com.alpsbte.plotsystem.core.system.plot.PlotHandler; -import com.alpsbte.plotsystem.core.system.plot.generator.DefaultPlotGenerator; import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; import com.alpsbte.plotsystem.utils.Utils; import com.alpsbte.plotsystem.utils.enums.PlotDifficulty; @@ -126,13 +125,7 @@ public void handleCityProjectClick(Player player, CityProject city) { } Plot plot = unclaimedPlots.get(Utils.getRandom().nextInt(unclaimedPlots.size())); - - 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 = PlotHandler.assignPlot(builder, plot); - if (successful) PlotHandler.generatePlot(builder, plot, type); + PlotHandler.assignAndGeneratePlot(builder, plot); } public static boolean generateRandomPlot(Player player, @NotNull List items, PlotDifficulty selectedPlotDifficulty) { @@ -147,14 +140,13 @@ public static boolean generateRandomPlot(Player player, @NotNull List { 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/PlotHandler.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java index 8621fead..544fbca3 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java @@ -27,6 +27,8 @@ 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; @@ -34,21 +36,41 @@ 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; @@ -81,6 +103,23 @@ public static boolean assignPlot(Builder builder, Plot plot) { 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(); @@ -198,4 +237,63 @@ public static void undoSubmit(@NotNull Plot plot) { } } } + + 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/AbstractPlotGenerator.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/AbstractPlotGenerator.java deleted file mode 100644 index bd85fb4e..00000000 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/AbstractPlotGenerator.java +++ /dev/null @@ -1,333 +0,0 @@ -package com.alpsbte.plotsystem.core.system.plot.generator; - -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.PlotWorld; -import com.alpsbte.plotsystem.utils.DependencyManager; -import com.alpsbte.plotsystem.utils.Utils; -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.EditSession; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.bukkit.BukkitWorld; -import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; -import com.sk89q.worldedit.function.mask.BlockTypeMask; -import com.sk89q.worldedit.function.mask.Mask; -import com.sk89q.worldedit.function.mask.RegionMask; -import com.sk89q.worldedit.function.operation.Operation; -import com.sk89q.worldedit.function.operation.Operations; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.math.Vector2; -import com.sk89q.worldedit.regions.CylinderRegion; -import com.sk89q.worldedit.regions.Polygonal2DRegion; -import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.session.ClipboardHolder; -import com.sk89q.worldedit.world.World; -import com.sk89q.worldedit.world.block.BlockTypes; -import com.sk89q.worldguard.WorldGuard; -import com.sk89q.worldguard.domains.DefaultDomain; -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.ProtectedPolygonalRegion; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import com.sk89q.worldguard.protection.regions.RegionContainer; -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.FileConfiguration; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; - -import static net.kyori.adventure.text.Component.text; - -public abstract class AbstractPlotGenerator { - protected final AbstractPlot plot; - private final Builder builder; - protected final PlotWorld world; - protected final double plotVersion; - protected final PlotType plotType; - /** - * 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()); - } - - /** - * 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, PlotWorld.getByType(plotType, (Plot) plot)); - } - - /** - * 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) { - this.plot = plot; - this.builder = builder; - this.world = world; - this.plotVersion = plot.getVersion(); - this.plotType = plotType; - - PlotSystem.getPlugin().getComponentLogger().info("plotType: " + plotType.getId()); - - long initStartTime = System.nanoTime(); - if (!init()) return; - PlotSystem.getPlugin().getComponentLogger().info("(APG) Init time: {}ms", (System.nanoTime() - initStartTime) / 1_000_000); - - boolean generateWorld = plotType.hasOnePlotPerWorld() || !world.isWorldGenerated(); - boolean isWorldLoaded = world.isWorldLoaded(); - - CompletableFuture.runAsync(() -> { - final Exception[] exception = new Exception[]{null}; - try { - // Generate and/or load world - if (generateWorld) { - long worldGenerationStartTime = System.nanoTime(); - new PlotWorldGenerator(world.getWorldName()); - PlotSystem.getPlugin().getComponentLogger().info("(APG) Total PlotWorldGenerator time: {}ms", (System.nanoTime() - worldGenerationStartTime) / 1_000_000); - } else if (!isWorldLoaded) { - final boolean[] successful = new boolean[1]; - Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> successful[0] = world.loadWorld()); - if (!successful[0]) throw new Exception("Could not load world"); - } - - long schemFetchTime = System.nanoTime(); - // Fetch initial plot schematic - byte[] initialSchematic = plot.getInitialSchematicBytes(); - PlotSystem.getPlugin().getComponentLogger().info("(APG) schem fetching time: {}ms", (System.nanoTime() - schemFetchTime) / 1_000_000); - - long generateOutlinesTime = System.nanoTime(); - Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { - try { - createPlotProtection(); - generateOutlines(initialSchematic); - } catch (StorageException | IOException e) { - exception[0] = e; - } - }); - PlotSystem.getPlugin().getComponentLogger().info("(APG) generate outlines time (main thread): {}ms", (System.nanoTime() - generateOutlinesTime) / 1_000_000); - - } catch (Exception ex) { - exception[0] = ex; - } - Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { - this.onComplete(exception[0] != null, false); - - if (exception[0] != null) { - PlotHandler.abandonPlot(plot); - onException(exception[0]); - } - }); - PlotSystem.getPlugin().getComponentLogger().info("(APG) total time: {}ms", (System.nanoTime() - initStartTime) / 1_000_000); - }); - } - - - /** - * Executed before plot generation - * - * @return true if initialization was successful - */ - protected abstract boolean init(); - - - /** - * Generates plot schematic and outlines - */ - protected void generateOutlines(byte[] initialSchematic) throws IOException { - if (plotVersion >= 3 && plotType.hasEnvironment()) { - pasteSchematic(null, initialSchematic, world, false); - } else { - Mask airMask = new BlockTypeMask(BukkitAdapter.adapt(world.getBukkitWorld()), BlockTypes.AIR); - pasteSchematic(airMask, PlotUtils.getOutlinesSchematicBytes(plot, initialSchematic, world.getBukkitWorld()), world, true); - } - } - - - /** - * Creates plot protection - */ - protected void createPlotProtection() throws StorageException { - RegionContainer regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer(); - RegionManager regionManager = regionContainer.get(BukkitAdapter.adapt(world.getBukkitWorld())); - - 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); - 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); - - // Add plot owner - DefaultDomain owner = protectedBuildRegion.getOwners(); - owner.addPlayer(builder.getUUID()); - protectedBuildRegion.setOwners(owner); - - // Set protected build region permissions - setBuildRegionPermissions(protectedBuildRegion); - - // Set protected region permissions - 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"); - regionManager.addRegion(protectedBuildRegion); - regionManager.addRegion(protectedRegion); - regionManager.saveChanges(); - } else PlotSystem.getPlugin().getComponentLogger().warn(text("Region Manager is null!")); - } - - /** - * Sets the permissions for the plot build region only - * - * @param region build region - */ - protected void setBuildRegionPermissions(@NotNull ProtectedRegion region) { - region.setFlag(Flags.BUILD, StateFlag.State.ALLOW); - region.setFlag(Flags.BUILD.getRegionGroupFlag(), RegionGroup.OWNERS); - if (DependencyManager.isWorldGuardExtraFlagsEnabled()) - region.setFlag(new StateFlag("worldedit", true, RegionGroup.OWNERS), StateFlag.State.ALLOW); - } - - /** - * Sets the permissions for the whole plot region - * - * @param region plot region - */ - protected void setRegionPermissions(@NotNull ProtectedRegion region) { - region.setFlag(Flags.ENTRY, StateFlag.State.ALLOW); - region.setFlag(Flags.ENTRY.getRegionGroupFlag(), RegionGroup.ALL); - - FileConfiguration config = ConfigUtil.getInstance().configs[1]; - region.setFlag(Flags.BLOCKED_CMDS, new HashSet<>(getBlockedCommands(config))); - region.setFlag(Flags.BLOCKED_CMDS.getRegionGroupFlag(), RegionGroup.ALL); - } - - /** - * Reads the blocked commands for the plot region from the config - * - * @param config commands.yml config - * @return list of blocked commands - */ - protected List getBlockedCommands(@NotNull FileConfiguration config) { - List blockedCommands = config.getStringList(ConfigPaths.BLOCKED_COMMANDS_BUILDERS); - blockedCommands.removeIf(c -> c.equals("/cmd1")); - 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; - } - - - /** - * 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 - if (!world.loadWorld()) return; - World weWorld = new BukkitWorld(world.getBukkitWorld()); - - // set outline region with air - if (clearArea) { - try (EditSession editSession = WorldEdit.getInstance().newEditSession(BukkitAdapter.adapt(world.getBukkitWorld()))) { - Polygonal2DRegion polyRegion = new Polygonal2DRegion(weWorld, world.getPlot().getOutline(), 0, PlotWorld.MAX_WORLD_HEIGHT); - editSession.setMask(new RegionMask(polyRegion)); - editSession.setBlocks((Region) polyRegion, Objects.requireNonNull(BlockTypes.AIR).getDefaultState()); - } - } - - // load schematic - Clipboard clipboard; - ByteArrayInputStream inputStream = new ByteArrayInputStream(schematicFile); - try (ClipboardReader reader = AbstractPlot.CLIPBOARD_FORMAT.getReader(inputStream)) { - clipboard = reader.read(); - } - - // paste schematic - try (EditSession editSession = WorldEdit.getInstance().newEditSession(BukkitAdapter.adapt(world.getBukkitWorld()))) { - if (pasteMask != null) editSession.setMask(pasteMask); - Operation clipboardHolder = new ClipboardHolder(clipboard) - .createPaste(editSession) - .to(BlockVector3.at(world.getPlot().getCenter().x(), world.getPlotHeight(), world.getPlot().getCenter().z())) - .build(); - Operations.complete(clipboardHolder); - } - } -} 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 997682a1..00000000 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/DefaultPlotGenerator.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright © 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; - -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) { - 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; - } - - if (DefaultPlotGenerator.playerPlotGenerationHistory.containsKey(getBuilder().getUUID())) { - if (!DefaultPlotGenerator.playerPlotGenerationHistory.get(getBuilder().getUUID()).isBefore(LocalDateTime.now().minusSeconds(10))) { - 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.remove(getBuilder().getUUID()); - } - - 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; - } - - @Override - protected void generateOutlines(byte[] schematic) throws IOException, WorldEditException { - long startTime = System.nanoTime(); - if (!(plot instanceof Plot p)) super.generateOutlines(schematic); - else { - byte[] completedSchematic = p.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(schematic); - } - - // 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); - } - }; - } - - PlotSystem.getPlugin().getComponentLogger().info("(DPG) Generate Outlines time: {}ms", (System.nanoTime() - startTime) / 1_000_000); - } - - @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/loader/AbstractPlotLoader.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java index d239e980..7681a699 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java @@ -27,6 +27,7 @@ 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.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; @@ -259,7 +260,7 @@ public static void pasteSchematic(@Nullable Mask pasteMask, byte[] schematicFile } protected void onException(Exception e) { - // TODO: abandon plot + 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))); 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 index 4e581177..feafcc26 100644 --- 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 @@ -29,6 +29,7 @@ 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; @@ -41,25 +42,36 @@ 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(); - else { - 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(); + 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(); } @@ -71,12 +83,13 @@ protected void onCompletion() { }); } - protected void copyToCityWorld() { + 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 - // TODO: generate plot on city world - // TODO: unload CIM world when done + 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 70% 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 ba9889d0..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(getPlot().getInitialSchematicBytes()); + 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/utils/PlotUtils.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/utils/PlotUtils.java index 45f1d2ed..bb17afd3 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,8 +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.generator.AbstractPlotGenerator; -import com.alpsbte.plotsystem.core.system.plot.world.CityPlotWorld; 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; @@ -21,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; @@ -33,7 +29,6 @@ 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.protection.regions.ProtectedRegion; @@ -60,7 +55,6 @@ 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; @@ -193,58 +187,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; 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 12fdc078..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,7 +1,7 @@ package com.alpsbte.plotsystem.core.system.plot.world; import com.alpsbte.plotsystem.core.system.plot.AbstractPlot; -import com.alpsbte.plotsystem.core.system.plot.generator.AbstractPlotGenerator; +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; @@ -21,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 @@ -29,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 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 72de7d8c..ca3563c1 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 @@ -29,10 +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; @@ -56,13 +55,13 @@ public OnePlotWorld(@NotNull AbstractPlot plot) { } @Override - public boolean generateWorld(@NotNull Class generator) { + public boolean generateWorld(@NotNull Class generator) { if (isWorldGenerated()) return false; - if (generator.isAssignableFrom(DefaultPlotGenerator.class)) { - new DefaultPlotGenerator(plot, plotOwner); - } else if (generator.isAssignableFrom(TutorialPlotGenerator.class)) { - new TutorialPlotGenerator(plot, 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; } @@ -73,7 +72,7 @@ public boolean loadWorld() { // Generate plot if it doesn't exist if (plot.getPlotType() == PlotType.TUTORIAL || ((Plot) plot).getCompletedSchematic() == null) - generateWorld(TutorialPlotGenerator.class); + generateWorld(TutorialPlotLoader.class); new DefaultPlotLoader(plot, plotOwner, plot.getPlotType(), this); 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 3b94a713..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 @@ -30,7 +30,7 @@ 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; @@ -75,12 +75,12 @@ 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); } 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 4a275a7d..f977afbc 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; } @@ -147,11 +146,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; } From 17a83765862a7c036dd5fec7e768f28f01fa1822 Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Tue, 25 Nov 2025 13:43:29 +0100 Subject: [PATCH 19/21] remove unused imports --- .../plotsystem/core/menus/companion/CityProjectMenu.java | 2 -- .../plotsystem/core/menus/review/ReviewPlotTogglesMenu.java | 1 - 2 files changed, 3 deletions(-) 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 bb612808..574a35bd 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 @@ -10,12 +10,10 @@ import com.alpsbte.plotsystem.core.system.Country; import com.alpsbte.plotsystem.core.system.plot.Plot; import com.alpsbte.plotsystem.core.system.plot.PlotHandler; -import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; 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.ConfigPaths; -import com.alpsbte.plotsystem.utils.io.ConfigUtil; import com.alpsbte.plotsystem.utils.io.LangPaths; import com.alpsbte.plotsystem.utils.io.LangUtil; import com.alpsbte.plotsystem.utils.items.MenuItems; 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 24b53a93..6d9ee902 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 @@ -8,7 +8,6 @@ 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.utils.PlotUtils; import com.alpsbte.plotsystem.core.system.review.PlotReview; import com.alpsbte.plotsystem.core.system.review.ReviewRating; import com.alpsbte.plotsystem.core.system.review.ToggleCriteria; From 3ca8cb7372c9bba44c08d36ae1b85e2f8d443448 Mon Sep 17 00:00:00 2001 From: cinnazeyy Date: Tue, 2 Dec 2025 22:58:08 +0100 Subject: [PATCH 20/21] add nbt dependency to gradle for parity with maven --- build.gradle.kts | 1 + gradle/libs.versions.toml | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index f7dd808c..a22ef494 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" } From 4a9a6cea4aa00e50543f53ddeb6998fb5a14630b Mon Sep 17 00:00:00 2001 From: cinnazeyy Date: Wed, 3 Dec 2025 00:17:18 +0100 Subject: [PATCH 21/21] fix merging issues --- .../core/system/plot/PlotHandler.java | 22 +++++++++---------- .../plot/generator/DefaultPlotGenerator.java | 0 .../generator/loader/AbstractPlotLoader.java | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/DefaultPlotGenerator.java 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 index 544fbca3..c0230592 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/PlotHandler.java @@ -98,7 +98,7 @@ public static boolean assignPlot(Builder builder, Plot plot) { } // Assign - if (!builder.setSlot(builder.getFreeSlot(), plot.getID())) return false; + if (!builder.setSlot(builder.getFreeSlot(), plot.getId())) return false; if (!plot.setStatus(Status.unfinished)) return false; return plot.setPlotOwner(builder); } @@ -144,7 +144,7 @@ public static void generatePlot(Builder builder, Plot plot, PlotType type) { 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() + "!")); + PlotSystem.getPlugin().getComponentLogger().error(text("Failed to abandon plot with the ID " + plot.getId() + "!")); return false; } @@ -152,7 +152,7 @@ public static boolean abandonPlot(AbstractPlot plot) { if (plot.getPlotType() == PlotType.TUTORIAL) return; Plot dPlot = (Plot) plot; boolean successful; - successful = DataProvider.REVIEW.removeAllReviewsOfPlot(dPlot.getID()); + successful = DataProvider.REVIEW.removeAllReviewsOfPlot(dPlot.getId()); for (Builder builder : dPlot.getPlotMembers()) { if (!successful) break; @@ -171,20 +171,20 @@ public static boolean abandonPlot(AbstractPlot plot) { && 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() + "!")); + 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() + "!")); + 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!")); + 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; } @@ -202,10 +202,10 @@ public static void abandonInactivePlots() { Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { if (!abandonPlot(plot)) { - PlotSystem.getPlugin().getComponentLogger().warn(text("An error occurred while abandoning plot #" + plot.getID() + " due to inactivity!")); + 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!")); + PlotSystem.getPlugin().getComponentLogger().info(text("Abandoned plot #" + plot.getId() + " due to inactivity!")); }); } } @@ -284,7 +284,7 @@ public static boolean savePlotAsSchematic(@NotNull Plot plot) throws IOException } // Set Completed Schematic - boolean successful = DataProvider.PLOT.setCompletedSchematic(plot.getID(), outputStream.toByteArray()); + 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 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 e69de29b..00000000 diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java index 7681a699..7b5946f8 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/AbstractPlotLoader.java @@ -93,7 +93,7 @@ protected AbstractPlotLoader(@NotNull AbstractPlot plot, Builder builder, PlotTy this.plotWorld = plotWorld; this.builder = builder; - PlotSystem.getPlugin().getComponentLogger().info("Loading plot #{}...", plot.getID()); + PlotSystem.getPlugin().getComponentLogger().info("Loading plot #{}...", plot.getId()); PlotSystem.getPlugin().getComponentLogger().info("Plot Type: {}", plot.getPlotType().name()); boolean successful = true;