From 77f41e2a95ad1ea8fc14f3aff1a4c3ceaacf0d0c Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Mon, 14 Jul 2025 04:17:26 +0200 Subject: [PATCH 01/39] 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/39] 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/39] 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/39] 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/39] 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/39] - 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/39] =?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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] - 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/39] =?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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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; From ac9a97a41545f48ea2532d46ff45366f36713ad8 Mon Sep 17 00:00:00 2001 From: Zoriot Date: Mon, 16 Feb 2026 11:22:01 +0100 Subject: [PATCH 22/39] =?UTF-8?q?feat:=20=E2=9C=A8=20Remove=20VoidGen=20de?= =?UTF-8?q?pendency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/plot/generator/world/PlotWorldGenerator.java | 8 +++++--- src/main/resources/plugin.yml | 3 +-- 2 files changed, 6 insertions(+), 5 deletions(-) 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 024253f3..b721c445 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 @@ -40,7 +40,10 @@ import net.querz.nbt.io.NamedTag; import net.querz.nbt.tag.CompoundTag; import org.apache.commons.io.FileUtils; -import org.bukkit.*; +import org.bukkit.Bukkit; +import org.bukkit.Difficulty; +import org.bukkit.GameMode; +import org.bukkit.World; import org.bukkit.entity.SpawnCategory; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; import org.mvplugins.multiverse.core.world.WorldManager; @@ -58,7 +61,6 @@ public class PlotWorldGenerator { private final WorldManager worldManager = DependencyManager.getMultiverseCore().getWorldManager(); private final String worldName; private static final World.Environment environment = World.Environment.NORMAL; - private static final WorldType worldType = WorldType.FLAT; public PlotWorldGenerator(String worldName) throws Exception { long startTime = System.nanoTime(); @@ -111,7 +113,7 @@ protected void createMultiverseWorld() { if (!worldManager.isLoadedWorld(worldName)) { worldManager.importWorld(ImportWorldOptions.worldName(worldName) .environment(environment) - .generator("VoidGen:{\"caves\":false,\"decoration\":false,\"mobs\":false,\"structures\":false}") + .generator("Plot-System") .useSpawnAdjust(false) ); } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index a9a177fd..8b50ac2b 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -4,8 +4,7 @@ api-version: "1.21" name: Plot-System description: "${description}" author: R3tuxn, Cinnazeyy & Zoriot -depend: [ LangLibs, FancyNpcs, VoidGen, FastAsyncWorldEdit, Multiverse-Core, - DecentHolograms, WorldGuard, HeadDatabase ] +depend: [ LangLibs, FancyNpcs, FastAsyncWorldEdit, Multiverse-Core, DecentHolograms, WorldGuard, HeadDatabase ] softdepend: [ WorldGuardExtraFlags, ParticleNativeAPI ] commands: From f3cfdfc447e900c889c2f55697c025d6aac5d393 Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Wed, 8 Apr 2026 17:16:13 +0200 Subject: [PATCH 23/39] fix merge conflicts and use new GameRules api --- .../core/system/plot/PlotHandler.java | 11 +++++---- .../generator/loader/AbstractPlotLoader.java | 6 ++--- .../generator/loader/DefaultPlotLoader.java | 4 ++-- .../world/SkeletonWorldGenerator.java | 23 ++++++++----------- .../core/system/plot/world/CityPlotWorld.java | 2 +- 5 files changed, 23 insertions(+), 23 deletions(-) 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 c0230592..2f563b82 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 @@ -44,11 +44,14 @@ 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.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.mask.BlockTypeMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.ForwardExtentCopy; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.math.BlockVector2; @@ -57,6 +60,7 @@ import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Polygonal2DRegion; +import com.sk89q.worldedit.world.block.BlockTypes; import org.bukkit.Bukkit; import org.bukkit.SoundCategory; import org.bukkit.configuration.file.FileConfiguration; @@ -289,10 +293,9 @@ public static boolean savePlotAsSchematic(@NotNull Plot plot) throws IOException // 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(); + var cpw = new CityPlotWorld(plot); + Mask airMask = new BlockTypeMask(BukkitAdapter.adapt(cpw.getBukkitWorld()), BlockTypes.AIR); + AbstractPlotLoader.pasteSchematic(airMask, outputStream.toByteArray(), cpw, false, true); } return true; } 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 bede05df..02d4167d 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 @@ -218,16 +218,16 @@ protected List getBlockedCommands(@NotNull FileConfiguration config) { protected void generateStructure() throws Exception { Utils.runSync(() -> { if (plotType.hasEnvironment()) { - pasteSchematic(null, this.schematicBytes, this.plotWorld, false); + pasteSchematic(null, this.schematicBytes, this.plotWorld, false, 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); + pasteSchematic(airMask, PlotUtils.getOutlinesSchematicBytes(plot, this.schematicBytes, this.plotWorld.getBukkitWorld()), this.plotWorld, true, false); } return null; }).get(); } - public static void pasteSchematic(@Nullable Mask pasteMask, byte[] schematicFile, @NotNull PlotWorld world, boolean clearArea) throws IOException { + public static void pasteSchematic(@Nullable Mask pasteMask, byte[] schematicFile, @NotNull PlotWorld world, boolean clearArea, boolean offset) throws IOException { // load world if not loaded already if (!world.loadWorld()) return; World weWorld = new BukkitWorld(world.getBukkitWorld()); 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 feafcc26..0750a995 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 @@ -68,7 +68,7 @@ protected void generateStructure() throws Exception { if (completedSchematic != null) { Utils.runSync(() -> { Mask airMask = new BlockTypeMask(BukkitAdapter.adapt(plotWorld.getBukkitWorld()), BlockTypes.AIR); - pasteSchematic(airMask, completedSchematic, plotWorld, true); + pasteSchematic(airMask, completedSchematic, plotWorld, true, true); return null; }).get(); } else super.generateStructure(); @@ -90,6 +90,6 @@ protected void copyToCityWorld() throws IOException { // If the player is playing in his own world, then additionally generate the plot in the city world CityPlotWorld cityPlotWorld = new CityPlotWorld((Plot) plot); - AbstractPlotLoader.pasteSchematic(null, this.schematicBytes, cityPlotWorld, false); + AbstractPlotLoader.pasteSchematic(null, this.schematicBytes, cityPlotWorld, false, true); } } 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 acf97e31..586dec2b 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 @@ -24,8 +24,7 @@ package com.alpsbte.plotsystem.core.system.plot.generator.world; -import net.kyori.adventure.util.TriState; -import org.bukkit.GameRule; +import org.bukkit.GameRules; import org.bukkit.World; import org.bukkit.WorldCreator; import org.bukkit.WorldType; @@ -50,8 +49,7 @@ protected void generateWorld() { .environment(ENVIRONMENT) .type(WORLD_TYPE) .generator(new SkeletonWorldGenerator.EmptyChunkGenerator()) - .generatorSettings(GENERATOR_SETTINGS) - .keepSpawnLoaded(TriState.FALSE); + .generatorSettings(GENERATOR_SETTINGS); this.world = worldCreator.createWorld(); } @@ -60,15 +58,14 @@ protected void configureWorld() { 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); + bukkitWorld.setGameRule(GameRules.RANDOM_TICK_SPEED, 0); + bukkitWorld.setGameRule(GameRules.ADVANCE_TIME, false); + bukkitWorld.setGameRule(GameRules.FIRE_SPREAD_RADIUS_AROUND_PLAYER, 0); + bukkitWorld.setGameRule(GameRules.ADVANCE_WEATHER, false); + bukkitWorld.setGameRule(GameRules.KEEP_INVENTORY, true); + bukkitWorld.setGameRule(GameRules.SPAWN_MOBS, false); + bukkitWorld.setGameRule(GameRules.SHOW_ADVANCEMENT_MESSAGES, false); + bukkitWorld.setGameRule(GameRules.BLOCK_DROPS, false); // Set time to noon bukkitWorld.setTime(6000); 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 d4124697..d0c03ef6 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 @@ -141,7 +141,7 @@ public boolean onAbandon() { // paste initial schematic to reset plot try { - AbstractPlotLoader.pasteSchematic(null, PlotUtils.getOutlinesSchematicBytes(plot, plot.getInitialSchematicBytes(), getBukkitWorld()), this, true); + AbstractPlotLoader.pasteSchematic(null, PlotUtils.getOutlinesSchematicBytes(plot, plot.getInitialSchematicBytes(), getBukkitWorld()), this, true, false); } catch (IOException e) { PlotSystem.getPlugin().getComponentLogger().error(text("Could not paste schematic!"), e); } From ddeb946d23ecc42a01a69bf55db8dcd8a17ac3c4 Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Thu, 9 Apr 2026 16:38:35 +0200 Subject: [PATCH 24/39] ensure AbstractPlotLoader.pasteSchematic is run in main thread when copying plot to CityWorld --- .../plot/generator/loader/DefaultPlotLoader.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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 0750a995..5ba9c274 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 @@ -90,6 +90,14 @@ protected void copyToCityWorld() throws IOException { // If the player is playing in his own world, then additionally generate the plot in the city world CityPlotWorld cityPlotWorld = new CityPlotWorld((Plot) plot); - AbstractPlotLoader.pasteSchematic(null, this.schematicBytes, cityPlotWorld, false, true); + try { + Utils.runSync(() -> { + AbstractPlotLoader.pasteSchematic(null, this.schematicBytes, cityPlotWorld, false, true); + return null; + }).get(); + } catch (Exception e) { + if (e.getCause() instanceof IOException ioException) throw ioException; + throw new IOException("Could not copy plot to city world!", e); + } } } From 75e066b552b948ce9fcfadea891f9c05bb5ab24f Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Thu, 9 Apr 2026 16:55:40 +0200 Subject: [PATCH 25/39] run thread unsafe methods in Companion menues in main thread --- .../core/menus/companion/CityProjectMenu.java | 19 +++++++------- .../core/menus/companion/ContinentMenu.java | 26 ++++++++++--------- .../core/menus/companion/CountryMenu.java | 5 ++-- 3 files changed, 27 insertions(+), 23 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 df1902f8..a118fc56 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 @@ -16,9 +16,7 @@ import com.alpsbte.plotsystem.utils.io.LangPaths; import com.alpsbte.plotsystem.utils.io.LangUtil; import com.alpsbte.plotsystem.utils.items.MenuItems; -import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; -import org.bukkit.Material; import org.bukkit.entity.Player; import org.ipvp.canvas.mask.BinaryMask; import org.ipvp.canvas.mask.Mask; @@ -68,7 +66,8 @@ protected void setPreviewItems() { @Override protected void setItemClickEventsAsync() { getMenu().getSlot(0).setClickHandler((clickPlayer, clickInformation) -> - generateRandomPlot(clickPlayer, getValidCityProjects(selectedPlotDifficulty, clickPlayer, country), selectedPlotDifficulty)); + CompletableFuture.runAsync(() -> + generateRandomPlot(clickPlayer, getValidCityProjects(selectedPlotDifficulty, clickPlayer, country), selectedPlotDifficulty))); getMenu().getSlot(1).setClickHandler((clickPlayer, clickInformation) -> new CountryMenu(clickPlayer, country.getContinent(), selectedPlotDifficulty)); @@ -125,7 +124,7 @@ public void handleCityProjectClick(Player player, CityProject city) { public static boolean generateRandomPlot(Player player, @NotNull List items, PlotDifficulty selectedPlotDifficulty) { PlotDifficulty difficulty = selectedPlotDifficulty; if (items.isEmpty()) { - player.playSound(player, Utils.SoundUtils.ERROR_SOUND, 1, 1); + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> player.playSound(player, Utils.SoundUtils.ERROR_SOUND, 1, 1)); return false; } CityProject randomCity = items.get(Utils.getRandom().nextInt(items.size())); @@ -138,10 +137,10 @@ public static boolean generateRandomPlot(Player player, @NotNull List player.closeInventory()); boolean successful = PlotHandler.assignAndGenerateRandomPlot(builder, randomCity, difficulty); - if (successful) player.playSound(player, Utils.SoundUtils.DONE_SOUND, 1, 1); - return true; + if (successful) Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> player.playSound(player, Utils.SoundUtils.DONE_SOUND, 1, 1)); + return successful; } public static @NotNull @Unmodifiable List getValidCityProjects(PlotDifficulty selectedPlotDifficulty, Player player, @NotNull Country country) { @@ -210,8 +209,10 @@ protected void setPaginatedItemClickEventsAsync(@NotNull List source) { private static void sqlError(@NotNull Player clickPlayer, Exception ex) { Utils.logSqlException(ex); - clickPlayer.sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(clickPlayer, LangPaths.Message.Error.ERROR_OCCURRED))); - clickPlayer.playSound(clickPlayer.getLocation(), Utils.SoundUtils.ERROR_SOUND, 1, 1); + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { + clickPlayer.sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(clickPlayer, LangPaths.Message.Error.ERROR_OCCURRED))); + clickPlayer.playSound(clickPlayer.getLocation(), Utils.SoundUtils.ERROR_SOUND, 1, 1); + }); Thread.currentThread().interrupt(); } } diff --git a/src/main/java/com/alpsbte/plotsystem/core/menus/companion/ContinentMenu.java b/src/main/java/com/alpsbte/plotsystem/core/menus/companion/ContinentMenu.java index 46c965d4..b64749de 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/menus/companion/ContinentMenu.java +++ b/src/main/java/com/alpsbte/plotsystem/core/menus/companion/ContinentMenu.java @@ -14,6 +14,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; public class ContinentMenu extends AbstractMenu { private final HashMap layout = new HashMap<>(); @@ -47,18 +48,19 @@ protected void setMenuItemsAsync() { @Override protected void setItemClickEventsAsync() { - getMenu().getSlot(0).setClickHandler((clickPlayer, clickInformation) -> { // Set click event for random selection item - List layout2 = new java.util.ArrayList<>(layout.values().stream().toList()); - while (!layout2.isEmpty()) { - var rndContinent = layout2.get(Utils.getRandom().nextInt(layout2.size())); - var successful = CountryMenu.generateRandomPlot(clickPlayer, DataProvider.COUNTRY.getCountriesByContinent(rndContinent), null); - if (successful) { - return; - } else { - layout2.remove(rndContinent); - } - } - }); + getMenu().getSlot(0).setClickHandler((clickPlayer, clickInformation) -> + CompletableFuture.runAsync(() -> { // Set click event for random selection item + List layout2 = new java.util.ArrayList<>(layout.values().stream().toList()); + while (!layout2.isEmpty()) { + var rndContinent = layout2.get(Utils.getRandom().nextInt(layout2.size())); + var successful = CountryMenu.generateRandomPlot(clickPlayer, DataProvider.COUNTRY.getCountriesByContinent(rndContinent), null); + if (successful) { + return; + } else { + layout2.remove(rndContinent); + } + } + })); for (Map.Entry continent : layout.entrySet()) { getMenu().getSlot(continent.getKey()).setClickHandler((clickPlayer, clickInfo) -> new CountryMenu(clickPlayer, continent.getValue())); diff --git a/src/main/java/com/alpsbte/plotsystem/core/menus/companion/CountryMenu.java b/src/main/java/com/alpsbte/plotsystem/core/menus/companion/CountryMenu.java index 8e90f2f2..16a73c6c 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/menus/companion/CountryMenu.java +++ b/src/main/java/com/alpsbte/plotsystem/core/menus/companion/CountryMenu.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import static net.kyori.adventure.text.Component.text; import static net.kyori.adventure.text.format.NamedTextColor.AQUA; @@ -77,7 +78,7 @@ protected void setMenuItemsAsync() { @Override protected void setItemClickEventsAsync() { getMenu().getSlot(0).setClickHandler((clickPlayer, clickInformation) -> // Set click event for random selection item - generateRandomPlot(clickPlayer, countryProjects, selectedPlotDifficulty)); + CompletableFuture.runAsync(() -> generateRandomPlot(clickPlayer, countryProjects, selectedPlotDifficulty))); // Set click event for plots difficulty item getMenu().getSlot(6).setClickHandler((clickPlayer, clickInformation) -> { @@ -163,4 +164,4 @@ private void setCountryItems() { .build()); } } -} \ No newline at end of file +} From 546f4c6e953f8724252b863875185840baef1dd4 Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Thu, 9 Apr 2026 17:13:16 +0200 Subject: [PATCH 26/39] make sure onException feedback runs on mainthread --- .../system/plot/generator/loader/AbstractPlotLoader.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 02d4167d..3c6e4505 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 @@ -271,8 +271,11 @@ protected void onException(Exception e) { PlotHandler.abandonPlot(this.plot); PlotSystem.getPlugin().getComponentLogger().error(text("An error occurred while generating plot!"), e); - builder.getPlayer().sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(builder.getPlayer(), LangPaths.Message.Error.ERROR_OCCURRED))); - builder.getPlayer().playSound(builder.getPlayer().getLocation(), Utils.SoundUtils.ERROR_SOUND, 1, 1); + Utils.runSync(() -> { + 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 null; + }); } protected abstract void onCompletion(); From 7c3c4f1e2badd64c1aea3a5688c27e769613adde Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Thu, 9 Apr 2026 18:28:16 +0200 Subject: [PATCH 27/39] make the tutorial system run async when possible --- .../com/alpsbte/plotsystem/PlotSystem.java | 2 +- .../generator/loader/AbstractPlotLoader.java | 9 +++- .../generator/loader/TutorialPlotLoader.java | 14 +++++- .../generator/world/PlotWorldGenerator.java | 4 ++ .../system/tutorial/AbstractPlotTutorial.java | 29 +++++++----- .../system/tutorial/AbstractTutorial.java | 44 ++++++++++++++----- .../system/tutorial/stage/StageTimeline.java | 2 +- .../stage/tasks/PlotSchematicPasteTask.java | 24 +++++++--- .../tutorial/stage/tasks/TeleportTask.java | 17 ++++++- .../stage/tasks/events/BuildEventTask.java | 2 +- .../tasks/events/TeleportPointEventTask.java | 2 +- .../system/tutorial/utils/TutorialNPC.java | 9 +++- .../tutorial/utils/TutorialNPCHologram.java | 1 + .../utils/TutorialNPCTurnTracker.java | 2 +- 14 files changed, 122 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java index 4c730784..93c1a623 100644 --- a/src/main/java/com/alpsbte/plotsystem/PlotSystem.java +++ b/src/main/java/com/alpsbte/plotsystem/PlotSystem.java @@ -129,7 +129,7 @@ public void onEnable() { // Register tutorials if (getConfig().getBoolean(ConfigPaths.TUTORIAL_ENABLE)) { AbstractTutorial.registerTutorials(Collections.singletonList(BeginnerTutorial.class)); - Bukkit.getScheduler().runTaskTimerAsynchronously(FancyNpcsPlugin.get().getPlugin(), new TutorialNPCTurnTracker(), 0, 1L); + Bukkit.getScheduler().runTaskTimer(FancyNpcsPlugin.get().getPlugin(), new TutorialNPCTurnTracker(), 0, 1L); } // Generate Skeleton World 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 3c6e4505..464dc66a 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 @@ -268,7 +268,14 @@ public static void pasteSchematic(@Nullable Mask pasteMask, byte[] schematicFile } protected void onException(Exception e) { - PlotHandler.abandonPlot(this.plot); + try { + Utils.runSync(() -> { + PlotHandler.abandonPlot(this.plot); + return null; + }).get(); + } catch (Exception ex) { + PlotSystem.getPlugin().getComponentLogger().error(text("Failed to clean up plot after generation error!"), ex); + } PlotSystem.getPlugin().getComponentLogger().error(text("An error occurred while generating plot!"), e); Utils.runSync(() -> { diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/TutorialPlotLoader.java b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/TutorialPlotLoader.java index 77eb2bee..5fba9c50 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/TutorialPlotLoader.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/plot/generator/loader/TutorialPlotLoader.java @@ -55,7 +55,12 @@ public boolean isBuildingEnabled() { public void setBuildingEnabled(boolean buildingEnabled) { this.buildingEnabled = buildingEnabled; - setBuildRegionPermissions(plotWorld.getProtectedBuildRegion()); + ProtectedRegion buildRegion = plotWorld.getProtectedBuildRegion(); + if (buildRegion == null) { + PlotSystem.getPlugin().getComponentLogger().warn("Tutorial build region was not available while updating build permissions for plot {}.", plot.getId()); + return; + } + setBuildRegionPermissions(buildRegion); } public boolean isWorldEditEnabled() { @@ -64,7 +69,12 @@ public boolean isWorldEditEnabled() { public void setWorldEditEnabled(boolean worldEditEnabled) { this.worldEditEnabled = worldEditEnabled; - setBuildRegionPermissions(plotWorld.getProtectedBuildRegion()); + ProtectedRegion buildRegion = plotWorld.getProtectedBuildRegion(); + if (buildRegion == null) { + PlotSystem.getPlugin().getComponentLogger().warn("Tutorial build region was not available while updating WorldEdit permissions for plot {}.", plot.getId()); + return; + } + setBuildRegionPermissions(buildRegion); } @Override 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 b721c445..13d2d7b4 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 @@ -124,6 +124,10 @@ protected void configureWorld() { assert world != null; Option mvWorld = worldManager.getLoadedWorld(worldName); + world.setDifficulty(Difficulty.PEACEFUL); + world.setSpawnFlags(false, false); + world.setAutoSave(false); + // Configure multiverse world mvWorld.get().setAllowFlight(true); mvWorld.get().setGameMode(GameMode.CREATIVE); 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 607531cc..ca620bd5 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 @@ -147,18 +147,27 @@ public void saveTutorial(int stageId) { } @Override - public void onSwitchWorld(@NotNull UUID playerUUID, int tutorialWorldIndex) { - if (!getPlayerUUID().toString().equals(playerUUID.toString())) return; + public CompletableFuture switchWorldAsync(@NotNull UUID playerUUID, int tutorialWorldIndex) { + if (!getPlayerUUID().toString().equals(playerUUID.toString())) return CompletableFuture.completedFuture(null); if (tutorialWorldIndex == 1 && (plotGenerator == null || !tutorialPlot.getWorld().isWorldGenerated())) { - plotGenerator = new TutorialPlotLoader(tutorialPlot, Builder.byUUID(playerUUID)); - try { - onPlotSchematicPaste(playerUUID, ((AbstractPlotStage) currentStage).getInitSchematicId()); - } catch (Exception ex) { - onException(ex); - return; - } + return CompletableFuture.runAsync(() -> { + plotGenerator = new TutorialPlotLoader(tutorialPlot, Builder.byUUID(playerUUID)); + try { + onPlotSchematicPaste(playerUUID, ((AbstractPlotStage) currentStage).getInitSchematicId()); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + }).thenCompose(ignored -> { + CompletableFuture future = new CompletableFuture<>(); + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> + super.switchWorldAsync(playerUUID, tutorialWorldIndex).whenComplete((result, throwable) -> { + if (throwable != null) future.completeExceptionally(throwable); + else future.complete(result); + })); + return future; + }); } - super.onSwitchWorld(playerUUID, tutorialWorldIndex); + return super.switchWorldAsync(playerUUID, tutorialWorldIndex); } @Override diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/AbstractTutorial.java b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/AbstractTutorial.java index 124e03a1..008b3630 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/AbstractTutorial.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/AbstractTutorial.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import static net.kyori.adventure.text.Component.text; @@ -196,18 +197,28 @@ protected void nextStage() { try { // Switch to the next stage currentStage = getStage(); - - // Check if player has to switch world - onSwitchWorld(player.getUniqueId(), currentStage.getInitWorldIndex()); - - // Ge the timeline of the current stage - stageTimeline = currentStage.getTimeline(); - - // Start tasks timeline - prepareStage(stageTimeline::StartTimeline); } catch (Exception ex) { onException(ex); + return; } + + switchWorldAsync(player.getUniqueId(), currentStage.getInitWorldIndex()).whenComplete((ignored, throwable) -> + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { + if (throwable != null) { + onException(throwable instanceof Exception exception ? exception : new Exception(throwable)); + return; + } + + try { + // Ge the timeline of the current stage + stageTimeline = currentStage.getTimeline(); + + // Start tasks timeline + prepareStage(stageTimeline::StartTimeline); + } catch (Exception ex) { + onException(ex); + } + })); } } @@ -220,12 +231,20 @@ public void onStageComplete(UUID playerUUID) { @Override public void onSwitchWorld(UUID playerUUID, int tutorialWorldIndex) { - if (!player.getUniqueId().toString().equals(playerUUID.toString())) return; - if (currentWorldIndex == tutorialWorldIndex) return; + switchWorldAsync(playerUUID, tutorialWorldIndex).exceptionally(ex -> { + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> + onException(ex instanceof Exception exception ? exception : new Exception(ex))); + return null; + }); + } + + public CompletableFuture switchWorldAsync(UUID playerUUID, int tutorialWorldIndex) { + if (!player.getUniqueId().toString().equals(playerUUID.toString())) return CompletableFuture.completedFuture(null); + if (currentWorldIndex == tutorialWorldIndex) return CompletableFuture.completedFuture(null); currentWorldIndex = tutorialWorldIndex; TutorialWorld world = worlds.get(tutorialWorldIndex); - if (world == null) return; + if (world == null) return CompletableFuture.completedFuture(null); player.teleport(world.getPlayerSpawnLocation()); if (npc.getNpc() != null) { npc.move(player, world.getNpcSpawnLocation()); @@ -233,6 +252,7 @@ public void onSwitchWorld(UUID playerUUID, int tutorialWorldIndex) { npc.create(world.getNpcSpawnLocation()); npc.spawn(player); } + return CompletableFuture.completedFuture(null); } @Override diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/StageTimeline.java b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/StageTimeline.java index ae1fa2a9..ae4f2deb 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/StageTimeline.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/StageTimeline.java @@ -75,7 +75,7 @@ public void run() { this.cancel(); } else updatePlayerActionBar(); } - }.runTaskTimerAsynchronously(PlotSystem.getPlugin(), 0, 20); + }.runTaskTimer(PlotSystem.getPlugin(), 0, 20); } // If the task has npc interaction show the hologram click info, otherwise hide it 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 e1975aaa..4ba50d01 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 @@ -1,9 +1,13 @@ package com.alpsbte.plotsystem.core.system.tutorial.stage.tasks; +import com.alpsbte.plotsystem.PlotSystem; import com.alpsbte.plotsystem.core.system.tutorial.AbstractTutorial; import com.alpsbte.plotsystem.core.system.tutorial.PlotTutorial; +import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import java.util.concurrent.CompletableFuture; + public class PlotSchematicPasteTask extends AbstractTask { private final int schematicId; @@ -16,12 +20,20 @@ public PlotSchematicPasteTask(Player player, int schematicId) { public void performTask() { PlotTutorial tutorial = (PlotTutorial) AbstractTutorial.getActiveTutorial(player.getUniqueId()); if (tutorial != null) { - try { - tutorial.onPlotSchematicPaste(player.getUniqueId(), schematicId); - } catch (Exception ex) { - tutorial.onException(ex); - return; - } + CompletableFuture.runAsync(() -> { + try { + tutorial.onPlotSchematicPaste(player.getUniqueId(), schematicId); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + }).whenComplete((ignored, throwable) -> Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { + if (throwable != null) { + tutorial.onException(throwable instanceof Exception exception ? exception : new Exception(throwable)); + return; + } + setTaskDone(); + })); + return; } setTaskDone(); } diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/TeleportTask.java b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/TeleportTask.java index 650066b7..ef50ed57 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/TeleportTask.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/TeleportTask.java @@ -24,8 +24,21 @@ public TeleportTask(Player player, Location location) { public void performTask() { Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { if (location == null) { - for (int i = 0; i < AbstractTutorial.getActiveTutorials().size(); i++) - AbstractTutorial.getActiveTutorials().get(i).onSwitchWorld(player.getUniqueId(), tutorialWorldIndex); + AbstractTutorial tutorial = AbstractTutorial.getActiveTutorial(player.getUniqueId()); + if (tutorial == null) { + setTaskDone(); + return; + } + + tutorial.switchWorldAsync(player.getUniqueId(), tutorialWorldIndex).whenComplete((ignored, throwable) -> + Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { + if (throwable != null) { + tutorial.onException(throwable instanceof Exception exception ? exception : new Exception(throwable)); + return; + } + setTaskDone(); + })); + return; } else { player.teleport(location); } diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/events/BuildEventTask.java b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/events/BuildEventTask.java index cf0c06d4..b70fb093 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/events/BuildEventTask.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/events/BuildEventTask.java @@ -38,7 +38,7 @@ public void run() { player.getWorld().spawnParticle(Particle.FLAME, blockVector.getBlockX() + 0.5, blockVector.getBlockY() + 0.5, blockVector.getBlockZ() + 0.5, 1, 0, 0, 0, 0); } - }.runTaskTimerAsynchronously(PlotSystem.getPlugin(), 0, 10); + }.runTaskTimer(PlotSystem.getPlugin(), 0, 10); } @Override diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/events/TeleportPointEventTask.java b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/events/TeleportPointEventTask.java index fb2a5736..5be18801 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/events/TeleportPointEventTask.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/stage/tasks/events/TeleportPointEventTask.java @@ -40,7 +40,7 @@ public void run() { player.getWorld().spawnParticle(Particle.FLAME, blockVector.getBlockX() + 0.5, blockVector.getBlockY() + 1.5, blockVector.getBlockZ() + 0.5, 1, 0, 0, 0, 0); } - }.runTaskTimerAsynchronously(PlotSystem.getPlugin(), 0, 10); + }.runTaskTimer(PlotSystem.getPlugin(), 0, 10); } @Override diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/utils/TutorialNPC.java b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/utils/TutorialNPC.java index a7a03448..6e6fd490 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/utils/TutorialNPC.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/utils/TutorialNPC.java @@ -48,6 +48,10 @@ public TutorialNPC(String npcId, String npcDisplayName, String npcInteractionPro public void create(Location spawnPos) { if (npc != null) delete(); + if (FancyNpcsPlugin.get().getNpcAdapter() == null) { + throw new IllegalStateException("FancyNpcs NPC adapter is unavailable. This usually means the installed FancyNpcs version is incompatible with Plot-System."); + } + NpcData npcData = new NpcData(id, UUID.randomUUID(), spawnPos); npc = FancyNpcsPlugin.get().getNpcAdapter().apply(npcData); npc.getData().setSkinData(skin); @@ -66,9 +70,10 @@ public void create(Location spawnPos) { */ public void spawn(Player player) { if (npc == null) return; + String playerWorldName = player.getWorld().getName(); Bukkit.getScheduler().runTaskAsynchronously(FancyNpcsPlugin.get().getPlugin(), () -> { npc.spawn(player); - if (hologram != null && player.getWorld().getName().equals( + if (hologram != null && playerWorldName.equals( Objects.requireNonNull(hologram.getLocation().getWorld()).getName())) hologram.create(player); }); @@ -95,6 +100,8 @@ public void delete() { if (npc != null) npc.removeForAll(); if (hologram != null) hologram.delete(); activeTutorialNPCs.remove(this); + npc = null; + hologram = null; } /** diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/utils/TutorialNPCHologram.java b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/utils/TutorialNPCHologram.java index 9b7058f5..3b8d9335 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/utils/TutorialNPCHologram.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/utils/TutorialNPCHologram.java @@ -42,6 +42,7 @@ public TutorialNPCHologram(@NotNull String id, Location location, TutorialNPC np @Override public void create(Player player) { Bukkit.getScheduler().runTask(FancyNpcsPlugin.get().getPlugin(), () -> { + if (npc.getNpc() == null) return; if (npc.getNpc().getIsVisibleForPlayer().containsKey(player.getUniqueId()) && npc.getNpc().getIsVisibleForPlayer().get(player.getUniqueId())) { super.create(player); diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/utils/TutorialNPCTurnTracker.java b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/utils/TutorialNPCTurnTracker.java index fb451c33..7d7a7f95 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/utils/TutorialNPCTurnTracker.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/tutorial/utils/TutorialNPCTurnTracker.java @@ -10,7 +10,7 @@ public class TutorialNPCTurnTracker implements Runnable { @Override public void run() { for (AbstractTutorial tutorial : AbstractTutorial.getActiveTutorials()) { - if (tutorial.getNPC() == null) continue; + if (tutorial.getNPC() == null || tutorial.getNPC().getNpc() == null) continue; Location playerLoc = tutorial.getPlayer().getLocation(); Location npcLoc = tutorial.getNPC().getNpc().getData().getLocation(); if (npcLoc == null || !npcLoc.getWorld().getName() From 9947a051a270fd0b42835befd6774306fc1bdb98 Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Thu, 9 Apr 2026 22:18:14 +0200 Subject: [PATCH 28/39] set fixed spawn location for EmptyChunkGenerator --- .../plot/generator/world/SkeletonWorldGenerator.java | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 586dec2b..a26fe069 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,10 +25,15 @@ package com.alpsbte.plotsystem.core.system.plot.generator.world; import org.bukkit.GameRules; +import org.bukkit.Location; import org.bukkit.World; import org.bukkit.WorldCreator; import org.bukkit.WorldType; import org.bukkit.generator.ChunkGenerator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Random; public class SkeletonWorldGenerator { public static final String WORLD_NAME = "Skeleton"; @@ -78,5 +83,10 @@ protected void saveWorld() { public static class EmptyChunkGenerator extends ChunkGenerator { // It should just do nothing + + @Override + public @Nullable Location getFixedSpawnLocation(@NotNull World world, @NotNull Random random) { + return new Location(world, 0, 0, 0); + } } } From 96859ec95676b88cc8c673760410f7b39831c597 Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Thu, 9 Apr 2026 22:20:12 +0200 Subject: [PATCH 29/39] use DefaultPlotLoader for plots with no completed schematic --- .../plotsystem/core/system/plot/world/OnePlotWorld.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 4eb4dfbb..c6c539b7 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 @@ -71,8 +71,10 @@ public boolean loadWorld() { if (plot == null || isWorldGenerated()) return super.loadWorld(); // Generate plot if it doesn't exist - if (plot.getPlotType() == PlotType.TUTORIAL || ((Plot) plot).getCompletedSchematic() == null) + if (plot.getPlotType() == PlotType.TUTORIAL) generateWorld(TutorialPlotLoader.class); + else if (((Plot) plot).getCompletedSchematic() == null) + generateWorld(DefaultPlotLoader.class); new DefaultPlotLoader(plot, plotOwner, plot.getPlotType(), this); From fefcd9993b35fae86cad2f39a5c95d61854358eb Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Wed, 6 May 2026 11:54:20 +0200 Subject: [PATCH 30/39] add restorePlotSlot method and no longer treat all slots occupied as a failure and skip instead --- .../core/system/review/PlotReview.java | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/review/PlotReview.java b/src/main/java/com/alpsbte/plotsystem/core/system/review/PlotReview.java index bc58723c..a2e3c407 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/review/PlotReview.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/review/PlotReview.java @@ -75,20 +75,12 @@ public boolean updateFeedback(String feedback) { public boolean undoReview() { // remove owner score and remove plot from slot if (!plot.getPlotOwner().addScore(splitScore == -1 ? -score : -splitScore)) return false; + if (!restorePlotSlot(plot.getPlotOwner())) return false; - Slot slot = plot.getPlotOwner().getSlotByPlotId(plot.getId()); // get slot if plot is still in slots (rejected) - if (slot == null) slot = plot.getPlotOwner().getFreeSlot(); // get new slot otherwise (completed) - if (slot == null) return false; - - if (!plot.getPlotOwner().setSlot(slot, plot.getId())) return false; - - // remove members score and remove plot from slot + // remove member's score and remove plot from slot for (Builder member : plot.getPlotMembers()) { if (!member.addScore(-splitScore)) return false; - - Slot memberSlot = member.getSlotByPlotId(plot.getId()); - if (memberSlot == null) memberSlot = member.getFreeSlot(); - if (memberSlot == null || member.setSlot(memberSlot, plot.getId())) return false; + if (!restorePlotSlot(member)) return false; } boolean successful = true; @@ -109,4 +101,23 @@ public boolean undoReview() { return successful; } + + /** + * Tries to assign the plot to a slot again if possible. + * If all slots are occupied, the plot won't be assigned to a slot, but still accessible via /plots + * + * @param builder target builder (owner or member) + * @return True if the slot was assigned successfully, false otherwise + */ + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + private boolean restorePlotSlot(Builder builder) { + Slot slot = builder.getSlotByPlotId(plot.getId()); // get slot if plot is still in slots (rejected) + if (slot == null) slot = builder.getFreeSlot(); // get new slot otherwise (completed) + if (slot == null) { + PlotSystem.getPlugin().getComponentLogger().warn("Skipping slot restore for plot #{} and builder {}. All slots are occupied!", plot.getId(), builder.getName()); + return true; + } + + return builder.setSlot(slot, plot.getId()); + } } From ca923667989edd133905226ce110ab72700e628a Mon Sep 17 00:00:00 2001 From: Cinnazeyy Date: Mon, 11 May 2026 04:34:07 +0200 Subject: [PATCH 31/39] when freeing up a slot, check for orphans and assign if possible --- .../com/alpsbte/plotsystem/core/system/Builder.java | 13 +++++++++++++ .../plotsystem/core/system/plot/PlotHandler.java | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/Builder.java b/src/main/java/com/alpsbte/plotsystem/core/system/Builder.java index 62c8809b..a0080e84 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/Builder.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/Builder.java @@ -5,10 +5,12 @@ import com.alpsbte.plotsystem.core.system.plot.Plot; import com.alpsbte.plotsystem.core.system.plot.utils.PlotType; import com.alpsbte.plotsystem.utils.enums.Slot; +import com.alpsbte.plotsystem.utils.enums.Status; import com.alpsbte.plotsystem.utils.io.ConfigPaths; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import java.util.List; import java.util.UUID; public class Builder { @@ -79,6 +81,12 @@ public Slot getSlot(Plot plot) { @SuppressWarnings("BooleanMethodIsAlwaysInverted") public boolean setSlot(Slot slot, int plotId) { + // check for orphans and assign orphan if available + if (plotId == -1) { + List orphans = getOrphans(); + if (!orphans.isEmpty()) plotId = orphans.getFirst().getId(); + } + if (DataProvider.BUILDER.setSlot(this.uuid, plotId, slot)) { switch (slot) { case FIRST -> firstSlot = plotId; @@ -129,6 +137,11 @@ public Slot getSlotByPlotId(int plotId) { return null; } + private List getOrphans() { + List currentPlots = DataProvider.PLOT.getPlots(this, Status.unfinished, Status.unreviewed); + return currentPlots.stream().filter(plot -> getSlotByPlotId(plot.getId()) == null).toList(); + } + public static Builder byUUID(UUID uuid) { return DataProvider.BUILDER.getBuilderByUUID(uuid); } 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 2f563b82..97d4b1c0 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 @@ -102,7 +102,7 @@ public static boolean assignPlot(Builder builder, Plot plot) { } // Assign - if (!builder.setSlot(builder.getFreeSlot(), plot.getId())) return false; + if (!builder.setSlot(freeSlot, plot.getId())) return false; if (!plot.setStatus(Status.unfinished)) return false; return plot.setPlotOwner(builder); } From 29b1c7897f2acfa8d28807f9d0278158c03f8ec8 Mon Sep 17 00:00:00 2001 From: cinnazeyy Date: Sat, 23 May 2026 17:06:48 +0200 Subject: [PATCH 32/39] fix: plot world loading not running on main thread if called via PlotActionsMenu and fix main thread deadlock if trying to runSync when already on main thread --- .../core/menus/PlotActionsMenu.java | 11 ++++++++++- .../core/system/plot/world/PlotWorld.java | 9 ++++++++- .../com/alpsbte/plotsystem/utils/Utils.java | 19 ++++++++++++------- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/alpsbte/plotsystem/core/menus/PlotActionsMenu.java b/src/main/java/com/alpsbte/plotsystem/core/menus/PlotActionsMenu.java index 32eedcb7..6c3d7b8c 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/menus/PlotActionsMenu.java +++ b/src/main/java/com/alpsbte/plotsystem/core/menus/PlotActionsMenu.java @@ -20,6 +20,8 @@ import org.ipvp.canvas.mask.Mask; import org.jetbrains.annotations.NotNull; +import java.util.concurrent.CompletableFuture; + import static net.kyori.adventure.text.Component.text; import static net.kyori.adventure.text.format.NamedTextColor.AQUA; import static net.kyori.adventure.text.format.NamedTextColor.GOLD; @@ -133,7 +135,14 @@ protected void setItemClickEventsAsync() { clickPlayer.sendMessage(Utils.ChatUtils.getAlertFormat(LangUtil.getInstance().get(clickPlayer, LangPaths.Message.Error.CANNOT_LOAD_LEGACY_PLOT))); return; } - plot.getWorld().teleportPlayer(clickPlayer); + CompletableFuture.runAsync(() -> { + if (!plot.getWorld().isWorldGenerated() && !plot.getWorld().loadWorld()) return; + + Utils.runSync(() -> { + plot.getWorld().teleportPlayer(clickPlayer); + return null; + }); + }); }); // Set click event for abandon plot item 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 c7f50d9c..07c878c5 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 @@ -115,7 +115,14 @@ public boolean loadWorld() { return false; } - return isWorldLoaded() || mvCore.getWorldManager().loadWorld(getWorldName()).isSuccess(); + if (isWorldLoaded()) return true; + + try { + return Utils.supplySync(() -> mvCore.getWorldManager().loadWorld(getWorldName()).isSuccess()).get(); + } catch (Exception e) { + PlotSystem.getPlugin().getComponentLogger().warn(text("Could not load world " + worldName + "!"), e); + return false; + } } @Override diff --git a/src/main/java/com/alpsbte/plotsystem/utils/Utils.java b/src/main/java/com/alpsbte/plotsystem/utils/Utils.java index a59997ab..cd3db3bf 100644 --- a/src/main/java/com/alpsbte/plotsystem/utils/Utils.java +++ b/src/main/java/com/alpsbte/plotsystem/utils/Utils.java @@ -42,7 +42,6 @@ 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; @@ -107,29 +106,35 @@ public static ItemStack getConfiguredItem(@NotNull String material, Object custo public static CompletableFuture runSync(Callable task) { CompletableFuture future = new CompletableFuture<>(); - Bukkit.getScheduler().runTask(PlotSystem.getPlugin(), () -> { + Runnable runnable = () -> { try { var result = task.call(); future.complete(result); } catch (Exception e) { future.completeExceptionally(e); } - }); + }; + + if (Bukkit.isPrimaryThread()) runnable.run(); + else Bukkit.getScheduler().getMainThreadExecutor(PlotSystem.getPlugin()).execute(runnable); return future; } public static CompletableFuture supplySync(Callable task) { CompletableFuture future = new CompletableFuture<>(); - Executor executor = Bukkit.getScheduler().getMainThreadExecutor(PlotSystem.getPlugin()); - executor.execute(() -> { + Runnable runnable = () -> { try { var result = task.call(); future.complete(result); } catch (Exception e) { future.completeExceptionally(e); } - }); + }; + + if (Bukkit.isPrimaryThread()) runnable.run(); + else Bukkit.getScheduler().getMainThreadExecutor(PlotSystem.getPlugin()).execute(runnable); + return future; } @@ -283,4 +288,4 @@ public static boolean isOwnerOrReviewer(CommandSender sender, @Nullable Player p } return hasPermission; } -} \ No newline at end of file +} From 92913b0a078a084c586e0174e515933cec556b09 Mon Sep 17 00:00:00 2001 From: cinnazeyy Date: Sat, 23 May 2026 17:10:49 +0200 Subject: [PATCH 33/39] fix: codacy best practice issue --- .../com/alpsbte/plotsystem/core/system/Builder.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/alpsbte/plotsystem/core/system/Builder.java b/src/main/java/com/alpsbte/plotsystem/core/system/Builder.java index a0080e84..cca7856e 100644 --- a/src/main/java/com/alpsbte/plotsystem/core/system/Builder.java +++ b/src/main/java/com/alpsbte/plotsystem/core/system/Builder.java @@ -81,17 +81,18 @@ public Slot getSlot(Plot plot) { @SuppressWarnings("BooleanMethodIsAlwaysInverted") public boolean setSlot(Slot slot, int plotId) { + int newPlotId = plotId; // check for orphans and assign orphan if available if (plotId == -1) { List orphans = getOrphans(); - if (!orphans.isEmpty()) plotId = orphans.getFirst().getId(); + if (!orphans.isEmpty()) newPlotId = orphans.getFirst().getId(); } - if (DataProvider.BUILDER.setSlot(this.uuid, plotId, slot)) { + if (DataProvider.BUILDER.setSlot(this.uuid, newPlotId, slot)) { switch (slot) { - case FIRST -> firstSlot = plotId; - case SECOND -> secondSlot = plotId; - default -> thirdSlot = plotId; + case FIRST -> firstSlot = newPlotId; + case SECOND -> secondSlot = newPlotId; + default -> thirdSlot = newPlotId; } return true; } From d11965543e56c99b346e992cfba2fa6dc33de3c9 Mon Sep 17 00:00:00 2001 From: Zoriot Date: Sun, 24 May 2026 23:04:05 +0200 Subject: [PATCH 34/39] [WIP] feat: Support 26.1 world changes This currently doesn't really work. When you have the old skeleton world it may work but as soon as you have the new one there are some exceptions. Needs to be polished before released --- .../generator/world/PlotWorldGenerator.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) 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 13d2d7b4..7356acfc 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 @@ -84,21 +84,23 @@ public PlotWorldGenerator(String worldName) throws Exception { 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); + Path skeletonPath = Bukkit.getWorld("Skeleton").getWorldPath(); + Path worldPath = skeletonPath.getParent().resolve(worldName); // TODO Switch to world tag once we only support 26.1+ & trmove the checks FileUtils.copyDirectory(skeletonPath.toFile(), worldPath.toFile()); // delete uid.dat - Files.delete(worldPath.resolve("uid.dat")); + if (worldPath.resolve("uid.dat").toFile().exists()) Files.delete(worldPath.resolve("uid.dat")); // rename world name in level.dat - Path levelDat = worldPath.resolve("level.dat"); - NamedTag level = NBTUtil.read(levelDat.toFile()); - CompoundTag tag = (CompoundTag) level.getTag(); - tag.remove("LevelName"); - tag.putString("LevelName", worldName); - level.setTag(tag); - NBTUtil.write(level, levelDat.toFile()); + if (worldPath.resolve("level.dat").toFile().exists()) { + 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"); From 0021ab2cc201a2d9c7831f5a8986241e90da8663 Mon Sep 17 00:00:00 2001 From: cinnazeyy Date: Tue, 9 Jun 2026 01:18:25 +0200 Subject: [PATCH 35/39] fix: handle Paper 26.1 world folder changes --- .../generator/world/PlotWorldGenerator.java | 47 ++++++++++++++----- 1 file changed, 35 insertions(+), 12 deletions(-) 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 7356acfc..7bff13d2 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 @@ -83,17 +83,38 @@ public PlotWorldGenerator(String worldName) throws Exception { } protected void generateWorld() throws IOException { - // copy skeleton world with correct world name Path skeletonPath = Bukkit.getWorld("Skeleton").getWorldPath(); - Path worldPath = skeletonPath.getParent().resolve(worldName); // TODO Switch to world tag once we only support 26.1+ & trmove the checks - FileUtils.copyDirectory(skeletonPath.toFile(), worldPath.toFile()); + + // detect world format + // old = level.dat in world root + // new = paper 26.1+ dimension layout + Path skeletonLevelPath; + Path worldLevelPath; + + if (skeletonPath.resolve("level.dat").toFile().exists()) { + // old format (pre-26.1): world path is the level root + skeletonLevelPath = skeletonPath; + worldLevelPath = skeletonPath.getParent().resolve(worldName); + } else { + // new format (26.1+): world path is dimension folder, level root is 3 parents up + skeletonLevelPath = skeletonPath.getParent().getParent().getParent(); + worldLevelPath = skeletonLevelPath.getParent().resolve(worldName); + } + + // copy entire skeleton level directory with correct name + FileUtils.copyDirectory(skeletonLevelPath.toFile(), worldLevelPath.toFile()); + + // get dimension folder + Path dimensionRelativePath = skeletonLevelPath.relativize(skeletonPath); + Path worldDimensionPath = worldLevelPath.resolve(dimensionRelativePath); // delete uid.dat - if (worldPath.resolve("uid.dat").toFile().exists()) Files.delete(worldPath.resolve("uid.dat")); + Path uidDat = worldDimensionPath.resolve("uid.dat"); + if (uidDat.toFile().exists()) Files.delete(uidDat); // rename world name in level.dat - if (worldPath.resolve("level.dat").toFile().exists()) { - Path levelDat = worldPath.resolve("level.dat"); + Path levelDat = worldLevelPath.resolve("level.dat"); + if (levelDat.toFile().exists()) { NamedTag level = NBTUtil.read(levelDat.toFile()); CompoundTag tag = (CompoundTag) level.getTag(); tag.remove("LevelName"); @@ -103,12 +124,14 @@ protected void generateWorld() throws IOException { } // 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); + Path paperWorld = worldDimensionPath.resolve("paper-world.yml"); + if (paperWorld.toFile().exists()) { + String paperWorldContents = Files.readString(paperWorld); + String updatedContents = paperWorldContents + .replaceAll(SkeletonWorldGenerator.WORLD_NAME, worldName) + .replaceAll(SkeletonWorldGenerator.WORLD_NAME.toLowerCase(), worldName.toLowerCase()); + Files.writeString(paperWorld, updatedContents); + } } protected void createMultiverseWorld() { From ba40e779ebb0e82c3a65ef117e50d8761a8ba0aa Mon Sep 17 00:00:00 2001 From: Zoriot Date: Mon, 15 Jun 2026 15:43:29 +0200 Subject: [PATCH 36/39] fix(plot): Fix world ref when copy the completed schematic --- .../alpsbte/plotsystem/core/system/plot/PlotHandler.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 922e96c2..05cbf1fc 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 @@ -45,6 +45,7 @@ import com.alpsbte.plotsystem.utils.io.LangUtil; 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; @@ -59,6 +60,7 @@ import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Polygonal2DRegion; +import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BlockTypes; import org.bukkit.Bukkit; import org.bukkit.SoundCategory; @@ -74,7 +76,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -279,7 +280,8 @@ public static boolean savePlotAsSchematic(@NotNull Plot plot) throws IOException 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()); + World world = new BukkitWorld(plot.getWorld().getBukkitWorld()); + ForwardExtentCopy forwardExtentCopy = new ForwardExtentCopy(world, region, cb, region.getMinimumPoint()); Operations.complete(forwardExtentCopy); try (ClipboardWriter writer = AbstractPlot.CLIPBOARD_FORMAT.getWriter(outputStream)) { From fa51b38f1b4a3f445edfb9422edd99e091070d5b Mon Sep 17 00:00:00 2001 From: Zoriot Date: Sat, 20 Jun 2026 03:11:29 +0200 Subject: [PATCH 37/39] fix: Fully support only new world format (26.1+) This removes handling for <=1.21.11 Worlds, the old changes made that the migration was started from paper. We always just support the latest version. --- build.gradle.kts | 3 +- gradle/libs.versions.toml | 3 - .../generator/world/PlotWorldGenerator.java | 62 +++---------------- 3 files changed, 9 insertions(+), 59 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index fd97d9bd..a9a7ff07 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -67,8 +67,7 @@ dependencies { if (paperNextEnabled.get()) { constraints { - compileOnly("io.papermc.paper:paper-api:26.1.2.build.+") - compileOnly("com.github.decentsoftware-eu:decentholograms:2.10.0") + compileOnly("io.papermc.paper:paper-api:26.2-rc-2.build.+") } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d8343991..89e1ea11 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,8 +17,6 @@ com-arcaniax-headdatabase-api = "1.3.3-SNAPSHOT" com-github-decentsoftware-eu-decentholograms = "2.9.9" # @pin https://github.com/Fierioziy/ParticleNativeAPI/releases - we are stuck at v3 for now. Need to migrate some time or remove it com-github-fierioziy-particlenativeapi-particlenativeapi-plugin = "3.3.2" -# Ref: https://github.com/Querz/NBT -com-github-querz-nbt-newest = "6.1" # Ref: https://github.com/IntellectualSites/bom com-intellectualsites-bom-bom-newest = "1.56" # https://maven.enginehub.org/repo/com/sk89q/worldguard/worldguard-bukkit/ @@ -53,7 +51,6 @@ 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" } com-intellectualsites-bom-bom-newest = { module = "com.intellectualsites.bom:bom-newest", version.ref = "com-intellectualsites-bom-bom-newest" } 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" } 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 7bff13d2..3bf2d8cd 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 @@ -36,13 +36,13 @@ 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.apache.commons.io.filefilter.NameFileFilter; +import org.apache.commons.io.filefilter.NotFileFilter; import org.bukkit.Bukkit; import org.bukkit.Difficulty; import org.bukkit.GameMode; +import org.bukkit.NamespacedKey; import org.bukkit.World; import org.bukkit.entity.SpawnCategory; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; @@ -51,8 +51,8 @@ import org.mvplugins.multiverse.external.vavr.control.Option; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; +import java.util.Locale; import java.util.Objects; import static net.kyori.adventure.text.Component.text; @@ -83,60 +83,14 @@ public PlotWorldGenerator(String worldName) throws Exception { } protected void generateWorld() throws IOException { - Path skeletonPath = Bukkit.getWorld("Skeleton").getWorldPath(); - - // detect world format - // old = level.dat in world root - // new = paper 26.1+ dimension layout - Path skeletonLevelPath; - Path worldLevelPath; - - if (skeletonPath.resolve("level.dat").toFile().exists()) { - // old format (pre-26.1): world path is the level root - skeletonLevelPath = skeletonPath; - worldLevelPath = skeletonPath.getParent().resolve(worldName); - } else { - // new format (26.1+): world path is dimension folder, level root is 3 parents up - skeletonLevelPath = skeletonPath.getParent().getParent().getParent(); - worldLevelPath = skeletonLevelPath.getParent().resolve(worldName); - } - - // copy entire skeleton level directory with correct name - FileUtils.copyDirectory(skeletonLevelPath.toFile(), worldLevelPath.toFile()); - - // get dimension folder - Path dimensionRelativePath = skeletonLevelPath.relativize(skeletonPath); - Path worldDimensionPath = worldLevelPath.resolve(dimensionRelativePath); - - // delete uid.dat - Path uidDat = worldDimensionPath.resolve("uid.dat"); - if (uidDat.toFile().exists()) Files.delete(uidDat); - - // rename world name in level.dat - Path levelDat = worldLevelPath.resolve("level.dat"); - if (levelDat.toFile().exists()) { - 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 = worldDimensionPath.resolve("paper-world.yml"); - if (paperWorld.toFile().exists()) { - String paperWorldContents = Files.readString(paperWorld); - String updatedContents = paperWorldContents - .replaceAll(SkeletonWorldGenerator.WORLD_NAME, worldName) - .replaceAll(SkeletonWorldGenerator.WORLD_NAME.toLowerCase(), worldName.toLowerCase()); - Files.writeString(paperWorld, updatedContents); - } + Path skeletonPath = Objects.requireNonNull(Bukkit.getWorld("Skeleton"), "Skeleton World is required at this point").getWorldPath(); + Path worldLevelPath = skeletonPath.getParent().resolve(worldName.toLowerCase(Locale.ROOT)); + FileUtils.copyDirectory(skeletonPath.toFile(), worldLevelPath.toFile(), new NotFileFilter(new NameFileFilter("metadata.dat"))); } protected void createMultiverseWorld() { if (!worldManager.isLoadedWorld(worldName)) { - worldManager.importWorld(ImportWorldOptions.worldName(worldName) + worldManager.importWorld(ImportWorldOptions.worldKey(NamespacedKey.minecraft(worldName.toLowerCase(Locale.ROOT))) .environment(environment) .generator("Plot-System") .useSpawnAdjust(false) From 20c4cc318437ace41c3e2a7a7486513e472bbbc0 Mon Sep 17 00:00:00 2001 From: Zoriot Date: Sat, 20 Jun 2026 03:12:20 +0200 Subject: [PATCH 38/39] build(dependencies): update dependency versions for mc 26.1.2 and migrate to Java 25 - Upgraded multiple library dependencies for compatibility. - Updated Java version to 25 in build scripts and workflows. - Modified Gradle configurations to exclude unwanted transitive dependencies. - Adjusted workflows to use actions/checkout@v7 and relevant Java configurations. --- .github/workflows/build.yml | 8 ++++---- .github/workflows/release-draft.yml | 2 +- .github/workflows/release.yml | 6 +++--- build.gradle.kts | 26 ++++++++++++++++++-------- gradle/libs.versions.toml | 18 +++++++++--------- 5 files changed, 35 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 912c8edd..21242e14 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout PR merge commit if: github.event_name == 'pull_request' - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: ref: refs/pull/${{ github.event.pull_request.number }}/merge fetch-depth: 0 @@ -24,15 +24,15 @@ jobs: - name: Checkout main on push if: github.event_name == 'push' - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 0 fetch-tags: true - - name: Set up JDK 21 + - name: Set up JDK 25 uses: actions/setup-java@v5 with: - java-version: '21' + java-version: '25' distribution: 'temurin' - name: Setup Gradle diff --git a/.github/workflows/release-draft.yml b/.github/workflows/release-draft.yml index 1bffe0b1..1dbe8e3e 100644 --- a/.github/workflows/release-draft.yml +++ b/.github/workflows/release-draft.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: fetch-depth: 0 fetch-tags: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 77990d16..84ef07d4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,16 +11,16 @@ jobs: contents: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: fetch-depth: 0 fetch-tags: true ref: ${{ github.event.release.tag_name }} - - name: Set up JDK 21 + - name: Set up JDK 25 uses: actions/setup-java@v5 with: - java-version: '21' + java-version: '25' distribution: 'temurin' - name: Setup Gradle diff --git a/build.gradle.kts b/build.gradle.kts index a9a7ff07..71a49ce2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,6 +43,9 @@ val paperNextEnabled = providers.gradleProperty("paperNext") .map(String::toBoolean) .orElse(false) +val guavaPackage = "com.google.guava" +val gsonPackage = "com.google.code.gson" + dependencies { implementation(libs.com.alpsbte.canvas) implementation(libs.com.alpsbte.alpslib.alpslib.io) @@ -52,10 +55,18 @@ dependencies { implementation(libs.com.zaxxer.hikaricp) { exclude(group = "org.slf4j") } - implementation(libs.com.github.querz.nbt) - implementation(platform(libs.com.intellectualsites.bom.bom.newest)) - compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Core") - compileOnly(libs.com.sk89q.worldguard.worldguard.bukkit) + implementation(platform(libs.com.intellectualsites.bom.bom.newest)) { + exclude(group = guavaPackage) + exclude(group = gsonPackage) + } + compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Core") { + exclude(group = guavaPackage) + exclude(group = gsonPackage) + } + compileOnly(libs.com.sk89q.worldguard.worldguard.bukkit) { + exclude(group = guavaPackage) + exclude(group = gsonPackage) + } compileOnly(libs.multiverse.core) compileOnly(libs.com.github.fierioziy.particlenativeapi.particlenativeapi.plugin) compileOnly(libs.com.arcaniax.headdatabase.api) @@ -122,7 +133,7 @@ tasks.processResources { val targetJava = providers.gradleProperty("targetJava") .map(String::toInt) - .orElse(21) + .orElse(25) java { toolchain { @@ -141,14 +152,13 @@ tasks.withType().configureEach { tasks.register("buildPaperNext") { group = "verification" - description = "Builds against Java 25 and the Paper-next dependency set" + description = "Builds against Paper-next dependency set" tasks = listOf("clean", "build") startParameter.projectProperties.putAll( mapOf( - "paperNext" to "true", - "targetJava" to "25" + "paperNext" to "true" ) ) } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 89e1ea11..84c2f379 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,27 +8,27 @@ com-alpsbte-alpslib-alpslib-hologram = "1.1.3" # https://mvn.alps-bte.com/service/rest/repository/browse/alps-bte/com/alpsbte/alpslib/alpslib-io/ com-alpsbte-alpslib-alpslib-io = "1.2.5" # https://mvn.alps-bte.com/service/rest/repository/browse/alps-bte/com/alpsbte/alpslib/alpslib-utils/ -com-alpsbte-alpslib-alpslib-utils = "1.4.6" +com-alpsbte-alpslib-alpslib-utils = "1.5.0" # https://mvn.alps-bte.com/service/rest/repository/browse/alps-bte/com/alpsbte/canvas/ -com-alpsbte-canvas = "1.3" +com-alpsbte-canvas = "1.3.2" # https://github.com/Arcaniax-Development/HeadDatabase-API/releases com-arcaniax-headdatabase-api = "1.3.3-SNAPSHOT" -# @pin https://github.com/DecentSoftware-eu/DecentHolograms/releases 2.10+ require java 25 (MC 26.1+ mainly) -com-github-decentsoftware-eu-decentholograms = "2.9.9" +# https://github.com/DecentSoftware-eu/DecentHolograms/releases +com-github-decentsoftware-eu-decentholograms = "2.10.0" # @pin https://github.com/Fierioziy/ParticleNativeAPI/releases - we are stuck at v3 for now. Need to migrate some time or remove it com-github-fierioziy-particlenativeapi-particlenativeapi-plugin = "3.3.2" # Ref: https://github.com/IntellectualSites/bom com-intellectualsites-bom-bom-newest = "1.56" # https://maven.enginehub.org/repo/com/sk89q/worldguard/worldguard-bukkit/ -com-sk89q-worldguard-worldguard-bukkit = "7.0.17" +com-sk89q-worldguard-worldguard-bukkit = "7.0.18-SNAPSHOT" # https://github.com/brettwooldridge/HikariCP/tags -com-zaxxer-hikaricp = "7.0.2" +com-zaxxer-hikaricp = "7.1.0" # Provided by Minecraft (Libs folder) commons-io-commons-io = "2.22.0" # https://fancyspaces.net/spaces/fancynpcs/maven-repos/fi-releases/de.oliver:FancyNpcs de-oliver-fancynpcs = "2.9.2" -# @pin https://artifactory.papermc.io/ui/native/universe/io/papermc/paper/paper-api/ 26.1+ requires java 25 -io-papermc-paper-paper-api = "1.21.11-R0.1-SNAPSHOT" +# @pin https://artifactory.papermc.io/ui/native/universe/io/papermc/paper/paper-api/ We want to use the latest api buld +io-papermc-paper-paper-api = "26.1.2.build.+" # https://mvn.alps-bte.com/service/rest/repository/browse/alps-bte/li/cinnazeyy/LangLibs-API/ li-cinnazeyy-langlibs-api = "1.5.2" # https://repo.onarandombox.com/#/multiverse-releases/org/mvplugins/multiverse/core/multiverse-core @@ -39,7 +39,7 @@ org-mariadb-jdbc-mariadb-java-client = "3.6.0-SNAPSHOT" # Plugins # # https://github.com/jmongard/Git.SemVersioning.Gradle/releases -git-semver = "0.19.1" +git-semver = "0.19.2" # https://github.com/GradleUp/shadow/releases shadow = "9.4.2" From 8a9e1805e843374084e39452ec0ab072d5fe061f Mon Sep 17 00:00:00 2001 From: Zoriot Date: Sat, 16 May 2026 22:27:51 +0200 Subject: [PATCH 39/39] =?UTF-8?q?feat(plot-utils):=20=E2=9C=A8=20use=20Alp?= =?UTF-8?q?sLib=20MapLinks=20with=20configurable=20provider=20entries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor plot link message generation to rely on AlpsLib `MapLinks` and iterate providers via `getAllLinks()` instead of hardcoded provider slots. - Replace fixed Google/OSM/Apple link handling with dynamic provider iteration - Build chat link components from resolved `MapLink` entries - Keep shortlink behavior for each generated provider URL - Add plugin-side config mapping for `map-links.links` into `MapLinksConfig` and `MapLinkEntryConfig` so providers can be disabled/extended without plugin code changes - Add `ConfigPaths.MAP_LINKS` - Document optional `map-links` config section in `config.yml` This keeps provider definitions in AlpsLib defaults while allowing server-side overrides (enable/disable/add providers) through plugin config. Implements #177. --- gradle/libs.versions.toml | 2 +- .../core/system/plot/utils/PlotUtils.java | 123 +++++++++++------- .../plotsystem/utils/io/ConfigPaths.java | 3 + .../plotsystem/utils/io/ConfigUtil.java | 2 +- src/main/resources/config.yml | 24 +++- 5 files changed, 107 insertions(+), 47 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d8343991..a76e18e6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ com-alpsbte-alpslib-alpslib-hologram = "1.1.3" # https://mvn.alps-bte.com/service/rest/repository/browse/alps-bte/com/alpsbte/alpslib/alpslib-io/ com-alpsbte-alpslib-alpslib-io = "1.2.5" # https://mvn.alps-bte.com/service/rest/repository/browse/alps-bte/com/alpsbte/alpslib/alpslib-utils/ -com-alpsbte-alpslib-alpslib-utils = "1.4.6" +com-alpsbte-alpslib-alpslib-utils = "1.5.0" # https://mvn.alps-bte.com/service/rest/repository/browse/alps-bte/com/alpsbte/canvas/ com-alpsbte-canvas = "1.3" # https://github.com/Arcaniax-Development/HeadDatabase-API/releases 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 db053619..83aea3c4 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 @@ -1,5 +1,9 @@ package com.alpsbte.plotsystem.core.system.plot.utils; +import com.alpsbte.alpslib.utils.MapLink; +import com.alpsbte.alpslib.utils.MapLinkEntryConfig; +import com.alpsbte.alpslib.utils.MapLinks; +import com.alpsbte.alpslib.utils.MapLinksConfig; import com.alpsbte.plotsystem.PlotSystem; import com.alpsbte.plotsystem.commands.plot.CMD_Plot_Members; import com.alpsbte.plotsystem.core.database.DataProvider; @@ -25,8 +29,6 @@ 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.mask.BlockTypeMask; -import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.ForwardExtentCopy; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.math.BlockVector2; @@ -34,9 +36,6 @@ import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Polygonal2DRegion; -import com.sk89q.worldedit.world.block.BlockTypes; -import com.sk89q.worldguard.WorldGuard; -import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; @@ -44,6 +43,7 @@ import org.bukkit.Location; import org.bukkit.Particle; import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; import org.jetbrains.annotations.NotNull; @@ -57,7 +57,9 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.text.DecimalFormat; +import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -384,46 +386,30 @@ private ChatFormatting() {} public static void sendLinkMessages(AbstractPlot plot, Player player) { Bukkit.getScheduler().runTaskAsynchronously(PlotSystem.getPlugin(), () -> { - Component[] tc = new Component[4]; - - String shortLinkGoogleMaps = null; - String shortLinkGoogleEarth = null; - String shortLinkOSM = null; - String shortLinkAppleLookAround = null; - String googleMaps = " Google Maps "; - String googleEarthWeb = " Google Earth Web "; - String openStreetMap = " Open Street Map "; - String appleLookAround = " Apple Maps Look Around "; + List linkMessages; try { - if (PlotSystem.getPlugin().getConfig().getBoolean(ConfigPaths.SHORTLINK_ENABLE)) { - shortLinkGoogleMaps = ShortLink.generateShortLink(plot.getGoogleMapsLink()); - tc[0] = text("» ", DARK_GRAY).append(LangUtil.getInstance().getComponent(player.getUniqueId(), LangPaths.Note.Action.CLICK_TO_OPEN_LINK_WITH_SHORTLINK, GRAY, text(googleMaps, GREEN), text(shortLinkGoogleMaps, GREEN))); - shortLinkGoogleEarth = ShortLink.generateShortLink(plot.getGoogleEarthLink()); - tc[1] = text("» ", DARK_GRAY).append(LangUtil.getInstance().getComponent(player.getUniqueId(), LangPaths.Note.Action.CLICK_TO_OPEN_LINK_WITH_SHORTLINK, GRAY, text(googleEarthWeb, GREEN), text(shortLinkGoogleEarth, GREEN))); - shortLinkOSM = ShortLink.generateShortLink(plot.getOSMMapsLink()); - tc[2] = text("» ", DARK_GRAY).append(LangUtil.getInstance().getComponent(player.getUniqueId(), LangPaths.Note.Action.CLICK_TO_OPEN_LINK_WITH_SHORTLINK, GRAY, text(openStreetMap, GREEN), text(shortLinkOSM, GREEN))); - shortLinkAppleLookAround = ShortLink.generateShortLink(plot.getAppleLookAroundLink()); - tc[3] = text("» ", DARK_GRAY).append(LangUtil.getInstance().getComponent(player.getUniqueId(), LangPaths.Note.Action.CLICK_TO_OPEN_LINK_WITH_SHORTLINK, GRAY, text(appleLookAround, GREEN), text(shortLinkAppleLookAround, GREEN))); - } else { - tc[0] = text("» ", DARK_GRAY).append(LangUtil.getInstance().getComponent(player.getUniqueId(), LangPaths.Note.Action.CLICK_TO_OPEN_LINK, GRAY, text(googleMaps, GREEN))); - tc[1] = text("» ", DARK_GRAY).append(LangUtil.getInstance().getComponent(player.getUniqueId(), LangPaths.Note.Action.CLICK_TO_OPEN_LINK, GRAY, text(googleEarthWeb, GREEN))); - tc[2] = text("» ", DARK_GRAY).append(LangUtil.getInstance().getComponent(player.getUniqueId(), LangPaths.Note.Action.CLICK_TO_OPEN_LINK, GRAY, text(openStreetMap, GREEN))); - tc[3] = text("» ", DARK_GRAY).append(LangUtil.getInstance().getComponent(player.getUniqueId(), LangPaths.Note.Action.CLICK_TO_OPEN_LINK, GRAY, text(appleLookAround, GREEN))); + String[] coordsSplit = plot.getGeoCoordinates().split(","); + double lat = Double.parseDouble(coordsSplit[0]); + double lon = Double.parseDouble(coordsSplit[1]); + + MapLinksConfig mapLinksConfig = getMapLinksConfigFromPlugin(); + MapLinks mapLinks = mapLinksConfig == null ? new MapLinks(lat, lon) : new MapLinks(lat, lon, mapLinksConfig); + + if (mapLinks.getAllLinks().isEmpty()) { + PlotSystem.getPlugin().getComponentLogger().warn(text("No map links are enabled for plot #" + plot.getId())); + return; } - tc[0] = tc[0].clickEvent(ClickEvent.openUrl((shortLinkGoogleMaps != null) ? shortLinkGoogleMaps : plot.getGoogleMapsLink())); - tc[1] = tc[1].clickEvent(ClickEvent.openUrl((shortLinkGoogleEarth != null) ? shortLinkGoogleEarth : plot.getGoogleEarthLink())); - tc[2] = tc[2].clickEvent(ClickEvent.openUrl((shortLinkOSM != null) ? shortLinkOSM : plot.getOSMMapsLink())); - tc[3] = tc[3].clickEvent(ClickEvent.openUrl((shortLinkAppleLookAround != null) ? shortLinkAppleLookAround : plot.getAppleLookAroundLink())); - } catch (IOException | URISyntaxException ex) { - PlotSystem.getPlugin().getComponentLogger().error(text("An error occurred while creating short link!"), ex); + boolean shortLinksEnabled = PlotSystem.getPlugin().getConfig().getBoolean(ConfigPaths.SHORTLINK_ENABLE); + linkMessages = new ArrayList<>(mapLinks.getAllLinks().size()); + for (MapLink link : mapLinks.getAllLinks()) { + linkMessages.add(buildMapLinkMessage(player, link.name(), link.url(), shortLinksEnabled)); + } + } catch (IOException | URISyntaxException | NumberFormatException | NullPointerException ex) { + PlotSystem.getPlugin().getComponentLogger().error(text("An error occurred while creating map links!"), ex); + return; } - tc[0] = tc[0].hoverEvent(text(googleMaps)); - tc[1] = tc[1].hoverEvent(text(googleEarthWeb)); - tc[2] = tc[2].hoverEvent(text(openStreetMap)); - tc[3] = tc[3].hoverEvent(text(appleLookAround)); - // Temporary fix for bedrock players Component coords = null; try { @@ -442,16 +428,65 @@ public static void sendLinkMessages(AbstractPlot plot, Player player) { player.sendMessage(text(MSG_LINE, DARK_GRAY)); if (coords != null) player.sendMessage(text("Coords: ", GRAY).append(coords)); - player.sendMessage(tc[0]); - player.sendMessage(tc[1]); - player.sendMessage(tc[2]); - player.sendMessage(tc[3]); + for (Component linkMessage : linkMessages) { + player.sendMessage(linkMessage); + } player.sendMessage(text(MSG_LINE, DARK_GRAY)); if (plot instanceof Plot p) sendGroupTipMessage(p, player); }); } + private static @NotNull Component buildMapLinkMessage(@NotNull Player player, @NotNull String providerName, @NotNull String targetUrl, + boolean shortLinksEnabled) throws IOException, URISyntaxException { + String providerLabel = " " + providerName + " "; + Component message; + String clickUrl = targetUrl; + + if (shortLinksEnabled) { + clickUrl = ShortLink.generateShortLink(targetUrl); + message = text("» ", DARK_GRAY) + .append(LangUtil.getInstance().getComponent(player.getUniqueId(), LangPaths.Note.Action.CLICK_TO_OPEN_LINK_WITH_SHORTLINK, + GRAY, text(providerLabel, GREEN), text(clickUrl, GREEN))); + } else { + message = text("» ", DARK_GRAY) + .append(LangUtil.getInstance().getComponent(player.getUniqueId(), LangPaths.Note.Action.CLICK_TO_OPEN_LINK, + GRAY, text(providerLabel, GREEN))); + } + + return message + .clickEvent(ClickEvent.openUrl(clickUrl)) + .hoverEvent(text(providerLabel)); + } + + private static @Nullable MapLinksConfig getMapLinksConfigFromPlugin() { + ConfigurationSection linksRoot = PlotSystem.getPlugin().getConfig().getConfigurationSection(ConfigPaths.MAP_LINKS); + if (linksRoot == null) { + return null; + } + + ConfigurationSection linksSection = linksRoot.getConfigurationSection("links"); + if (linksSection == null) { + return null; + } + + Map links = new LinkedHashMap<>(); + for (String key : linksSection.getKeys(false)) { + ConfigurationSection entry = linksSection.getConfigurationSection(key); + if (entry == null) { + continue; + } + + String id = entry.getString("id", key); + String name = entry.getString("name"); + String template = entry.getString("url-template", entry.getString("urlTemplate")); + Boolean enabled = entry.isSet("enabled") ? entry.getBoolean("enabled") : null; + links.put(key, new MapLinkEntryConfig(id, name, template, enabled)); + } + + return links.isEmpty() ? null : new MapLinksConfig(links); + } + public static void sendGroupTipMessage(@NotNull Plot plot, Player player) { if (plot.getPlotMembers().isEmpty() && PlotSystem.getPlugin().getConfig().getBoolean(ConfigPaths.ENABLE_GROUP_SUPPORT) && player.hasPermission(CMD_Plot_Members.PERMISSION)) { diff --git a/src/main/java/com/alpsbte/plotsystem/utils/io/ConfigPaths.java b/src/main/java/com/alpsbte/plotsystem/utils/io/ConfigPaths.java index 0f263ca1..dfd2cb39 100644 --- a/src/main/java/com/alpsbte/plotsystem/utils/io/ConfigPaths.java +++ b/src/main/java/com/alpsbte/plotsystem/utils/io/ConfigPaths.java @@ -44,6 +44,9 @@ public abstract class ConfigPaths { public static final String SHORTLINK_APIKEY = SHORTLINK + "apikey"; public static final String SHORTLINK_HOST = SHORTLINK + "host"; + // MAP LINKS + public static final String MAP_LINKS = "map-links"; + // TUTORIALS private static final String TUTORIALS = "tutorials."; public static final String TUTORIAL_ENABLE = TUTORIALS + "tutorial-enable"; diff --git a/src/main/java/com/alpsbte/plotsystem/utils/io/ConfigUtil.java b/src/main/java/com/alpsbte/plotsystem/utils/io/ConfigUtil.java index 45dbd18e..85dbe948 100644 --- a/src/main/java/com/alpsbte/plotsystem/utils/io/ConfigUtil.java +++ b/src/main/java/com/alpsbte/plotsystem/utils/io/ConfigUtil.java @@ -20,7 +20,7 @@ private ConfigUtil() {} public static void init() throws ConfigNotImplementedException { if (configUtilInstance == null) { configUtilInstance = new ConfigurationUtil(new ConfigurationUtil.ConfigFile[]{ - new ConfigurationUtil.ConfigFile(Paths.get("config.yml"), 4.2, true), + new ConfigurationUtil.ConfigFile(Paths.get("config.yml"), 4.3, true), new ConfigurationUtil.ConfigFile(Paths.get("commands.yml"), 1.1, false), new ConfigurationUtil.ConfigFile(Paths.get("items.yml"), 1.3, false) }); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 1ef96243..06f92a8d 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -91,6 +91,28 @@ shortlink: host: https://your.shortlink.host +# ----------------------------------------------------- +# | Map Links +# | Optional overrides for provider links shown in /plot messages. +# | You can disable defaults or add custom providers. +# | Supported placeholders in url-template: {lat}, {lon} +# ----------------------------------------------------- +# map-links: +# links: +# googleMaps: +# enabled: true +# googleEarth: +# enabled: true +# osm: +# enabled: true +# appleLookAround: +# enabled: true +# bingMaps: +# name: "Bing Maps" +# url-template: "https://www.bing.com/maps?q={lat},{lon}" +# enabled: true + + # ----------------------------------------------------- # | Tutorials # ----------------------------------------------------- @@ -104,4 +126,4 @@ tutorials: # NOTE: Do not change -config-version: 4.2 \ No newline at end of file +config-version: 4.3 \ No newline at end of file