Update to new Spigot generation API (#3659)

* Address deprecations in queue/generation code

* Move to new generation API
 - Currently not working due to lack of biome-setting capability via BiomeProvider for flat worlds

* Any fixes to flat world biome setting will target 1.19

* Ensure compiled is actually set to true in BlockBucket

* Delegate to platformGenerator in deprecated generation method if applicable when using new generation methods (1.19)

* Re-add wrongly removed method

* Handle exceptions using logger

* We can simplify getting relative offset using floormod

* Replace many booleans with EnumSet

* Address comments, remove needless boolean return for populateChunk
This commit is contained in:
Jordan 2022-06-22 13:57:39 +01:00 committed by GitHub
parent 6b680fb2c0
commit 75fd9b2631
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 370 additions and 123 deletions

View File

@ -22,20 +22,30 @@ import com.plotsquared.bukkit.queue.GenChunk;
import com.plotsquared.bukkit.util.BukkitUtil; import com.plotsquared.bukkit.util.BukkitUtil;
import com.plotsquared.bukkit.util.BukkitWorld; import com.plotsquared.bukkit.util.BukkitWorld;
import com.plotsquared.core.PlotSquared; import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.generator.ClassicPlotWorld;
import com.plotsquared.core.generator.GeneratorWrapper; import com.plotsquared.core.generator.GeneratorWrapper;
import com.plotsquared.core.generator.IndependentPlotGenerator; import com.plotsquared.core.generator.IndependentPlotGenerator;
import com.plotsquared.core.generator.SingleWorldGenerator; import com.plotsquared.core.generator.SingleWorldGenerator;
import com.plotsquared.core.location.ChunkWrapper; import com.plotsquared.core.location.ChunkWrapper;
import com.plotsquared.core.location.UncheckedWorldLocation;
import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.plot.PlotArea;
import com.plotsquared.core.plot.world.PlotAreaManager; import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.queue.ZeroedDelegateScopedQueueCoordinator; import com.plotsquared.core.queue.ZeroedDelegateScopedQueueCoordinator;
import com.plotsquared.core.util.ChunkManager; import com.plotsquared.core.util.ChunkManager;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector2;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.HeightMap;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.WorldInfo;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -44,6 +54,8 @@ import java.util.Set;
public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrapper<ChunkGenerator> { public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrapper<ChunkGenerator> {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + BukkitPlotGenerator.class.getSimpleName());
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final boolean PAPER_ASYNC_SAFE = true; public final boolean PAPER_ASYNC_SAFE = true;
@ -52,9 +64,15 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap
private final ChunkGenerator platformGenerator; private final ChunkGenerator platformGenerator;
private final boolean full; private final boolean full;
private final String levelName; private final String levelName;
private final boolean useNewGenerationMethods;
private final BiomeProvider biomeProvider;
private List<BlockPopulator> populators; private List<BlockPopulator> populators;
private boolean loaded = false; private boolean loaded = false;
private PlotArea lastPlotArea;
private int lastChunkX = Integer.MIN_VALUE;
private int lastChunkZ = Integer.MIN_VALUE;
public BukkitPlotGenerator( public BukkitPlotGenerator(
final @NonNull String name, final @NonNull String name,
final @NonNull IndependentPlotGenerator generator, final @NonNull IndependentPlotGenerator generator,
@ -72,18 +90,23 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap
this.populators.add(new LegacyBlockStatePopulator(this.plotGenerator)); this.populators.add(new LegacyBlockStatePopulator(this.plotGenerator));
} }
this.full = true; 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) { public BukkitPlotGenerator(final String world, final ChunkGenerator cg, final @NonNull PlotAreaManager plotAreaManager) {
if (cg instanceof BukkitPlotGenerator) { if (cg instanceof BukkitPlotGenerator) {
throw new IllegalArgumentException("ChunkGenerator: " + cg.getClass().getName() throw new IllegalArgumentException("ChunkGenerator: " + cg
+ " is already a BukkitPlotGenerator!"); .getClass()
.getName() + " is already a BukkitPlotGenerator!");
} }
this.plotAreaManager = plotAreaManager; this.plotAreaManager = plotAreaManager;
this.levelName = world; this.levelName = world;
this.full = false; this.full = false;
this.platformGenerator = cg; this.platformGenerator = cg;
this.plotGenerator = new DelegatePlotGenerator(cg, world); this.plotGenerator = new DelegatePlotGenerator(cg, world);
this.useNewGenerationMethods = PlotSquared.platform().serverVersion()[1] >= 19;
this.biomeProvider = null;
} }
@Override @Override
@ -111,7 +134,7 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap
try { try {
checkLoaded(world); checkLoaded(world);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); LOGGER.error("Error attempting to load world into PlotSquared.", e);
} }
ArrayList<BlockPopulator> toAdd = new ArrayList<>(); ArrayList<BlockPopulator> toAdd = new ArrayList<>();
List<BlockPopulator> existing = world.getPopulators(); List<BlockPopulator> existing = world.getPopulators();
@ -128,6 +151,7 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap
return toAdd; return toAdd;
} }
// Extracted to synchronized method for thread-safety, preventing multiple internal world load calls
private synchronized void checkLoaded(@NonNull World world) { private synchronized void checkLoaded(@NonNull World world) {
// Do not attempt to load configurations until WorldEdit has a platform ready. // Do not attempt to load configurations until WorldEdit has a platform ready.
if (!PlotSquared.get().isWeInitialised()) { if (!PlotSquared.get().isWeInitialised()) {
@ -153,7 +177,7 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap
} }
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation") // Kept for compatibility with <=1.17.1
private void setSpawnLimits(@NonNull World world, int limit) { private void setSpawnLimits(@NonNull World world, int limit) {
world.setAmbientSpawnLimit(limit); world.setAmbientSpawnLimit(limit);
world.setAnimalSpawnLimit(limit); world.setAnimalSpawnLimit(limit);
@ -162,10 +186,112 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap
} }
@Override @Override
public @NonNull ChunkData generateChunkData( public void generateNoise(
@NonNull World world, @NonNull Random random, int x, int z, @NotNull final WorldInfo worldInfo,
@NonNull BiomeGrid biome @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) {
LOGGER.error("Error attempting to generate chunk.", e);
}
}
@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) {
if (this.platformGenerator != this) {
return this.platformGenerator.generateChunkData(world, random, x, z, biome);
} else {
// 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 minY = BukkitWorld.getMinWorldHeight(world);
int maxY = BukkitWorld.getMaxWorldHeight(world); int maxY = BukkitWorld.getMaxWorldHeight(world);
GenChunk result = new GenChunk(minY, maxY); GenChunk result = new GenChunk(minY, maxY);
@ -175,7 +301,6 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap
for (int chunkZ = 0; chunkZ < 16; chunkZ++) { for (int chunkZ = 0; chunkZ < 16; chunkZ++) {
for (int y = minY; y < maxY; y++) { for (int y = minY; y < maxY; y++) {
biome.setBiome(chunkX, y, chunkZ, Biome.PLAINS); biome.setBiome(chunkX, y, chunkZ, Biome.PLAINS);
} }
} }
} }
@ -195,35 +320,32 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap
if (this.platformGenerator != this) { if (this.platformGenerator != this) {
return this.platformGenerator.generateChunkData(world, random, x, z, biome); return this.platformGenerator.generateChunkData(world, random, x, z, biome);
} else { } else {
generate(BlockVector2.at(x, z), world, result); generate(BlockVector2.at(x, z), world.getName(), result, true);
} }
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); LOGGER.error("Error attempting to load world into PlotSquared.", e);
} }
// Return the result data // Return the result data
return result.getChunkData(); return result.getChunkData();
} }
private void generate(BlockVector2 loc, World world, ZeroedDelegateScopedQueueCoordinator result) { private void generate(BlockVector2 loc, String world, ZeroedDelegateScopedQueueCoordinator result, boolean biomes) {
// Load if improperly loaded // Load if improperly loaded
if (!this.loaded) { if (!this.loaded) {
checkLoaded(world); synchronized (this) {
PlotSquared.get().loadWorld(world, this);
}
} }
// Process the chunk // Process the chunk
if (ChunkManager.preProcessChunk(loc, result)) { if (ChunkManager.preProcessChunk(loc, result)) {
return; return;
} }
PlotArea area = this.plotAreaManager.getPlotArea(world.getName(), null); PlotArea area = getPlotArea(world, loc.getX(), loc.getZ());
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);
}
try { try {
this.plotGenerator.generateChunk(result, area); this.plotGenerator.generateChunk(result, area, biomes);
} catch (Throwable e) { } catch (Throwable e) {
// Recover from generator error // Recover from generator error
e.printStackTrace(); LOGGER.error("Error attempting to generate chunk.", e);
} }
ChunkManager.postProcessChunk(loc, result); ChunkManager.postProcessChunk(loc, result);
} }
@ -277,4 +399,51 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap
return this.levelName; 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;
}
/**
* Biome provider should never need to be accessed outside of this class.
*/
private final class BukkitPlotBiomeProvider extends BiomeProvider {
private static final List<Biome> BIOMES;
static {
ArrayList<Biome> 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<Biome> getBiomes(@NotNull final WorldInfo worldInfo) {
return BIOMES; // Allow all biomes
}
}
} }

