Begin to implement extended world heights:

- Implemented in Bukkit module (and where required in Core module)
This commit is contained in:
dordsor21 2022-01-25 15:22:33 +00:00 committed by Jordan
parent 40e1bd9897
commit f1d9d4bdc7
12 changed files with 164 additions and 56 deletions

View File

@ -27,6 +27,7 @@ package com.plotsquared.bukkit.generator;
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.GeneratorWrapper;
import com.plotsquared.core.generator.IndependentPlotGenerator;
@ -159,12 +160,14 @@ public class BukkitPlotGenerator extends ChunkGenerator
@NonNull BiomeGrid biome
) {
GenChunk result = new GenChunk();
int minY = BukkitWorld.getMinWorldHeight(world);
int maxY = BukkitWorld.getMaxWorldHeight(world);
GenChunk result = new GenChunk(minY, maxY);
if (this.getPlotGenerator() instanceof SingleWorldGenerator) {
if (result.getChunkData() != null) {
for (int chunkX = 0; chunkX < 16; chunkX++) {
for (int chunkZ = 0; chunkZ < 16; chunkZ++) {
for (int y = 0; y < world.getMaxHeight(); y++) {
for (int y = minY; y < maxY; y++) {
biome.setBiome(chunkX, y, chunkZ, Biome.PLAINS);
}

View File

@ -354,7 +354,7 @@ public class BlockEventListener implements Listener {
Plot plot = area.getPlot(location);
if (plot != null) {
BukkitPlayer plotPlayer = BukkitUtil.adapt(player);
if (event.getBlock().getY() == 0) {
if (event.getBlock().getY() == area.getMinBuildHeight()) {
if (!Permissions
.hasPermission(plotPlayer, Permission.PERMISSION_ADMIN_DESTROY_GROUNDLEVEL)) {
plotPlayer.sendMessage(
@ -649,7 +649,7 @@ public class BlockEventListener implements Listener {
event.getBlock().breakNaturally();
}
}
if (location.getY() == 0) {
if (location.getY() == area.getMinBuildHeight()) {
event.setCancelled(true);
return;
}

View File

@ -111,8 +111,8 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator {
public boolean enqueue() {
final Clipboard regenClipboard;
if (isRegen()) {
BlockVector3 start = BlockVector3.at(getRegenStart()[0] << 4, 0, getRegenStart()[1] << 4);
BlockVector3 end = BlockVector3.at((getRegenEnd()[0] << 4) + 15, 255, (getRegenEnd()[1] << 4) + 15);
BlockVector3 start = BlockVector3.at(getRegenStart()[0] << 4, getWorld().getMinY(), getRegenStart()[1] << 4);
BlockVector3 end = BlockVector3.at((getRegenEnd()[0] << 4) + 15, getWorld().getMaxY(), (getRegenEnd()[1] << 4) + 15);
Region region = new CuboidRegion(start, end);
regenClipboard = new BlockArrayClipboard(region);
regenClipboard.setOrigin(start);
@ -134,7 +134,7 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator {
int sx = blockVector2.getX() << 4;
int sz = blockVector2.getZ() << 4;
if (isRegenChunk) {
for (int layer = 0; layer < 16; layer++) {
for (int layer = (getWorld().getMinY() >> 4); layer < (getWorld().getMaxY() >> 4); layer++) {
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
@ -170,7 +170,7 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator {
int lx = ChunkUtil.getX(j);
int lz = ChunkUtil.getZ(j);
int x = sx + lx;
int y = ChunkUtil.getY(layer, j);
int y = ChunkUtil.getY(layer + localChunk.getMinSection(), j);
int z = sz + lz;
boolean edge = Settings.QUEUE.UPDATE_EDGES && isEdge(y >> 4, lx, y & 15, lz, blockVector2,
localChunk
@ -295,47 +295,48 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator {
}
private boolean isEdge(int layer, int x, int y, int z, BlockVector2 blockVector2, LocalChunk localChunk) {
if (layer == 0 || layer == localChunk.getBaseblocks().length - 1) {
int layerIndex = (layer - localChunk.getMinSection());
if (layer == localChunk.getMinSection() || layerIndex == localChunk.getBaseblocks().length - 1) {
return false;
}
if (x == 0) {
LocalChunk localChunkX = getBlockChunks().get(blockVector2.withX(blockVector2.getX() - 1));
if (localChunkX == null || localChunkX.getBaseblocks()[layer] == null ||
localChunkX.getBaseblocks()[layer][ChunkUtil.getJ(15, y, z)] != null) {
if (localChunkX == null || localChunkX.getBaseblocks()[layerIndex] == null ||
localChunkX.getBaseblocks()[layerIndex][ChunkUtil.getJ(15, y, z)] != null) {
return true;
}
} else if (x == 15) {
LocalChunk localChunkX = getBlockChunks().get(blockVector2.withX(blockVector2.getX() + 1));
if (localChunkX == null || localChunkX.getBaseblocks()[layer] == null ||
localChunkX.getBaseblocks()[layer][ChunkUtil.getJ(0, y, z)] != null) {
if (localChunkX == null || localChunkX.getBaseblocks()[layerIndex] == null ||
localChunkX.getBaseblocks()[layerIndex][ChunkUtil.getJ(0, y, z)] != null) {
return true;
}
}
if (z == 0) {
LocalChunk localChunkZ = getBlockChunks().get(blockVector2.withZ(blockVector2.getZ() - 1));
if (localChunkZ == null || localChunkZ.getBaseblocks()[layer] == null ||
localChunkZ.getBaseblocks()[layer][ChunkUtil.getJ(x, y, 15)] != null) {
if (localChunkZ == null || localChunkZ.getBaseblocks()[layerIndex] == null ||
localChunkZ.getBaseblocks()[layerIndex][ChunkUtil.getJ(x, y, 15)] != null) {
return true;
}
} else if (z == 15) {
LocalChunk localChunkZ = getBlockChunks().get(blockVector2.withZ(blockVector2.getZ() + 1));
if (localChunkZ == null || localChunkZ.getBaseblocks()[layer] == null ||
localChunkZ.getBaseblocks()[layer][ChunkUtil.getJ(x, y, 0)] != null) {
if (localChunkZ == null || localChunkZ.getBaseblocks()[layerIndex] == null ||
localChunkZ.getBaseblocks()[layerIndex][ChunkUtil.getJ(x, y, 0)] != null) {
return true;
}
}
if (y == 0) {
if (localChunk.getBaseblocks()[layer - 1] == null ||
localChunk.getBaseblocks()[layer][ChunkUtil.getJ(x, 15, z)] != null) {
if (localChunk.getBaseblocks()[layerIndex - 1] == null ||
localChunk.getBaseblocks()[layerIndex][ChunkUtil.getJ(x, 15, z)] != null) {
return true;
}
} else if (y == 15) {
if (localChunk.getBaseblocks()[layer + 1] == null ||
localChunk.getBaseblocks()[layer][ChunkUtil.getJ(x, 0, z)] != null) {
if (localChunk.getBaseblocks()[layerIndex + 1] == null ||
localChunk.getBaseblocks()[layerIndex][ChunkUtil.getJ(x, 0, z)] != null) {
return true;
}
}
BaseBlock[] baseBlocks = localChunk.getBaseblocks()[layer];
BaseBlock[] baseBlocks = localChunk.getBaseblocks()[layerIndex];
if (x > 0 && baseBlocks[ChunkUtil.getJ(x - 1, y, z)] == null) {
return true;
}

View File

@ -61,8 +61,28 @@ public class GenChunk extends ScopedQueueCoordinator {
public int chunkZ;
private ChunkData chunkData = null;
/**
* Not API. Used as a bridge between P2 and Bukkit {@link Chunk}.
*
* @deprecated Use {@link GenChunk#regenChunk(int, int)} for extended world heights
* @since TODO
*/
@Deprecated(forRemoval = true, since = "TODO")
public GenChunk() {
super(null, Location.at("", 0, 0, 0), Location.at("", 15, 255, 15));
this(0, 255);
}
/**
* Not API. Used as a bridge between P2 and Bukkit {@link Chunk}.
*
* @param minY minimum world Y, inclusive
* @param maxY maximum world Y, inclusive
*
* @since TODO
*/
public GenChunk(int minY, int maxY) {
super(null, Location.at("", 0, minY, 0), Location.at("", 15, maxY, 15));
this.biomes = Biome.values();
}
@ -117,7 +137,7 @@ public class GenChunk extends ScopedQueueCoordinator {
return;
}
Biome biome = BukkitAdapter.adapt(biomeType);
for (int y = 0; y < 256; y++) {
for (int y = getMin().getY(); y <= getMax().getY(); y++) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
this.biomeGrid.setBiome(x, y, z, biome);
@ -130,7 +150,7 @@ public class GenChunk extends ScopedQueueCoordinator {
public void setCuboid(@NonNull Location pos1, @NonNull Location pos2, @NonNull BlockState block) {
if (result != null && pos1.getX() == 0 && pos1.getZ() == 0 && pos2.getX() == 15 && pos2.getZ() == 15) {
for (int y = pos1.getY(); y <= pos2.getY(); y++) {
int layer = y >> 4;
int layer = (y - getMin().getY()) >> 4;
BlockState[] data = result[layer];
if (data == null) {
result[layer] = data = new BlockState[4096];
@ -164,7 +184,7 @@ public class GenChunk extends ScopedQueueCoordinator {
*/
public boolean setBiome(int x, int z, @NonNull Biome biome) {
if (this.biomeGrid != null) {
for (int y = 0; y < 256; y++) {
for (int y = getMin().getY(); y < getMax().getY(); y++) {
this.setBiome(x, y, z, biome);
}
return true;
@ -197,7 +217,7 @@ public class GenChunk extends ScopedQueueCoordinator {
}
private void storeCache(final int x, final int y, final int z, final @NonNull BlockState id) {
int i = y >> 4;
int i = (y - getMin().getY()) >> 4;
BlockState[] v = this.result[i];
if (v == null) {
this.result[i] = v = new BlockState[4096];
@ -219,7 +239,7 @@ public class GenChunk extends ScopedQueueCoordinator {
@Override
public @Nullable BlockState getBlock(int x, int y, int z) {
int i = y >> 4;
int i = (y - getMin().getY()) >> 4;
if (result == null) {
return BukkitBlockUtil.get(chunkData.getType(x, y, z));
}
@ -246,16 +266,16 @@ public class GenChunk extends ScopedQueueCoordinator {
@Override
public @NonNull Location getMax() {
return Location.at(getWorld().getName(), 15 + (getX() << 4), 255, 15 + (getZ() << 4));
return Location.at(getWorld().getName(), 15 + (getX() << 4), super.getMax().getY(), 15 + (getZ() << 4));
}
@Override
public @NonNull Location getMin() {
return Location.at(getWorld().getName(), getX() << 4, 0, getZ() << 4);
return Location.at(getWorld().getName(), getX() << 4, super.getMin().getY(), getZ() << 4);
}
public @NonNull GenChunk clone() {
GenChunk toReturn = new GenChunk();
GenChunk toReturn = new GenChunk(getMin().getY(), getMax().getY());
if (this.result != null) {
for (int i = 0; i < this.result.length; i++) {
BlockState[] matrix = this.result[i];

View File

@ -276,7 +276,9 @@ public class BukkitRegionManager extends RegionManager {
PlotLoc plotLoc = new PlotLoc(bx + x1, bz + z1);
BaseBlock[] ids = map.allBlocks.get(plotLoc);
if (ids != null) {
for (int y = 0; y < Math.min(128, ids.length); y++) {
int minY = value.getMin().getY();
for (int yIndex = 0; yIndex < Math.min(128 - minY, ids.length); yIndex++) {
int y = yIndex + minY;
BaseBlock id = ids[y];
if (id != null) {
value.setBlock(x1, y, z1, id);
@ -284,7 +286,8 @@ public class BukkitRegionManager extends RegionManager {
value.setBlock(x1, y, z1, BlockTypes.AIR.getDefaultState());
}
}
for (int y = Math.min(128, ids.length); y < ids.length; y++) {
for (int yIndex = Math.min(128 - minY, ids.length); yIndex < ids.length; yIndex++) {
int y = yIndex + minY;
BaseBlock id = ids[y];
if (id != null) {
value.setBlock(x1, y, z1, id);

View File

@ -246,7 +246,9 @@ public class BukkitUtil extends WorldUtil {
final World bukkitWorld = Objects.requireNonNull(getWorld(world));
// Skip top and bottom block
int air = 1;
for (int y = bukkitWorld.getMaxHeight() - 1; y >= 0; y--) {
int maxY = com.plotsquared.bukkit.util.BukkitWorld.getMaxWorldHeight(bukkitWorld);
int minY = com.plotsquared.bukkit.util.BukkitWorld.getMinWorldHeight(bukkitWorld);
for (int y = maxY - 1; y >= minY; y--) {
Block block = bukkitWorld.getBlockAt(x, y, z);
Material type = block.getType();
if (type.isSolid()) {
@ -273,7 +275,9 @@ public class BukkitUtil extends WorldUtil {
final World bukkitWorld = Objects.requireNonNull(getWorld(world));
// Skip top and bottom block
int air = 1;
for (int y = bukkitWorld.getMaxHeight() - 1; y >= 0; y--) {
int maxY = com.plotsquared.bukkit.util.BukkitWorld.getMaxWorldHeight(bukkitWorld);
int minY = com.plotsquared.bukkit.util.BukkitWorld.getMinWorldHeight(bukkitWorld);
for (int y = maxY - 1; y >= minY; y--) {
Block block = bukkitWorld.getBlockAt(x, y, z);
Material type = block.getType();
if (type.isSolid()) {

View File

@ -36,6 +36,18 @@ import java.util.Objects;
public class BukkitWorld implements World<org.bukkit.World> {
private static final Map<String, BukkitWorld> worldMap = Maps.newHashMap();
private static final boolean HAS_MIN_Y;
static {
boolean temp;
try {
org.bukkit.World.class.getMethod("getMinHeight");
temp = true;
} catch (NoSuchMethodException e) {
temp = false;
}
HAS_MIN_Y = temp;
}
private final org.bukkit.World world;
@ -73,6 +85,24 @@ public class BukkitWorld implements World<org.bukkit.World> {
return bukkitWorld;
}
/**
* Get the min world height from a Bukkit {@link org.bukkit.World}. Inclusive
*
* @since TODO
*/
public static int getMinWorldHeight(org.bukkit.World world) {
return HAS_MIN_Y ? world.getMinHeight() : 0;
}
/**
* Get the max world height from a Bukkit {@link org.bukkit.World}. Exclusive
*
* @since TODO
*/
public static int getMaxWorldHeight(org.bukkit.World world) {
return HAS_MIN_Y ? world.getMaxHeight() : 256;
}
@Override
public org.bukkit.World getPlatformWorld() {
return this.world;
@ -83,6 +113,16 @@ public class BukkitWorld implements World<org.bukkit.World> {
return this.world.getName();
}
@Override
public int getMinHeight() {
return getMinWorldHeight(world);
}
@Override
public int getMaxHeight() {
return getMaxWorldHeight(world);
}
@Override
public boolean equals(final Object o) {
if (this == o) {

View File

@ -70,7 +70,7 @@ public class ContentMap {
}
for (int x = x1; x <= x2; x++) {
for (int z = z1; z <= z2; z++) {
saveBlocks(world, 256, x, z, 0, 0);
saveBlocks(world, x, z, 0, 0);
}
}
}
@ -134,13 +134,11 @@ public class ContentMap {
this.entities.clear();
}
//todo optimize maxY
void saveBlocks(BukkitWorld world, int maxY, int x, int z, int offsetX, int offsetZ) {
maxY = Math.min(255, maxY);
BaseBlock[] ids = new BaseBlock[maxY + 1];
for (short y = 0; y <= maxY; y++) {
BaseBlock block = world.getFullBlock(BlockVector3.at(x, y, z));
ids[y] = block;
private void saveBlocks(BukkitWorld world, int x, int z, int offsetX, int offsetZ) {
BaseBlock[] ids = new BaseBlock[world.getMaxY() - world.getMinY()];
for (short yIndex = 0; yIndex <= world.getMaxY() - world.getMinY(); yIndex++) {
BaseBlock block = world.getFullBlock(BlockVector3.at(x, yIndex + world.getMinY(), z));
ids[yIndex] = block;
}
PlotLoc loc = new PlotLoc(x + offsetX, z + offsetZ);
this.allBlocks.put(loc, ids);

View File

@ -58,6 +58,21 @@ public interface World<T> {
*/
@NonNull String getName();
/**
* Get the min world height. Inclusive.
*
* @since TODO
*/
int getMinHeight();
/**
* Get the max world height. Exclusive.
*
* @since TODO
*/
int getMaxHeight();
class NullWorld<T> implements World<T> {
private NullWorld() {
@ -74,6 +89,16 @@ public interface World<T> {
return "";
}
@Override
public int getMinHeight() {
return 0;
}
@Override
public int getMaxHeight() {
return 0;
}
@Override
public boolean equals(final Object obj) {
return obj instanceof NullWorld;

View File

@ -42,6 +42,7 @@ public class LocalChunk {
private final QueueCoordinator parent;
private final int x;
private final int z;
private final int minSection;
private final BaseBlock[][] baseblocks;
private final BiomeType[][] biomes;
@ -52,8 +53,10 @@ public class LocalChunk {
this.parent = parent;
this.x = x;
this.z = z;
baseblocks = new BaseBlock[16][];
biomes = new BiomeType[16][];
this.minSection = parent.getWorld() != null ? (parent.getWorld().getMinY() >> 4) : 0;
int sections = parent.getWorld() != null ? (parent.getWorld().getMaxY() >> 4) - minSection : 16;
baseblocks = new BaseBlock[sections][];
biomes = new BiomeType[sections][];
}
public @NonNull QueueCoordinator getParent() {
@ -68,6 +71,13 @@ public class LocalChunk {
return this.z;
}
/**
* Get the minimum layer position stored (usually -4 or 0).
*/
public int getMinSection() {
return this.minSection;
}
public @NonNull BaseBlock[][] getBaseblocks() {
return this.baseblocks;
}

View File

@ -39,6 +39,8 @@ import org.checkerframework.checker.nullness.qual.Nullable;
*/
public class ScopedQueueCoordinator extends DelegateQueueCoordinator {
private final Location min;
private final Location max;
private final int minX;
private final int minY;
private final int minZ;
@ -53,6 +55,8 @@ public class ScopedQueueCoordinator extends DelegateQueueCoordinator {
public ScopedQueueCoordinator(@Nullable QueueCoordinator parent, @NonNull Location min, @NonNull Location max) {
super(parent);
this.min = min;
this.max = max;
this.minX = min.getX();
this.minY = min.getY();
this.minZ = min.getZ();
@ -112,11 +116,11 @@ public class ScopedQueueCoordinator extends DelegateQueueCoordinator {
}
public @NonNull Location getMin() {
return Location.at(this.getWorld().getName(), this.minX, this.minY, this.minZ);
return min;
}
public @NonNull Location getMax() {
return Location.at(this.getWorld().getName(), this.maxX, this.maxY, this.maxZ);
return max;
}
}

View File

@ -39,13 +39,13 @@ public class ChunkUtil {
* - Used for efficient world generation<br>
*/
private static final short[] x_loc;
private static final short[][] y_loc;
private static final short[] y_loc;
private static final short[] z_loc;
private static final short[][][] CACHE_J;
static {
x_loc = new short[4096];
y_loc = new short[16][4096];
y_loc = new short[4096];
z_loc = new short[4096];
for (int i = 0; i < 16; i++) {
int i4 = i << 4;
@ -55,14 +55,14 @@ public class ChunkUtil {
int z1 = a >> 4;
int x1 = a - (z1 << 4);
x_loc[j] = (short) x1;
y_loc[i][j] = (short) y;
y_loc[j] = (short) y;
z_loc[j] = (short) z1;
}
}
CACHE_J = new short[256][16][16];
CACHE_J = new short[16][16][16];
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 256; y++) {
for (int y = 0; y < 16; y++) {
short j = (short) ((y & 0xF) << 8 | z << 4 | x);
CACHE_J[y][x][z] = j;
}
@ -83,7 +83,7 @@ public class ChunkUtil {
* @return J value for xyz position in Array[4096].
*/
public static int getJ(int x, int y, int z) {
return CACHE_J[y][x][z];
return CACHE_J[y & 15][x & 15][z & 15];
}
/**
@ -97,14 +97,14 @@ public class ChunkUtil {
}
/**
* Gets the y coordinate for specific I and J values for a Chunk 16x16x16x16 layerxyz Array[16][4096].
* Gets the y coordinate for specific I and J values for a Chunk 16x16x16x16 layerxyz Array[N][4096].
*
* @param i Relative layer of the position in the layerxyz Array[16][4096].
* @param i Relative layer of the position in the layerxyz Array[16][4096]. May be negative.
* @param j Position in the xyz Array[4096].
* @return x coordinate within the chunk
*/
public static int getY(int i, int j) {
return y_loc[i][j];
return (i << 4) + y_loc[j];
}
/**