From 9188c8c40de30609b2948410e7e570a8427fe60e Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Wed, 8 Jun 2022 21:12:16 +0100 Subject: [PATCH] Move to new generation API - Currently not working due to lack of biome-setting capability via BiomeProvider for flat worlds --- .../bukkit/generator/BukkitPlotGenerator.java | 262 ++++++++++++++---- .../generator/DelegatePlotGenerator.java | 8 +- .../com/plotsquared/core/PlotSquared.java | 4 +- .../core/generator/AugmentedUtils.java | 2 +- .../plotsquared/core/generator/HybridGen.java | 89 +++--- .../core/generator/HybridUtils.java | 2 +- .../generator/IndependentPlotGenerator.java | 18 +- .../core/generator/SingleWorldGenerator.java | 24 +- .../plotsquared/core/location/Location.java | 4 +- .../core/location/UncheckedWorldLocation.java | 68 +++++ .../plotsquared/core/plot/BlockBucket.java | 86 +++--- .../plot/world/DefaultPlotAreaManager.java | 5 +- .../core/plot/world/PlotAreaManager.java | 3 +- .../plot/world/SinglePlotAreaManager.java | 4 +- 14 files changed, 434 insertions(+), 145 deletions(-) create mode 100644 Core/src/main/java/com/plotsquared/core/location/UncheckedWorldLocation.java diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/generator/BukkitPlotGenerator.java b/Bukkit/src/main/java/com/plotsquared/bukkit/generator/BukkitPlotGenerator.java index d9cfdd89d..d84630e04 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/generator/BukkitPlotGenerator.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/generator/BukkitPlotGenerator.java @@ -29,28 +29,35 @@ import com.plotsquared.bukkit.queue.GenChunk; import com.plotsquared.bukkit.util.BukkitUtil; import com.plotsquared.bukkit.util.BukkitWorld; import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.generator.ClassicPlotWorld; import com.plotsquared.core.generator.GeneratorWrapper; import com.plotsquared.core.generator.IndependentPlotGenerator; import com.plotsquared.core.generator.SingleWorldGenerator; import com.plotsquared.core.location.ChunkWrapper; +import com.plotsquared.core.location.UncheckedWorldLocation; import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.plot.world.PlotAreaManager; import com.plotsquared.core.queue.ZeroedDelegateScopedQueueCoordinator; import com.plotsquared.core.util.ChunkManager; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.math.BlockVector2; +import org.bukkit.HeightMap; import org.bukkit.World; import org.bukkit.block.Biome; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; +import org.bukkit.generator.WorldInfo; import org.checkerframework.checker.nullness.qual.NonNull; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.Set; -public class BukkitPlotGenerator extends ChunkGenerator - implements GeneratorWrapper { +public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrapper { @SuppressWarnings("unused") public final boolean PAPER_ASYNC_SAFE = true; @@ -60,9 +67,15 @@ public class BukkitPlotGenerator extends ChunkGenerator private final ChunkGenerator platformGenerator; private final boolean full; private final String levelName; + private final boolean useNewGenerationMethods; + private final BiomeProvider biomeProvider; private List populators; private boolean loaded = false; + private PlotArea lastPlotArea; + private int lastChunkX = Integer.MIN_VALUE; + private int lastChunkZ = Integer.MIN_VALUE; + public BukkitPlotGenerator( final @NonNull String name, final @NonNull IndependentPlotGenerator generator, @@ -75,18 +88,23 @@ public class BukkitPlotGenerator extends ChunkGenerator this.populators = new ArrayList<>(); this.populators.add(new BlockStatePopulator(this.plotGenerator, this.plotAreaManager)); this.full = true; + this.useNewGenerationMethods = PlotSquared.platform().serverVersion()[1] >= 17; + this.biomeProvider = new BukkitPlotBiomeProvider(); } public BukkitPlotGenerator(final String world, final ChunkGenerator cg, final @NonNull PlotAreaManager plotAreaManager) { if (cg instanceof BukkitPlotGenerator) { - throw new IllegalArgumentException("ChunkGenerator: " + cg.getClass().getName() - + " is already a BukkitPlotGenerator!"); + throw new IllegalArgumentException("ChunkGenerator: " + cg + .getClass() + .getName() + " is already a BukkitPlotGenerator!"); } this.plotAreaManager = plotAreaManager; this.levelName = world; this.full = false; this.platformGenerator = cg; this.plotGenerator = new DelegatePlotGenerator(cg, world); + this.useNewGenerationMethods = PlotSquared.platform().serverVersion()[1] >= 17; + this.biomeProvider = null; } @Override @@ -112,30 +130,7 @@ public class BukkitPlotGenerator extends ChunkGenerator @Override public @NonNull List getDefaultPopulators(@NonNull World world) { try { - if (!this.loaded) { - String name = world.getName(); - PlotSquared.get().loadWorld(name, this); - final Set areas = this.plotAreaManager.getPlotAreasSet(name); - if (!areas.isEmpty()) { - PlotArea area = areas.iterator().next(); - if (!area.isMobSpawning()) { - if (!area.isSpawnEggs()) { - world.setSpawnFlags(false, false); - } - world.setAmbientSpawnLimit(0); - world.setAnimalSpawnLimit(0); - world.setMonsterSpawnLimit(0); - world.setWaterAnimalSpawnLimit(0); - } else { - world.setSpawnFlags(true, true); - world.setAmbientSpawnLimit(-1); - world.setAnimalSpawnLimit(-1); - world.setMonsterSpawnLimit(-1); - world.setWaterAnimalSpawnLimit(-1); - } - } - this.loaded = true; - } + checkLoaded(world); } catch (Exception e) { e.printStackTrace(); } @@ -154,12 +149,138 @@ public class BukkitPlotGenerator extends ChunkGenerator return toAdd; } - @Override - public @NonNull ChunkData generateChunkData( - @NonNull World world, @NonNull Random random, int x, int z, - @NonNull BiomeGrid biome - ) { + // Extracted to synchronized method for thread-safety, preventing multiple internal world load calls + private synchronized void checkLoaded(World world) { + if (!this.loaded) { + String name = world.getName(); + PlotSquared.get().loadWorld(name, this); + final Set areas = this.plotAreaManager.getPlotAreasSet(name); + if (!areas.isEmpty()) { + PlotArea area = areas.iterator().next(); + if (!area.isMobSpawning()) { + if (!area.isSpawnEggs()) { + world.setSpawnFlags(false, false); + } + setSpawnLimits(world, 0); + } else { + world.setSpawnFlags(true, true); + setSpawnLimits(world, -1); + } + } + this.loaded = true; + } + } + @SuppressWarnings("deprecation") // Kept for compatibility with <=1.17.1 + private void setSpawnLimits(World world, int spawnLimit) { + world.setAmbientSpawnLimit(spawnLimit); + world.setAnimalSpawnLimit(spawnLimit); + world.setMonsterSpawnLimit(spawnLimit); + world.setWaterAnimalSpawnLimit(spawnLimit); + } + + @Override + public void generateNoise( + @NotNull final WorldInfo worldInfo, + @NotNull final Random random, + final int chunkX, + final int chunkZ, + @NotNull final ChunkData chunkData + ) { + if (this.platformGenerator != this) { + this.platformGenerator.generateNoise(worldInfo, random, chunkX, chunkZ, chunkData); + return; + } + int minY = chunkData.getMinHeight(); + int maxY = chunkData.getMaxHeight(); + GenChunk result = new GenChunk(minY, maxY); + // Set the chunk location + result.setChunk(new ChunkWrapper(worldInfo.getName(), chunkX, chunkZ)); + // Set the result data + result.setChunkData(chunkData); + result.result = null; + + // Catch any exceptions (as exceptions usually thrown) + try { + generate(BlockVector2.at(chunkX, chunkZ), worldInfo.getName(), result, false); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + @Override + public void generateSurface( + @NotNull final WorldInfo worldInfo, + @NotNull final Random random, + final int chunkX, + final int chunkZ, + @NotNull final ChunkData chunkData + ) { + if (platformGenerator != this) { + platformGenerator.generateSurface(worldInfo, random, chunkX, chunkZ, chunkData); + } + } + + @Override + public void generateBedrock( + @NotNull final WorldInfo worldInfo, + @NotNull final Random random, + final int chunkX, + final int chunkZ, + @NotNull final ChunkData chunkData + ) { + if (platformGenerator != this) { + platformGenerator.generateBedrock(worldInfo, random, chunkX, chunkZ, chunkData); + } + } + + @Override + public void generateCaves( + @NotNull final WorldInfo worldInfo, + @NotNull final Random random, + final int chunkX, + final int chunkZ, + @NotNull final ChunkData chunkData + ) { + if (platformGenerator != this) { + platformGenerator.generateCaves(worldInfo, random, chunkX, chunkZ, chunkData); + } + } + + @Override + public @Nullable BiomeProvider getDefaultBiomeProvider(@NotNull final WorldInfo worldInfo) { + if (platformGenerator != this) { + return platformGenerator.getDefaultBiomeProvider(worldInfo); + } + return biomeProvider; + } + + @Override + public int getBaseHeight( + @NotNull final WorldInfo worldInfo, + @NotNull final Random random, + final int x, + final int z, + @NotNull final HeightMap heightMap + ) { + PlotArea area = getPlotArea(worldInfo.getName(), x, z); + if (area instanceof ClassicPlotWorld cpw) { + // Default to plot height being the heighest point before decoration (i.e. roads, walls etc.) + return cpw.PLOT_HEIGHT; + } + return super.getBaseHeight(worldInfo, random, x, z, heightMap); + } + + @SuppressWarnings("deprecation") // The entire method is deprecated, but kept for compatibility with <=1.16.2 + @Override + @Deprecated(since = "TODO") + public @NonNull ChunkData generateChunkData( + @NonNull World world, @NonNull Random random, int x, int z, @NonNull BiomeGrid biome + ) { + if (useNewGenerationMethods) { + // Return super as it will throw an exception caught by the server that will mean this method is no longer used. + return super.generateChunkData(world, random, x, z, biome); + } int minY = BukkitWorld.getMinWorldHeight(world); int maxY = BukkitWorld.getMaxWorldHeight(world); GenChunk result = new GenChunk(minY, maxY); @@ -169,7 +290,6 @@ public class BukkitPlotGenerator extends ChunkGenerator for (int chunkZ = 0; chunkZ < 16; chunkZ++) { for (int y = minY; y < maxY; y++) { biome.setBiome(chunkX, y, chunkZ, Biome.PLAINS); - } } } @@ -189,7 +309,7 @@ public class BukkitPlotGenerator extends ChunkGenerator if (this.platformGenerator != this) { return this.platformGenerator.generateChunkData(world, random, x, z, biome); } else { - generate(BlockVector2.at(x, z), world, result); + generate(BlockVector2.at(x, z), world.getName(), result, true); } } catch (Throwable e) { e.printStackTrace(); @@ -198,25 +318,14 @@ public class BukkitPlotGenerator extends ChunkGenerator return result.getChunkData(); } - private void generate(BlockVector2 loc, World world, ZeroedDelegateScopedQueueCoordinator result) { - // Load if improperly loaded - if (!this.loaded) { - String name = world.getName(); - PlotSquared.get().loadWorld(name, this); - this.loaded = true; - } + private void generate(BlockVector2 loc, String world, ZeroedDelegateScopedQueueCoordinator result, boolean biomes) { // Process the chunk if (ChunkManager.preProcessChunk(loc, result)) { return; } - PlotArea area = this.plotAreaManager.getPlotArea(world.getName(), null); - if (area == null && (area = this.plotAreaManager.getPlotArea(this.levelName, null)) == null) { - throw new IllegalStateException( - "Cannot regenerate chunk that does not belong to a plot area." + " Location: " + loc - + ", world: " + world); - } + PlotArea area = getPlotArea(world, loc.getX(), loc.getZ()); try { - this.plotGenerator.generateChunk(result, area); + this.plotGenerator.generateChunk(result, area, biomes); } catch (Throwable e) { // Recover from generator error e.printStackTrace(); @@ -273,4 +382,59 @@ public class BukkitPlotGenerator extends ChunkGenerator return this.levelName; } + private synchronized PlotArea getPlotArea(String name, int chunkX, int chunkZ) { + // Load if improperly loaded + if (!this.loaded) { + PlotSquared.get().loadWorld(name, this); + // Do not set loaded to true as we want to ensure spawn limits are set when "loading" is actually able to be + // completed properly. + } + if (lastPlotArea != null && name.equals(this.levelName) && chunkX == lastChunkX && chunkZ == lastChunkZ) { + return lastPlotArea; + } + PlotArea area = UncheckedWorldLocation.at(name, chunkX << 4, 0, chunkZ << 4).getPlotArea(); + if (area == null) { + throw new IllegalStateException(String.format("Cannot generate chunk that does not belong to a plot area. World: %s", + name + )); + } + this.lastChunkX = chunkX; + this.lastChunkZ = chunkZ; + return this.lastPlotArea = area; + } + + private PlotArea getPlotArea(String name) { + final Set areas = this.plotAreaManager.getPlotAreasSet(name); + if (!areas.isEmpty()) { + return areas.iterator().next(); + } + return null; + } + + /** + * Biome provider should never need to be accessed outside of this class. + */ + private final class BukkitPlotBiomeProvider extends BiomeProvider { + + private static final List BIOMES; + + static { + ArrayList biomes = new ArrayList<>(List.of(Biome.values())); + biomes.remove(Biome.CUSTOM); + BIOMES = List.copyOf(biomes); + } + + @Override + public @NotNull Biome getBiome(@NotNull final WorldInfo worldInfo, final int x, final int y, final int z) { + PlotArea area = getPlotArea(worldInfo.getName(), x >> 4, z >> 4); + return BukkitAdapter.adapt(plotGenerator.getBiome(area, x, y, z)); + } + + @Override + public @NotNull List getBiomes(@NotNull final WorldInfo worldInfo) { + return BIOMES; // Allow all biomes + } + + } + } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/generator/DelegatePlotGenerator.java b/Bukkit/src/main/java/com/plotsquared/bukkit/generator/DelegatePlotGenerator.java index 7ce29ace6..c5c5415a1 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/generator/DelegatePlotGenerator.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/generator/DelegatePlotGenerator.java @@ -34,6 +34,7 @@ import com.plotsquared.core.plot.PlotId; import com.plotsquared.core.queue.ZeroedDelegateScopedQueueCoordinator; import com.plotsquared.core.util.MathMan; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.world.biome.BiomeType; import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.generator.BlockPopulator; @@ -56,6 +57,11 @@ final class DelegatePlotGenerator extends IndependentPlotGenerator { public void initialize(PlotArea area) { } + @Override + public BiomeType getBiome(final PlotArea settings, final int x, final int y, final int z) { + return null; + } + @Override public String getName() { return this.chunkGenerator.getClass().getName(); @@ -67,7 +73,7 @@ final class DelegatePlotGenerator extends IndependentPlotGenerator { } @Override - public void generateChunk(final ZeroedDelegateScopedQueueCoordinator result, PlotArea settings) { + public void generateChunk(final ZeroedDelegateScopedQueueCoordinator result, PlotArea settings, boolean biomes) { World world = BukkitUtil.getWorld(this.world); Location min = result.getMin(); int chunkX = min.getX() >> 4; diff --git a/Core/src/main/java/com/plotsquared/core/PlotSquared.java b/Core/src/main/java/com/plotsquared/core/PlotSquared.java index 9c9bb09f3..3885bd399 100644 --- a/Core/src/main/java/com/plotsquared/core/PlotSquared.java +++ b/Core/src/main/java/com/plotsquared/core/PlotSquared.java @@ -787,7 +787,9 @@ public class PlotSquared { if (world.equals("CheckingPlotSquaredGenerator")) { return; } - this.getPlotAreaManager().addWorld(world); + if (!this.getPlotAreaManager().addWorld(world)) { + return; + } Set worlds; if (this.worldConfiguration.contains("worlds")) { worlds = this.worldConfiguration.getConfigurationSection("worlds").getKeys(false); diff --git a/Core/src/main/java/com/plotsquared/core/generator/AugmentedUtils.java b/Core/src/main/java/com/plotsquared/core/generator/AugmentedUtils.java index 5a6c7b504..b9fcc0975 100644 --- a/Core/src/main/java/com/plotsquared/core/generator/AugmentedUtils.java +++ b/Core/src/main/java/com/plotsquared/core/generator/AugmentedUtils.java @@ -174,7 +174,7 @@ public class AugmentedUtils { Location.at(world, blockX, area.getMinGenHeight(), blockZ), Location.at(world, blockX + 15, area.getMaxGenHeight(), blockZ + 15) ); - generator.generateChunk(scoped, area); + generator.generateChunk(scoped, area, true); generator.populateChunk(scoped, area); } if (enqueue) { diff --git a/Core/src/main/java/com/plotsquared/core/generator/HybridGen.java b/Core/src/main/java/com/plotsquared/core/generator/HybridGen.java index 6fec46c25..79a73179e 100644 --- a/Core/src/main/java/com/plotsquared/core/generator/HybridGen.java +++ b/Core/src/main/java/com/plotsquared/core/generator/HybridGen.java @@ -61,7 +61,8 @@ public class HybridGen extends IndependentPlotGenerator { short relativeZ, int x, int z, - boolean isRoad + boolean isRoad, + boolean biomes ) { int minY; // Math.min(world.PLOT_HEIGHT, world.ROAD_HEIGHT); if ((isRoad && Settings.Schematics.PASTE_ROAD_ON_TOP) || (!isRoad && Settings.Schematics.PASTE_ON_TOP)) { @@ -77,6 +78,9 @@ public class HybridGen extends IndependentPlotGenerator { } } } + if (!biomes) { + return; + } BiomeType biome = world.G_SCH_B.get(MathMan.pair(relativeX, relativeZ)); if (biome != null) { result.setBiome(x, z, biome); @@ -84,13 +88,15 @@ public class HybridGen extends IndependentPlotGenerator { } @Override - public void generateChunk(@NonNull ZeroedDelegateScopedQueueCoordinator result, @NonNull PlotArea settings) { + public void generateChunk(@NonNull ZeroedDelegateScopedQueueCoordinator result, @NonNull PlotArea settings, boolean biomes) { Preconditions.checkNotNull(result, "result cannot be null"); Preconditions.checkNotNull(settings, "settings cannot be null"); HybridPlotWorld hybridPlotWorld = (HybridPlotWorld) settings; // Biome - result.fillBiome(hybridPlotWorld.getPlotBiome()); + if (biomes) { + result.fillBiome(hybridPlotWorld.getPlotBiome()); + } // Bedrock if (hybridPlotWorld.PLOT_BEDROCK) { for (short x = 0; x < 16; x++) { @@ -132,10 +138,8 @@ public class HybridGen extends IndependentPlotGenerator { } relativeX[i] = v; if (hybridPlotWorld.ROAD_WIDTH != 0) { - insideRoadX[i] = - v < hybridPlotWorld.PATH_WIDTH_LOWER || v > hybridPlotWorld.PATH_WIDTH_UPPER; - insideWallX[i] = - v == hybridPlotWorld.PATH_WIDTH_LOWER || v == hybridPlotWorld.PATH_WIDTH_UPPER; + insideRoadX[i] = v < hybridPlotWorld.PATH_WIDTH_LOWER || v > hybridPlotWorld.PATH_WIDTH_UPPER; + insideWallX[i] = v == hybridPlotWorld.PATH_WIDTH_LOWER || v == hybridPlotWorld.PATH_WIDTH_UPPER; } } // The Z-coordinate of a given Z coordinate, relative to the @@ -153,14 +157,12 @@ public class HybridGen extends IndependentPlotGenerator { } relativeZ[i] = v; if (hybridPlotWorld.ROAD_WIDTH != 0) { - insideRoadZ[i] = - v < hybridPlotWorld.PATH_WIDTH_LOWER || v > hybridPlotWorld.PATH_WIDTH_UPPER; - insideWallZ[i] = - v == hybridPlotWorld.PATH_WIDTH_LOWER || v == hybridPlotWorld.PATH_WIDTH_UPPER; + insideRoadZ[i] = v < hybridPlotWorld.PATH_WIDTH_LOWER || v > hybridPlotWorld.PATH_WIDTH_UPPER; + insideWallZ[i] = v == hybridPlotWorld.PATH_WIDTH_LOWER || v == hybridPlotWorld.PATH_WIDTH_UPPER; } } // generation - int startY = hybridPlotWorld.getMinGenHeight() + (hybridPlotWorld.PLOT_BEDROCK ? 1: 0); + int startY = hybridPlotWorld.getMinGenHeight() + (hybridPlotWorld.PLOT_BEDROCK ? 1 : 0); for (short x = 0; x < 16; x++) { if (insideRoadX[x]) { for (short z = 0; z < 16; z++) { @@ -169,7 +171,7 @@ public class HybridGen extends IndependentPlotGenerator { result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern()); } if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { - placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true); + placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, biomes); } } } else if (insideWallX[x]) { @@ -180,9 +182,7 @@ public class HybridGen extends IndependentPlotGenerator { result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern()); } if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { - placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, - true - ); + placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, biomes); } } else { // wall @@ -191,14 +191,10 @@ public class HybridGen extends IndependentPlotGenerator { } if (!hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { if (hybridPlotWorld.PLACE_TOP_BLOCK) { - result.setBlock(x, hybridPlotWorld.WALL_HEIGHT + 1, z, - hybridPlotWorld.WALL_BLOCK.toPattern() - ); + result.setBlock(x, hybridPlotWorld.WALL_HEIGHT + 1, z, hybridPlotWorld.WALL_BLOCK.toPattern()); } } else { - placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, - true - ); + placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, biomes); } } } @@ -210,9 +206,7 @@ public class HybridGen extends IndependentPlotGenerator { result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern()); } if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { - placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, - true - ); + placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, biomes); } } else if (insideWallZ[z]) { // wall @@ -221,27 +215,19 @@ public class HybridGen extends IndependentPlotGenerator { } if (!hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { if (hybridPlotWorld.PLACE_TOP_BLOCK) { - result.setBlock(x, hybridPlotWorld.WALL_HEIGHT + 1, z, - hybridPlotWorld.WALL_BLOCK.toPattern() - ); + result.setBlock(x, hybridPlotWorld.WALL_HEIGHT + 1, z, hybridPlotWorld.WALL_BLOCK.toPattern()); } } else { - placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, - true - ); + placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, biomes); } } else { // plot for (int y = startY; y < hybridPlotWorld.PLOT_HEIGHT; y++) { result.setBlock(x, y, z, hybridPlotWorld.MAIN_BLOCK.toPattern()); } - result.setBlock(x, hybridPlotWorld.PLOT_HEIGHT, z, - hybridPlotWorld.TOP_BLOCK.toPattern() - ); + result.setBlock(x, hybridPlotWorld.PLOT_HEIGHT, z, hybridPlotWorld.TOP_BLOCK.toPattern()); if (hybridPlotWorld.PLOT_SCHEMATIC) { - placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, - false - ); + placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, false, biomes); } } } @@ -259,4 +245,33 @@ public class HybridGen extends IndependentPlotGenerator { // All initialization is done in the PlotArea class } + @Override + public BiomeType getBiome(final PlotArea settings, final int worldX, final int worldY, final int worldZ) { + HybridPlotWorld hybridPlotWorld = (HybridPlotWorld) settings; + if (!hybridPlotWorld.PLOT_SCHEMATIC && !hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { + return hybridPlotWorld.getPlotBiome(); + } + int relativeX = worldX; + int relativeZ = worldZ; + if (hybridPlotWorld.ROAD_OFFSET_X != 0) { + relativeX -= hybridPlotWorld.ROAD_OFFSET_X; + } + if (hybridPlotWorld.ROAD_OFFSET_Z != 0) { + relativeZ -= hybridPlotWorld.ROAD_OFFSET_Z; + } + int size = hybridPlotWorld.PLOT_WIDTH + hybridPlotWorld.ROAD_WIDTH; + if (relativeX < 0) { + relativeX = size + (relativeX % size); + } else { + relativeX = relativeX % size; + } + if (relativeZ < 0) { + relativeZ = size + (relativeZ % size); + } else { + relativeZ = relativeZ % size; + } + BiomeType biome = hybridPlotWorld.G_SCH_B.get(MathMan.pair((short) relativeX, (short) relativeZ)); + return biome == null ? hybridPlotWorld.getPlotBiome() : biome; + } + } diff --git a/Core/src/main/java/com/plotsquared/core/generator/HybridUtils.java b/Core/src/main/java/com/plotsquared/core/generator/HybridUtils.java index a8aac4eed..e1dd56bf4 100644 --- a/Core/src/main/java/com/plotsquared/core/generator/HybridUtils.java +++ b/Core/src/main/java/com/plotsquared/core/generator/HybridUtils.java @@ -170,7 +170,7 @@ public class HybridUtils { int relChunkZ = chunkPos.getZ() - cbz; oldBlockQueue.setOffsetX(relChunkX << 4); oldBlockQueue.setOffsetZ(relChunkZ << 4); - hpw.getGenerator().generateChunk(oldBlockQueue, hpw); + hpw.getGenerator().generateChunk(oldBlockQueue, hpw, false); }); final BlockState[][][] oldBlocks = oldBlockQueue.getBlockStates(); diff --git a/Core/src/main/java/com/plotsquared/core/generator/IndependentPlotGenerator.java b/Core/src/main/java/com/plotsquared/core/generator/IndependentPlotGenerator.java index 58fa6460d..c513f7639 100644 --- a/Core/src/main/java/com/plotsquared/core/generator/IndependentPlotGenerator.java +++ b/Core/src/main/java/com/plotsquared/core/generator/IndependentPlotGenerator.java @@ -30,6 +30,7 @@ import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.plot.PlotId; import com.plotsquared.core.queue.ZeroedDelegateScopedQueueCoordinator; import com.plotsquared.core.setup.PlotAreaBuilder; +import com.sk89q.worldedit.world.biome.BiomeType; import org.checkerframework.checker.nullness.qual.NonNull; /** @@ -51,10 +52,11 @@ public abstract class IndependentPlotGenerator { * The PlotArea settings is the same one this was initialized with. * The PseudoRandom random is a fast random object. * - * @param result queue + * @param result Queue to write to * @param settings PlotArea (settings) + * @param biomes If biomes should be generated */ - public abstract void generateChunk(ZeroedDelegateScopedQueueCoordinator result, PlotArea settings); + public abstract void generateChunk(ZeroedDelegateScopedQueueCoordinator result, PlotArea settings, boolean biomes); public boolean populateChunk(ZeroedDelegateScopedQueueCoordinator result, PlotArea setting) { return false; @@ -100,6 +102,18 @@ public abstract class IndependentPlotGenerator { return (GeneratorWrapper) PlotSquared.platform().wrapPlotGenerator(world, this); } + /** + * Get the biome to be generated at a specific point + * + * @param settings PlotArea settings to provide biome + * @param x World x position + * @param y World y position + * @param z World z position + * @return Biome type to be generated + * @since TODO + */ + public abstract BiomeType getBiome(PlotArea settings, int x, int y, int z); + @Override public String toString() { return getName(); diff --git a/Core/src/main/java/com/plotsquared/core/generator/SingleWorldGenerator.java b/Core/src/main/java/com/plotsquared/core/generator/SingleWorldGenerator.java index f7c200364..18325359b 100644 --- a/Core/src/main/java/com/plotsquared/core/generator/SingleWorldGenerator.java +++ b/Core/src/main/java/com/plotsquared/core/generator/SingleWorldGenerator.java @@ -33,7 +33,9 @@ import com.plotsquared.core.plot.world.PlotAreaManager; import com.plotsquared.core.plot.world.SinglePlotArea; import com.plotsquared.core.plot.world.SinglePlotAreaManager; import com.plotsquared.core.queue.ZeroedDelegateScopedQueueCoordinator; +import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; import org.checkerframework.checker.nullness.qual.NonNull; @@ -45,6 +47,9 @@ public class SingleWorldGenerator extends IndependentPlotGenerator { private static final Location dirt2 = Location.at("", 15, 2, 15); private static final Location grass1 = Location.at("", 0, 3, 0); private static final Location grass2 = Location.at("", 15, 3, 15); + private static final BlockState BEDROCK = BlockTypes.BEDROCK.getDefaultState(); + private static final BlockState DIRT = BlockTypes.DIRT.getDefaultState(); + private static final BlockState GRASS_BLOCK = BlockTypes.GRASS_BLOCK.getDefaultState(); private final PlotAreaManager plotAreaManager; @@ -59,19 +64,21 @@ public class SingleWorldGenerator extends IndependentPlotGenerator { } @Override - public void generateChunk(ZeroedDelegateScopedQueueCoordinator result, PlotArea settings) { + public void generateChunk(ZeroedDelegateScopedQueueCoordinator result, PlotArea settings, boolean biomes) { SinglePlotArea area = (SinglePlotArea) settings; if (area.VOID) { Location min = result.getMin(); if (min.getX() == 0 && min.getZ() == 0) { - result.setBlock(0, 0, 0, BlockTypes.BEDROCK.getDefaultState()); + result.setBlock(0, 0, 0, BEDROCK); } } else { - result.setCuboid(bedrock1, bedrock2, BlockTypes.BEDROCK.getDefaultState()); - result.setCuboid(dirt1, dirt2, BlockTypes.DIRT.getDefaultState()); - result.setCuboid(grass1, grass2, BlockTypes.GRASS_BLOCK.getDefaultState()); + result.setCuboid(bedrock1, bedrock2, BEDROCK); + result.setCuboid(dirt1, dirt2, DIRT); + result.setCuboid(grass1, grass2, GRASS_BLOCK); + } + if (biomes) { + result.fillBiome(BiomeTypes.PLAINS); } - result.fillBiome(BiomeTypes.PLAINS); } @Override @@ -83,4 +90,9 @@ public class SingleWorldGenerator extends IndependentPlotGenerator { public void initialize(PlotArea area) { } + @Override + public BiomeType getBiome(final PlotArea settings, final int x, final int y, final int z) { + return BiomeTypes.PLAINS; + } + } diff --git a/Core/src/main/java/com/plotsquared/core/location/Location.java b/Core/src/main/java/com/plotsquared/core/location/Location.java index cb37d41af..7737e806c 100644 --- a/Core/src/main/java/com/plotsquared/core/location/Location.java +++ b/Core/src/main/java/com/plotsquared/core/location/Location.java @@ -41,14 +41,14 @@ import org.khelekore.prtree.SimpleMBR; * An unmodifiable 6-tuple (world,x,y,z,yaw,pitch) */ @SuppressWarnings("unused") -public final class Location extends BlockLoc implements Comparable { +public class Location extends BlockLoc implements Comparable { private final float yaw; private final float pitch; private final BlockVector3 blockVector3; private final World world; - private Location( + protected Location( final @NonNull World world, final @NonNull BlockVector3 blockVector3, final float yaw, final float pitch ) { diff --git a/Core/src/main/java/com/plotsquared/core/location/UncheckedWorldLocation.java b/Core/src/main/java/com/plotsquared/core/location/UncheckedWorldLocation.java new file mode 100644 index 000000000..e193c34a0 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/location/UncheckedWorldLocation.java @@ -0,0 +1,68 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2014 - 2022 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.location; + +import com.plotsquared.core.util.AnnotationHelper; +import com.sk89q.worldedit.math.BlockVector3; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Used internally for generation to reference locations in worlds that "don't exist yet". There is no guarantee that the world + * name provided by {@link UncheckedWorldLocation#getWorldName()} exists on the server. + */ +@AnnotationHelper.ApiDescription(info = "Internal use only. Subject to changes at any time.") +public class UncheckedWorldLocation extends Location { + + private final String worldName; + + private UncheckedWorldLocation( + final @NonNull String worldName, final int x, final int y, final int z + ) { + super(World.nullWorld(), BlockVector3.at(x, y, z), 0f, 0f); + this.worldName = worldName; + } + + /** + * Construct a new location with yaw and pitch equal to 0 + * + * @param world World + * @param x X coordinate + * @param y Y coordinate + * @param z Z coordinate + * @return New location + */ + public static @NonNull UncheckedWorldLocation at( + final @NonNull String world, final int x, final int y, final int z + ) { + return new UncheckedWorldLocation(world, x, y, z); + } + + @Override + public @NonNull String getWorldName() { + return this.worldName; + } + +} diff --git a/Core/src/main/java/com/plotsquared/core/plot/BlockBucket.java b/Core/src/main/java/com/plotsquared/core/plot/BlockBucket.java index 9f5b1a1e2..f6e726c5a 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/BlockBucket.java +++ b/Core/src/main/java/com/plotsquared/core/plot/BlockBucket.java @@ -117,54 +117,60 @@ public final class BlockBucket implements ConfigurationSerializable { if (isCompiled()) { return; } - this.compiled = true; - String string = this.input.toString(); - if (string.isEmpty()) { - this.single = null; - this.pattern = null; - return; - } - // Convert legacy format - boolean legacy = false; - String[] blocksStr = string.split(",(?![^\\(\\[]*[\\]\\)])"); - if (blocksStr.length == 1) { - try { - Matcher matcher = regex.matcher(string); + // Synchronized as BlockBuckets may require compilation asynchronously due to async chunk generation on Paper servers + synchronized (this) { + if (isCompiled()) { + return; + } + String string = this.input.toString(); + if (string.isEmpty()) { + this.single = null; + this.pattern = null; + return; + } + // Convert legacy format + boolean legacy = false; + String[] blocksStr = string.split(",(?![^\\(\\[]*[\\]\\)])"); + if (blocksStr.length == 1) { + try { + Matcher matcher = regex.matcher(string); + if (matcher.find()) { + String chanceStr = matcher.group("chance"); + String block = matcher.group("block"); + //noinspection PointlessNullCheck + if (chanceStr != null && block != null && !MathMan.isInteger(block) && MathMan + .isInteger(chanceStr)) { + String namespace = matcher.group("namespace"); + string = (namespace == null ? "" : namespace + ":") + block; + } + } + this.single = BlockUtil.get(string); + this.pattern = new BlockPattern(single); + return; + } catch (Exception ignore) { + } + } + for (int i = 0; i < blocksStr.length; i++) { + String entry = blocksStr[i]; + Matcher matcher = regex.matcher(entry); if (matcher.find()) { String chanceStr = matcher.group("chance"); - String block = matcher.group("block"); //noinspection PointlessNullCheck - if (chanceStr != null && block != null && !MathMan.isInteger(block) && MathMan - .isInteger(chanceStr)) { - String namespace = matcher.group("namespace"); - string = (namespace == null ? "" : namespace + ":") + block; + if (chanceStr != null && MathMan.isInteger(chanceStr)) { + String[] parts = entry.split(":"); + parts = Arrays.copyOf(parts, parts.length - 1); + entry = chanceStr + "%" + StringMan.join(parts, ":"); + blocksStr[i] = entry; + legacy = true; } } - this.single = BlockUtil.get(string); - this.pattern = new BlockPattern(single); - return; - } catch (Exception ignore) { } - } - for (int i = 0; i < blocksStr.length; i++) { - String entry = blocksStr[i]; - Matcher matcher = regex.matcher(entry); - if (matcher.find()) { - String chanceStr = matcher.group("chance"); - //noinspection PointlessNullCheck - if (chanceStr != null && MathMan.isInteger(chanceStr)) { - String[] parts = entry.split(":"); - parts = Arrays.copyOf(parts, parts.length - 1); - entry = chanceStr + "%" + StringMan.join(parts, ":"); - blocksStr[i] = entry; - legacy = true; - } + if (legacy) { + string = StringMan.join(blocksStr, ","); } + pattern = PatternUtil.parse(null, string); + this.compiled = true; } - if (legacy) { - string = StringMan.join(blocksStr, ","); - } - pattern = PatternUtil.parse(null, string); } public boolean isCompiled() { diff --git a/Core/src/main/java/com/plotsquared/core/plot/world/DefaultPlotAreaManager.java b/Core/src/main/java/com/plotsquared/core/plot/world/DefaultPlotAreaManager.java index 48c04abd0..32e9508f2 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/world/DefaultPlotAreaManager.java +++ b/Core/src/main/java/com/plotsquared/core/plot/world/DefaultPlotAreaManager.java @@ -144,15 +144,16 @@ public class DefaultPlotAreaManager implements PlotAreaManager { } @Override - public void addWorld(final @NonNull String worldName) { + public boolean addWorld(final @NonNull String worldName) { PlotWorld world = this.plotWorlds.get(worldName); if (world != null) { - return; + return false; } // Create a new empty world. When a new area is added // the world will be re-recreated with the correct type world = new StandardPlotWorld(worldName, null); this.plotWorlds.put(worldName, world); + return true; } @Override diff --git a/Core/src/main/java/com/plotsquared/core/plot/world/PlotAreaManager.java b/Core/src/main/java/com/plotsquared/core/plot/world/PlotAreaManager.java index 26a2c70b7..46fa6a514 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/world/PlotAreaManager.java +++ b/Core/src/main/java/com/plotsquared/core/plot/world/PlotAreaManager.java @@ -117,8 +117,9 @@ public interface PlotAreaManager { * Add a world * * @param worldName Name of the world to add + * @return true if successful. False if world already existed */ - void addWorld(@NonNull String worldName); + boolean addWorld(@NonNull String worldName); /** * Remove a world diff --git a/Core/src/main/java/com/plotsquared/core/plot/world/SinglePlotAreaManager.java b/Core/src/main/java/com/plotsquared/core/plot/world/SinglePlotAreaManager.java index e451fbc74..1917c9aa0 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/world/SinglePlotAreaManager.java +++ b/Core/src/main/java/com/plotsquared/core/plot/world/SinglePlotAreaManager.java @@ -179,8 +179,8 @@ public class SinglePlotAreaManager extends DefaultPlotAreaManager { } @Override - public void addWorld(final @NonNull String worldName) { - super.addWorld(worldName); + public boolean addWorld(final @NonNull String worldName) { + return super.addWorld(worldName); } @Override