View File

@ -27,6 +27,7 @@ import com.plotsquared.core.plot.PlotId;
import com.plotsquared.core.queue.ZeroedDelegateScopedQueueCoordinator; import com.plotsquared.core.queue.ZeroedDelegateScopedQueueCoordinator;
import com.plotsquared.core.util.MathMan; import com.plotsquared.core.util.MathMan;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.world.biome.BiomeType;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.BlockPopulator;
@ -49,6 +50,11 @@ final class DelegatePlotGenerator extends IndependentPlotGenerator {
public void initialize(PlotArea area) { public void initialize(PlotArea area) {
} }
@Override
public BiomeType getBiome(final PlotArea settings, final int x, final int y, final int z) {
return null;
}
@Override @Override
public String getName() { public String getName() {
return this.chunkGenerator.getClass().getName(); return this.chunkGenerator.getClass().getName();
@ -60,7 +66,7 @@ final class DelegatePlotGenerator extends IndependentPlotGenerator {
} }
@Override @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); World world = BukkitUtil.getWorld(this.world);
Location min = result.getMin(); Location min = result.getMin();
int chunkX = min.getX() >> 4; int chunkX = min.getX() >> 4;

View File

@ -786,7 +786,9 @@ public class PlotSquared {
if (world.equals("CheckingPlotSquaredGenerator")) { if (world.equals("CheckingPlotSquaredGenerator")) {
return; return;
} }
this.getPlotAreaManager().addWorld(world); if (!this.getPlotAreaManager().addWorld(world)) {
return;
}
Set<String> worlds; Set<String> worlds;
if (this.worldConfiguration.contains("worlds")) { if (this.worldConfiguration.contains("worlds")) {
worlds = this.worldConfiguration.getConfigurationSection("worlds").getKeys(false); worlds = this.worldConfiguration.getConfigurationSection("worlds").getKeys(false);

View File

@ -167,7 +167,7 @@ public class AugmentedUtils {
Location.at(world, blockX, area.getMinGenHeight(), blockZ), Location.at(world, blockX, area.getMinGenHeight(), blockZ),
Location.at(world, blockX + 15, area.getMaxGenHeight(), blockZ + 15) Location.at(world, blockX + 15, area.getMaxGenHeight(), blockZ + 15)
); );
generator.generateChunk(scoped, area); generator.generateChunk(scoped, area, true);
generator.populateChunk(scoped, area); generator.populateChunk(scoped, area);
} }
if (enqueue) { if (enqueue) {

View File

@ -42,6 +42,8 @@ import com.sk89q.worldedit.world.block.BlockTypes;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.EnumSet;
public class HybridGen extends IndependentPlotGenerator { public class HybridGen extends IndependentPlotGenerator {
private static final CuboidRegion CHUNK = new CuboidRegion(BlockVector3.ZERO, BlockVector3.at(15, 396, 15)); private static final CuboidRegion CHUNK = new CuboidRegion(BlockVector3.ZERO, BlockVector3.at(15, 396, 15));
@ -64,11 +66,11 @@ public class HybridGen extends IndependentPlotGenerator {
short relativeZ, short relativeZ,
int x, int x,
int z, int z,
boolean isRoad, EnumSet<SchematicFeature> features
boolean isPopulating
) { ) {
int minY; // Math.min(world.PLOT_HEIGHT, world.ROAD_HEIGHT); int minY; // Math.min(world.PLOT_HEIGHT, world.ROAD_HEIGHT);
if ((isRoad && Settings.Schematics.PASTE_ROAD_ON_TOP) || (!isRoad && Settings.Schematics.PASTE_ON_TOP)) { if ((features.contains(SchematicFeature.ROAD) && Settings.Schematics.PASTE_ROAD_ON_TOP)
|| (!features.contains(SchematicFeature.ROAD) && Settings.Schematics.PASTE_ON_TOP)) {
minY = world.SCHEM_Y; minY = world.SCHEM_Y;
} else { } else {
minY = world.getMinBuildHeight(); minY = world.getMinBuildHeight();
@ -77,12 +79,15 @@ public class HybridGen extends IndependentPlotGenerator {
if (blocks != null) { if (blocks != null) {
for (int y = 0; y < blocks.length; y++) { for (int y = 0; y < blocks.length; y++) {
if (blocks[y] != null) { if (blocks[y] != null) {
if (!isPopulating || blocks[y].hasNbtData()) { if (!features.contains(SchematicFeature.POPULATING) || blocks[y].hasNbtData()) {
result.setBlock(x, minY + y, z, blocks[y]); result.setBlock(x, minY + y, z, blocks[y]);
} }
} }
} }
} }
if (!features.contains(SchematicFeature.BIOMES)) {
return;
}
BiomeType biome = world.G_SCH_B.get(MathMan.pair(relativeX, relativeZ)); BiomeType biome = world.G_SCH_B.get(MathMan.pair(relativeX, relativeZ));
if (biome != null) { if (biome != null) {
result.setBiome(x, z, biome); result.setBiome(x, z, biome);
@ -90,13 +95,15 @@ public class HybridGen extends IndependentPlotGenerator {
} }
@Override @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(result, "result cannot be null");
Preconditions.checkNotNull(settings, "settings cannot be null"); Preconditions.checkNotNull(settings, "settings cannot be null");
HybridPlotWorld hybridPlotWorld = (HybridPlotWorld) settings; HybridPlotWorld hybridPlotWorld = (HybridPlotWorld) settings;
// Biome // Biome
result.fillBiome(hybridPlotWorld.getPlotBiome()); if (biomes) {
result.fillBiome(hybridPlotWorld.getPlotBiome());
}
// Bedrock // Bedrock
if (hybridPlotWorld.PLOT_BEDROCK) { if (hybridPlotWorld.PLOT_BEDROCK) {
for (short x = 0; x < 16; x++) { for (short x = 0; x < 16; x++) {
@ -105,26 +112,25 @@ public class HybridGen extends IndependentPlotGenerator {
} }
} }
} }
EnumSet<SchematicFeature> roadFeatures = EnumSet.of(SchematicFeature.ROAD);
EnumSet<SchematicFeature> plotFeatures = EnumSet.noneOf(SchematicFeature.class);
if (biomes) {
roadFeatures.add(SchematicFeature.BIOMES);
plotFeatures.add(SchematicFeature.BIOMES);
}
// Coords // Coords
Location min = result.getMin(); Location min = result.getMin();
int bx = min.getX() - hybridPlotWorld.ROAD_OFFSET_X; int bx = min.getX() - hybridPlotWorld.ROAD_OFFSET_X;
int bz = min.getZ() - hybridPlotWorld.ROAD_OFFSET_Z; int bz = min.getZ() - hybridPlotWorld.ROAD_OFFSET_Z;
// The relative X-coordinate (within the plot) of the minimum X coordinate // The relative X-coordinate (within the plot) of the minimum X coordinate
// contained in the scoped queue // contained in the scoped queue
short relativeOffsetX; short relativeOffsetX = (short) Math.floorMod(bx, hybridPlotWorld.SIZE);
if (bx < 0) {
relativeOffsetX = (short) (hybridPlotWorld.SIZE + (bx % hybridPlotWorld.SIZE));
} else {
relativeOffsetX = (short) (bx % hybridPlotWorld.SIZE);
}
// The relative Z-coordinate (within the plot) of the minimum Z coordinate // The relative Z-coordinate (within the plot) of the minimum Z coordinate
// contained in the scoped queue // contained in the scoped queue
short relativeOffsetZ; short relativeOffsetZ = (short) Math.floorMod(bz, hybridPlotWorld.SIZE);
if (bz < 0) {
relativeOffsetZ = (short) (hybridPlotWorld.SIZE + (bz % hybridPlotWorld.SIZE));
} else {
relativeOffsetZ = (short) (bz % hybridPlotWorld.SIZE);
}
// The X-coordinate of a given X coordinate, relative to the // The X-coordinate of a given X coordinate, relative to the
// plot (Counting from the corner with the least positive // plot (Counting from the corner with the least positive
// coordinates) // coordinates)
@ -171,7 +177,7 @@ public class HybridGen extends IndependentPlotGenerator {
result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern()); result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern());
} }
if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false); placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, roadFeatures);
} }
} }
} else if (insideWallX[x]) { } else if (insideWallX[x]) {
@ -182,7 +188,7 @@ public class HybridGen extends IndependentPlotGenerator {
result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern()); result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern());
} }
if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false); placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, roadFeatures);
} }
} else { } else {
// wall // wall
@ -194,7 +200,7 @@ public class HybridGen extends IndependentPlotGenerator {
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 { } else {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false); placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, roadFeatures);
} }
} }
} }
@ -206,7 +212,7 @@ public class HybridGen extends IndependentPlotGenerator {
result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern()); result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern());
} }
if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false); placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, roadFeatures);
} }
} else if (insideWallZ[z]) { } else if (insideWallZ[z]) {
// wall // wall
@ -218,7 +224,7 @@ public class HybridGen extends IndependentPlotGenerator {
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 { } else {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false); placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, roadFeatures);
} }
} else { } else {
// plot // plot
@ -227,7 +233,7 @@ public class HybridGen extends IndependentPlotGenerator {
} }
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) { if (hybridPlotWorld.PLOT_SCHEMATIC) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, false, false); placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, plotFeatures);
} }
} }
} }
@ -236,31 +242,26 @@ public class HybridGen extends IndependentPlotGenerator {
} }
@Override @Override
public boolean populateChunk(final ZeroedDelegateScopedQueueCoordinator result, final PlotArea settings) { public void populateChunk(final ZeroedDelegateScopedQueueCoordinator result, final PlotArea settings) {
HybridPlotWorld hybridPlotWorld = (HybridPlotWorld) settings; HybridPlotWorld hybridPlotWorld = (HybridPlotWorld) settings;
if (!hybridPlotWorld.populationNeeded()) { if (!hybridPlotWorld.populationNeeded()) {
return false; return;
} }
EnumSet<SchematicFeature> roadFeatures = EnumSet.of(SchematicFeature.POPULATING, SchematicFeature.ROAD);
EnumSet<SchematicFeature> plotFeatures = EnumSet.of(SchematicFeature.POPULATING);
// Coords // Coords
Location min = result.getMin(); Location min = result.getMin();
int bx = min.getX() - hybridPlotWorld.ROAD_OFFSET_X; int bx = min.getX() - hybridPlotWorld.ROAD_OFFSET_X;
int bz = min.getZ() - hybridPlotWorld.ROAD_OFFSET_Z; int bz = min.getZ() - hybridPlotWorld.ROAD_OFFSET_Z;
// The relative X-coordinate (within the plot) of the minimum X coordinate // The relative X-coordinate (within the plot) of the minimum X coordinate
// contained in the scoped queue // contained in the scoped queue
short relativeOffsetX; short relativeOffsetX = (short) Math.floorMod(bx, hybridPlotWorld.SIZE);
if (bx < 0) {
relativeOffsetX = (short) (hybridPlotWorld.SIZE + (bx % hybridPlotWorld.SIZE));
} else {
relativeOffsetX = (short) (bx % hybridPlotWorld.SIZE);
}
// The relative Z-coordinate (within the plot) of the minimum Z coordinate // The relative Z-coordinate (within the plot) of the minimum Z coordinate
// contained in the scoped queue // contained in the scoped queue
short relativeOffsetZ; short relativeOffsetZ = (short) Math.floorMod(bz, hybridPlotWorld.SIZE);
if (bz < 0) {
relativeOffsetZ = (short) (hybridPlotWorld.SIZE + (bz % hybridPlotWorld.SIZE));
} else {
relativeOffsetZ = (short) (bz % hybridPlotWorld.SIZE);
}
boolean allRoad = true; boolean allRoad = true;
boolean overlap = false; boolean overlap = false;
@ -313,17 +314,17 @@ public class HybridGen extends IndependentPlotGenerator {
if (insideRoadX[x] || insideWallX[x]) { if (insideRoadX[x] || insideWallX[x]) {
if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) {
for (short z = 0; z < 16; z++) { for (short z = 0; z < 16; z++) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, true); placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, roadFeatures);
} }
} }
} else { } else {
for (short z = 0; z < 16; z++) { for (short z = 0; z < 16; z++) {
if (insideRoadZ[z] || insideWallZ[z]) { if (insideRoadZ[z] || insideWallZ[z]) {
if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, true); placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, roadFeatures);
} }
} else if (hybridPlotWorld.PLOT_SCHEMATIC) { } else if (hybridPlotWorld.PLOT_SCHEMATIC) {
placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, false, true); placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, plotFeatures);
} }
} }
} }
@ -364,7 +365,7 @@ public class HybridGen extends IndependentPlotGenerator {
} }
} }
} }
return true; return;
} }
@Override @Override
@ -377,6 +378,27 @@ public class HybridGen extends IndependentPlotGenerator {
// All initialization is done in the PlotArea class // 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;
relativeX = Math.floorMod(relativeX, size);
relativeZ = Math.floorMod(relativeZ, size);
BiomeType biome = hybridPlotWorld.G_SCH_B.get(MathMan.pair((short) relativeX, (short) relativeZ));
return biome == null ? hybridPlotWorld.getPlotBiome() : biome;
}
/** /**
* Wrapper to allow a WorldEdit {@link Entity} to effectively have a mutable location as the location in its NBT should be changed * Wrapper to allow a WorldEdit {@link Entity} to effectively have a mutable location as the location in its NBT should be changed
* when set to the world. * when set to the world.
@ -431,4 +453,10 @@ public class HybridGen extends IndependentPlotGenerator {
} }
private enum SchematicFeature {
BIOMES,
ROAD,
POPULATING
}
} }

View File

@ -163,7 +163,7 @@ public class HybridUtils {
int relChunkZ = chunkPos.getZ() - cbz; int relChunkZ = chunkPos.getZ() - cbz;
oldBlockQueue.setOffsetX(relChunkX << 4); oldBlockQueue.setOffsetX(relChunkX << 4);
oldBlockQueue.setOffsetZ(relChunkZ << 4); oldBlockQueue.setOffsetZ(relChunkZ << 4);
hpw.getGenerator().generateChunk(oldBlockQueue, hpw); hpw.getGenerator().generateChunk(oldBlockQueue, hpw, false);
}); });
final BlockState[][][] oldBlocks = oldBlockQueue.getBlockStates(); final BlockState[][][] oldBlocks = oldBlockQueue.getBlockStates();

View File

@ -23,6 +23,7 @@ import com.plotsquared.core.plot.PlotArea;
import com.plotsquared.core.plot.PlotId; import com.plotsquared.core.plot.PlotId;
import com.plotsquared.core.queue.ZeroedDelegateScopedQueueCoordinator; import com.plotsquared.core.queue.ZeroedDelegateScopedQueueCoordinator;
import com.plotsquared.core.setup.PlotAreaBuilder; import com.plotsquared.core.setup.PlotAreaBuilder;
import com.sk89q.worldedit.world.biome.BiomeType;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
/** /**
@ -42,14 +43,21 @@ public abstract class IndependentPlotGenerator {
/** /**
* Generate chunk block data * Generate chunk block data
* *
* @param result queue * @param result Queue to write to
* @param settings PlotArea (settings) * @param settings PlotArea (settings)
* @param biomes If biomes should be generated
* @since TODO
*/ */
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; * Populate a chunk-queue with tile entities, entities, etc.
} *
* @param result Queue to write to
* @param setting PlotArea (settings)
* @since TODO
*/
public void populateChunk(ZeroedDelegateScopedQueueCoordinator result, PlotArea setting) {}
/** /**
* Return a new PlotArea object. * Return a new PlotArea object.
@ -91,6 +99,18 @@ public abstract class IndependentPlotGenerator {
return (GeneratorWrapper<T>) PlotSquared.platform().wrapPlotGenerator(world, this); return (GeneratorWrapper<T>) 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 @Override
public String toString() { public String toString() {
return getName(); return getName();

View File

@ -26,7 +26,9 @@ import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.plot.world.SinglePlotArea; import com.plotsquared.core.plot.world.SinglePlotArea;
import com.plotsquared.core.plot.world.SinglePlotAreaManager; import com.plotsquared.core.plot.world.SinglePlotAreaManager;
import com.plotsquared.core.queue.ZeroedDelegateScopedQueueCoordinator; 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.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypes;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -38,6 +40,9 @@ public class SingleWorldGenerator extends IndependentPlotGenerator {
private static final Location dirt2 = Location.at("", 15, 2, 15); private static final Location dirt2 = Location.at("", 15, 2, 15);
private static final Location grass1 = Location.at("", 0, 3, 0); private static final Location grass1 = Location.at("", 0, 3, 0);
private static final Location grass2 = Location.at("", 15, 3, 15); 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; private final PlotAreaManager plotAreaManager;
@ -52,19 +57,21 @@ public class SingleWorldGenerator extends IndependentPlotGenerator {
} }
@Override @Override
public void generateChunk(ZeroedDelegateScopedQueueCoordinator result, PlotArea settings) { public void generateChunk(ZeroedDelegateScopedQueueCoordinator result, PlotArea settings, boolean biomes) {
SinglePlotArea area = (SinglePlotArea) settings; SinglePlotArea area = (SinglePlotArea) settings;
if (area.VOID) { if (area.VOID) {
Location min = result.getMin(); Location min = result.getMin();
if (min.getX() == 0 && min.getZ() == 0) { if (min.getX() == 0 && min.getZ() == 0) {
result.setBlock(0, 0, 0, BlockTypes.BEDROCK.getDefaultState()); result.setBlock(0, 0, 0, BEDROCK);
} }
} else { } else {
result.setCuboid(bedrock1, bedrock2, BlockTypes.BEDROCK.getDefaultState()); result.setCuboid(bedrock1, bedrock2, BEDROCK);
result.setCuboid(dirt1, dirt2, BlockTypes.DIRT.getDefaultState()); result.setCuboid(dirt1, dirt2, DIRT);
result.setCuboid(grass1, grass2, BlockTypes.GRASS_BLOCK.getDefaultState()); result.setCuboid(grass1, grass2, GRASS_BLOCK);
}
if (biomes) {
result.fillBiome(BiomeTypes.PLAINS);
} }
result.fillBiome(BiomeTypes.PLAINS);
} }
@Override @Override
@ -76,4 +83,9 @@ public class SingleWorldGenerator extends IndependentPlotGenerator {
public void initialize(PlotArea area) { public void initialize(PlotArea area) {
} }
@Override
public BiomeType getBiome(final PlotArea settings, final int x, final int y, final int z) {
return BiomeTypes.PLAINS;
}
} }

View File

@ -110,54 +110,62 @@ public final class BlockBucket implements ConfigurationSerializable {
if (isCompiled()) { if (isCompiled()) {
return; return;
} }
this.compiled = true; // Synchronized as BlockBuckets may require compilation asynchronously due to async chunk generation on Paper servers
String string = this.input.toString(); synchronized (this) {
if (string.isEmpty()) { if (isCompiled()) {
this.single = null; return;
this.pattern = null; }
return; String string = this.input.toString();
} if (string.isEmpty()) {
// Convert legacy format this.single = null;
boolean legacy = false; this.pattern = null;
String[] blocksStr = string.split(",(?![^\\(\\[]*[\\]\\)])"); this.compiled = true;
if (blocksStr.length == 1) { return;
try { }
Matcher matcher = regex.matcher(string); // 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);
this.compiled = true;
return;
} catch (Exception ignore) {
}
}
for (int i = 0; i < blocksStr.length; i++) {
String entry = blocksStr[i];
Matcher matcher = regex.matcher(entry);
if (matcher.find()) { if (matcher.find()) {
String chanceStr = matcher.group("chance"); String chanceStr = matcher.group("chance");
String block = matcher.group("block");
//noinspection PointlessNullCheck //noinspection PointlessNullCheck
if (chanceStr != null && block != null && !MathMan.isInteger(block) && MathMan if (chanceStr != null && MathMan.isInteger(chanceStr)) {
.isInteger(chanceStr)) { String[] parts = entry.split(":");
String namespace = matcher.group("namespace"); parts = Arrays.copyOf(parts, parts.length - 1);
string = (namespace == null ? "" : namespace + ":") + block; entry = chanceStr + "%" + StringMan.join(parts, ":");
blocksStr[i] = entry;
legacy = true;
} }
} }
this.single = BlockUtil.get(string);
this.pattern = new BlockPattern(single);
return;
} catch (Exception ignore) {
} }
} if (legacy) {
for (int i = 0; i < blocksStr.length; i++) { string = StringMan.join(blocksStr, ",");
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;
}
} }
pattern = PatternUtil.parse(null, string);
this.compiled = true;
} }
if (legacy) {
string = StringMan.join(blocksStr, ",");
}
pattern = PatternUtil.parse(null, string);
} }
public boolean isCompiled() { public boolean isCompiled() {

View File

@ -137,15 +137,16 @@ public class DefaultPlotAreaManager implements PlotAreaManager {
} }
@Override @Override
public void addWorld(final @NonNull String worldName) { public boolean addWorld(final @NonNull String worldName) {
PlotWorld world = this.plotWorlds.get(worldName); PlotWorld world = this.plotWorlds.get(worldName);
if (world != null) { if (world != null) {
return; return false;
} }
// Create a new empty world. When a new area is added // Create a new empty world. When a new area is added
// the world will be re-recreated with the correct type // the world will be re-recreated with the correct type
world = new StandardPlotWorld(worldName, null); world = new StandardPlotWorld(worldName, null);
this.plotWorlds.put(worldName, world); this.plotWorlds.put(worldName, world);
return true;
} }
@Override @Override

View File

@ -110,8 +110,10 @@ public interface PlotAreaManager {
* Add a world * Add a world
* *
* @param worldName Name of the world to add * @param worldName Name of the world to add
* @return {@code true} if successful, {@code false} if world already existed
* @since TODO
*/ */
void addWorld(@NonNull String worldName); boolean addWorld(@NonNull String worldName);
/** /**
* Remove a world * Remove a world

View File

@ -172,8 +172,8 @@ public class SinglePlotAreaManager extends DefaultPlotAreaManager {
} }
@Override @Override
public void addWorld(final @NonNull String worldName) { public boolean addWorld(final @NonNull String worldName) {
super.addWorld(worldName); return super.addWorld(worldName);
} }
@Override @Override

View File

@ -38,7 +38,6 @@ public abstract class ChunkManager {
/** /**
* @since TODO * @since TODO
*/ */
@Deprecated(forRemoval = true, since = "6.9.0")
public static void setChunkInPlotArea( public static void setChunkInPlotArea(
RunnableVal<ZeroedDelegateScopedQueueCoordinator> force, RunnableVal<ZeroedDelegateScopedQueueCoordinator> force,
RunnableVal<ZeroedDelegateScopedQueueCoordinator> add, RunnableVal<ZeroedDelegateScopedQueueCoordinator> add,