diff --git a/README.md b/README.md index ae6f79e90..09b28f24b 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ FAWE is a fork of WorldEdit that has huge speed and memory improvements and cons ## Downloads ### 1.13+ * [Download](https://intellectualsites.github.io/download/fawe.html) -* [Jenkins](https://ci.athion.net/job/FastAsyncWorldEdit-1.15/) +* [Jenkins](https://ci.athion.net/job/FastAsyncWorldEdit-1.16/) ### < 1.12.2 * [Download](https://intellectualsites.github.io/download/fawe.html) @@ -35,7 +35,7 @@ $ gradlew setupDecompWorkspace $ gradlew build ``` -The jar is located in `worldedit-bukkit/build/libs/FastAsyncWorldEdit-1.15-###.jar` +The jar is located in `worldedit-bukkit/build/libs/FastAsyncWorldEdit-1.16-###.jar` ## Contributing Have an idea for an optimization, or a cool feature? diff --git a/build.gradle.kts b/build.gradle.kts index b6e47f76c..84e6e066f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ logger.lifecycle(""" ******************************************* """) //TODO FIX THIS WHEN I FEEL LIKE IT -var rootVersion = "1.15" +var rootVersion = "1.16" var revision: String = "" var buildNumber = "" var date: String = "" diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index 823904be1..6a5327618 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -58,6 +58,7 @@ public class FaweBukkit implements IFawe, Listener { private boolean listeningImages; private BukkitImageListener imageListener; private CFIPacketListener packetListener; + private final boolean chunksStretched; public VaultUtil getVault() { return this.vault; @@ -81,6 +82,8 @@ public class FaweBukkit implements IFawe, Listener { e.printStackTrace(); Bukkit.getServer().shutdown(); } + + chunksStretched = Integer.parseInt(Bukkit.getBukkitVersion().split("-")[0].split("\\.")[1]) >= 16; //Vault is Spigot/Paper only so this needs to be done in the Bukkit module setupVault(); @@ -302,6 +305,11 @@ public class FaweBukkit implements IFawe, Listener { return null; } + @Override + public boolean isChunksStretched() { + return chunksStretched; + } + private void setupPlotSquared() { Plugin plotSquared = this.plugin.getServer().getPluginManager().getPlugin("PlotSquared"); if (plotSquared == null) diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java index eace17737..ae871b419 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java @@ -56,7 +56,7 @@ import net.minecraft.server.v1_14_R1.Entity; import net.minecraft.server.v1_14_R1.EntityTypes; import net.minecraft.server.v1_14_R1.EnumSkyBlock; import net.minecraft.server.v1_14_R1.IBlockData; -import net.minecraft.server.v1_14_R1.LightEngineThreaded; +import net.minecraft.server.v1_14_R1.LightEngine; import net.minecraft.server.v1_14_R1.NBTTagCompound; import net.minecraft.server.v1_14_R1.NBTTagInt; import net.minecraft.server.v1_14_R1.NibbleArray; @@ -69,8 +69,13 @@ import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; import org.bukkit.craftbukkit.v1_14_R1.block.CraftBlock; import org.bukkit.event.entity.CreatureSpawnEvent; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class BukkitGetBlocks_1_14 extends CharGetBlocks { + + private static final Logger log = LoggerFactory.getLogger(BukkitGetBlocks_1_14.class); + public ChunkSection[] sections; public Chunk nmsChunk; public WorldServer world; @@ -129,7 +134,17 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { int layer = y >> 4; if (skyLight[layer] == null) { //getDataLayerData - skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPosition); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (nibbleArray == null) { + byte[] a = new byte[2048]; + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(a, (byte) 15); + nibbleArray = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.SKY, sectionPosition, nibbleArray); + } + skyLight[layer] = nibbleArray; } long l = BlockPosition.a(x, y, z); return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); @@ -140,7 +155,17 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { int layer = y >> 4; if (blockLight[layer] == null) { //getDataLayerData - blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (nibbleArray == null) { + byte[] a = new byte[2048]; + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(a, (byte) 15); + nibbleArray = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.BLOCK, sectionPosition, nibbleArray); + } + blockLight[layer] = nibbleArray; } long l = BlockPosition.a(x, y, z); return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); @@ -299,7 +324,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { } else { existingSection = sections[layer]; if (existingSection == null) { - System.out.println("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); + log.error("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); continue; } } @@ -326,7 +351,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { } newSection = BukkitAdapter_1_14.newChunkSection(layer, this::load, setArr, fastmode); if (!BukkitAdapter_1_14.setSectionAtomic(sections, existingSection, newSection, layer)) { - System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); + log.error("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); continue; } else { updateGet(this, nmsChunk, sections, newSection, setArr, layer); @@ -667,9 +692,13 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { if (light[Y] == null) { continue; } - NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), Y); + NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(sectionPosition); if (nibble == null) { - continue; + byte[] a = new byte[2048]; + Arrays.fill(a, skyBlock == EnumSkyBlock.SKY ? (byte) 15 : (byte) 0); + nibble = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(skyBlock, sectionPosition, nibble); } synchronized (nibble) { for (int i = 0; i < 4096; i++) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java index 3292cc556..e0cbf7266 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java @@ -40,6 +40,7 @@ import net.minecraft.server.v1_15_R1.Entity; import net.minecraft.server.v1_15_R1.EntityTypes; import net.minecraft.server.v1_15_R1.EnumSkyBlock; import net.minecraft.server.v1_15_R1.IBlockData; +import net.minecraft.server.v1_15_R1.LightEngine; import net.minecraft.server.v1_15_R1.NBTTagCompound; import net.minecraft.server.v1_15_R1.NBTTagInt; import net.minecraft.server.v1_15_R1.NibbleArray; @@ -140,7 +141,17 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { public int getSkyLight(int x, int y, int z) { int layer = y >> 4; if (skyLight[layer] == null) { - skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPosition); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (nibbleArray == null) { + byte[] a = new byte[2048]; + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(a, (byte) 15); + nibbleArray = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.SKY, sectionPosition, nibbleArray); + } + skyLight[layer] = nibbleArray; } long l = BlockPosition.a(x, y, z); return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); @@ -150,7 +161,17 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { public int getEmmittedLight(int x, int y, int z) { int layer = y >> 4; if (blockLight[layer] == null) { - blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (nibbleArray == null) { + byte[] a = new byte[2048]; + // Safe enough to assume if it's not created, it's not got any emitted light. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(a, (byte) 0); + nibbleArray = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.BLOCK, sectionPosition, nibbleArray); + } + blockLight[layer] = nibbleArray; } long l = BlockPosition.a(x, y, z); return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); @@ -316,7 +337,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { } else { existingSection = sections[layer]; if (existingSection == null) { - System.out.println("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); + log.error("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); continue; } } @@ -343,7 +364,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { } newSection = BukkitAdapter_1_15_2.newChunkSection(layer, this::load, setArr, fastmode); if (!BukkitAdapter_1_15_2.setSectionAtomic(sections, existingSection, newSection, layer)) { - System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); + log.error("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); continue; } else { updateGet(this, nmsChunk, sections, newSection, setArr, layer); @@ -689,9 +710,13 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { if (light[Y] == null) { continue; } - NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), Y); + NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(sectionPosition); if (nibble == null) { - continue; + byte[] a = new byte[2048]; + Arrays.fill(a, skyBlock == EnumSkyBlock.SKY ? (byte) 15 : (byte) 0); + nibble = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(skyBlock, sectionPosition, nibble); } synchronized (nibble) { for (int i = 0; i < 4096; i++) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java index 7b1549d65..a8e096e0c 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java @@ -5,7 +5,7 @@ import com.boydti.fawe.FaweCache; import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.NMSAdapter; import com.boydti.fawe.config.Settings; -import com.boydti.fawe.object.collection.BitArray; +import com.boydti.fawe.object.collection.BitArrayUnstretched; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.TaskManager; @@ -232,11 +232,13 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter { bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries } - final int blockBitArrayEnd = (bitsPerEntry * 4096) >> 6; + final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntry); + final int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong); + if (num_palette == 1) { for (int i = 0; i < blockBitArrayEnd; i++) blockStates[i] = 0; } else { - final BitArray bitArray = new BitArray(bitsPerEntry, 4096, blockStates); + final BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntry, blockStates); bitArray.fromRaw(blocksCopy); } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java index b07589339..d6dada109 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java @@ -10,7 +10,7 @@ import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.mc1_16_1.nbt.LazyCompoundTag_1_16_1; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.AdaptedMap; -import com.boydti.fawe.object.collection.BitArray; +import com.boydti.fawe.object.collection.BitArrayUnstretched; import com.google.common.base.Suppliers; import com.google.common.collect.Iterables; import com.sk89q.jnbt.Tag; @@ -43,8 +43,7 @@ import static org.slf4j.LoggerFactory.getLogger; public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { - private static final Logger log = LoggerFactory.getLogger( - BukkitGetBlocks_1_16_1.class); + private static final Logger log = LoggerFactory.getLogger(BukkitGetBlocks_1_16_1.class); private static final Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); private final static Function nmsTile2We = tileEntity -> new LazyCompoundTag_1_16_1(Suppliers.memoize(() -> tileEntity.save(new NBTTagCompound()))); @@ -110,7 +109,17 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { public int getSkyLight(int x, int y, int z) { int layer = y >> 4; if (skyLight[layer] == null) { - skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPosition); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (nibbleArray == null) { + byte[] a = new byte[2048]; + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(a, (byte) 15); + nibbleArray = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.SKY, sectionPosition, nibbleArray, true); + } + skyLight[layer] = nibbleArray; } long l = BlockPosition.a(x, y, z); return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); @@ -119,8 +128,18 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { @Override public int getEmmittedLight(int x, int y, int z) { int layer = y >> 4; - if (blockLight[layer] == null) { - blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer)); + if (skyLight[layer] == null) { + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (nibbleArray == null) { + byte[] a = new byte[2048]; + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(a, (byte) 15); + nibbleArray = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.BLOCK, sectionPosition, nibbleArray, true); + } + skyLight[layer] = nibbleArray; } long l = BlockPosition.a(x, y, z); return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); @@ -286,7 +305,7 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { } else { existingSection = sections[layer]; if (existingSection == null) { - System.out.println("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); + log.error("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); continue; } } @@ -315,7 +334,7 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { .newChunkSection(layer, this::load, setArr, fastmode); if (!BukkitAdapter_1_16_1 .setSectionAtomic(sections, existingSection, newSection, layer)) { - System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); + log.error("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); continue; } else { updateGet(this, nmsChunk, sections, newSection, setArr, layer); @@ -553,7 +572,7 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { final int bitsPerEntry = (int) BukkitAdapter_1_16_1.fieldBitsPerEntry.get(bits); final long[] blockStates = bits.a(); - new BitArray(bitsPerEntry, 4096, blockStates).toRaw(data); + new BitArrayUnstretched(bitsPerEntry, blockStates).toRaw(data); int num_palette; if (palette instanceof DataPaletteLinear) { @@ -662,9 +681,13 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { if (light[Y] == null) { continue; } - NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y)); + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), Y); + NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(sectionPosition); if (nibble == null) { - continue; + byte[] a = new byte[2048]; + Arrays.fill(a, skyBlock == EnumSkyBlock.SKY ? (byte) 15 : (byte) 0); + nibble = new NibbleArray(a); + ((LightEngine) world.getChunkProvider().getLightEngine()).a(skyBlock, sectionPosition, nibble, true); } synchronized (nibble) { for (int i = 0; i < 4096; i++) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java index 60e0f8037..847b17a62 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java @@ -84,7 +84,7 @@ public class AsyncWorld extends PassthroughExtent implements World { private BukkitImplAdapter adapter; @Override - public void spawnParticle(Particle particle, double v, double v1, double v2, int i, double v3, double v4, double v5, double v6, T t) { + public void spawnParticle(@NotNull Particle particle, double v, double v1, double v2, int i, double v3, double v4, double v5, double v6, T t) { parent.spawnParticle(particle, v, v1, v2, i, v3, v4, v5, v6, t); } @@ -158,77 +158,77 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public WorldBorder getWorldBorder() { + public @NotNull WorldBorder getWorldBorder() { return TaskManager.IMP.sync(() -> parent.getWorldBorder()); } @Override - public void spawnParticle(Particle particle, Location location, int i) { + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int i) { parent.spawnParticle(particle, location, i); } @Override - public void spawnParticle(Particle particle, double v, double v1, double v2, int i) { + public void spawnParticle(@NotNull Particle particle, double v, double v1, double v2, int i) { parent.spawnParticle(particle, v, v1, v2, i); } @Override - public void spawnParticle(Particle particle, Location location, int i, T t) { + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int i, T t) { parent.spawnParticle(particle, location, i, t); } @Override - public void spawnParticle(Particle particle, double x, double y, double z, int count, T data) { + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, T data) { parent.spawnParticle(particle, x, y, z, count, data); } @Override - public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ) { + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count, double offsetX, double offsetY, double offsetZ) { parent.spawnParticle(particle, location, count, offsetX, offsetY, offsetZ); } @Override - public void spawnParticle(Particle particle, double v, double v1, double v2, int i, double v3, double v4, double v5) { + public void spawnParticle(@NotNull Particle particle, double v, double v1, double v2, int i, double v3, double v4, double v5) { parent.spawnParticle(particle, v, v1, v2, i, v3, v4, v5); } @Override - public void spawnParticle(Particle particle, Location location, int i, double v, double v1, double v2, T t) { + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int i, double v, double v1, double v2, T t) { parent.spawnParticle(particle, location, i, v, v1, v2, t); } @Override - public void spawnParticle(Particle particle, double v, double v1, double v2, int i, double v3, double v4, double v5, T t) { + public void spawnParticle(@NotNull Particle particle, double v, double v1, double v2, int i, double v3, double v4, double v5, T t) { parent.spawnParticle(particle, v, v1, v2, i, v3, v4, v5, t); } @Override - public void spawnParticle(Particle particle, Location location, int i, double v, double v1, double v2, double v3) { + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int i, double v, double v1, double v2, double v3) { parent.spawnParticle(particle, location, i, v, v1, v2, v3); } @Override - public void spawnParticle(Particle particle, double v, double v1, double v2, int i, double v3, double v4, double v5, double v6) { + public void spawnParticle(@NotNull Particle particle, double v, double v1, double v2, int i, double v3, double v4, double v5, double v6) { parent.spawnParticle(particle, v, v1, v2, i, v3, v4, v5, v6); } @Override - public void spawnParticle(Particle particle, Location location, int i, double v, double v1, double v2, double v3, T t) { + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int i, double v, double v1, double v2, double v3, T t) { parent.spawnParticle(particle, location, i, v, v1, v2, v3, t); } @Override - public boolean setSpawnLocation(Location location) { + public boolean setSpawnLocation(@NotNull Location location) { return parent.setSpawnLocation(location); } @Override - public AsyncBlock getBlockAt(final int x, final int y, final int z) { + public @NotNull AsyncBlock getBlockAt(final int x, final int y, final int z) { return new AsyncBlock(this, x, y, z); } @Override - public AsyncBlock getBlockAt(Location loc) { + public @NotNull AsyncBlock getBlockAt(Location loc) { return getBlockAt(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); } @@ -247,13 +247,13 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public AsyncBlock getHighestBlockAt(int x, int z) { + public @NotNull AsyncBlock getHighestBlockAt(int x, int z) { int y = getHighestBlockYAt(x, z); return getBlockAt(x, y, z); } @Override - public AsyncBlock getHighestBlockAt(Location loc) { + public @NotNull AsyncBlock getHighestBlockAt(Location loc) { return getHighestBlockAt(loc.getBlockX(), loc.getBlockZ()); } @@ -279,17 +279,17 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public AsyncChunk getChunkAt(int x, int z) { + public @NotNull AsyncChunk getChunkAt(int x, int z) { return new AsyncChunk(this, x, z); } @Override - public AsyncChunk getChunkAt(Location location) { + public @NotNull AsyncChunk getChunkAt(Location location) { return getChunkAt(location.getBlockX(), location.getBlockZ()); } @Override - public AsyncChunk getChunkAt(Block block) { + public @NotNull AsyncChunk getChunkAt(Block block) { return getChunkAt(block.getX(), block.getZ()); } @@ -405,17 +405,17 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public Item dropItem(final Location location, final ItemStack item) { + public @NotNull Item dropItem(final @NotNull Location location, final @NotNull ItemStack item) { return TaskManager.IMP.sync(() -> parent.dropItem(location, item)); } @Override - public Item dropItemNaturally(final Location location, final ItemStack item) { + public @NotNull Item dropItemNaturally(final @NotNull Location location, final @NotNull ItemStack item) { return TaskManager.IMP.sync(() -> parent.dropItemNaturally(location, item)); } @Override - public Arrow spawnArrow(final Location location, final Vector direction, final float speed, final float spread) { + public @NotNull Arrow spawnArrow(final @NotNull Location location, final @NotNull Vector direction, final float speed, final float spread) { return TaskManager.IMP.sync(() -> parent.spawnArrow(location, direction, speed, spread)); } @@ -425,78 +425,78 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public boolean generateTree(final Location location, final TreeType type) { + public boolean generateTree(final @NotNull Location location, final @NotNull TreeType type) { return TaskManager.IMP.sync(() -> parent.generateTree(location, type)); } @Override - public boolean generateTree(final Location loc, final TreeType type, final BlockChangeDelegate delegate) { + public boolean generateTree(final @NotNull Location loc, final @NotNull TreeType type, final @NotNull BlockChangeDelegate delegate) { return TaskManager.IMP.sync(() -> parent.generateTree(loc, type, delegate)); } @Override - public Entity spawnEntity(Location loc, EntityType type) { + public @NotNull Entity spawnEntity(@NotNull Location loc, EntityType type) { return spawn(loc, type.getEntityClass()); } @Override - public LightningStrike strikeLightning(final Location loc) { + public @NotNull LightningStrike strikeLightning(final @NotNull Location loc) { return TaskManager.IMP.sync(() -> parent.strikeLightning(loc)); } @Override - public LightningStrike strikeLightningEffect(final Location loc) { + public @NotNull LightningStrike strikeLightningEffect(final @NotNull Location loc) { return TaskManager.IMP.sync(() -> parent.strikeLightningEffect(loc)); } @Override - public List getEntities() { + public @NotNull List getEntities() { return TaskManager.IMP.sync(() -> parent.getEntities()); } @Override - public List getLivingEntities() { + public @NotNull List getLivingEntities() { return TaskManager.IMP.sync(() -> parent.getLivingEntities()); } @Override @Deprecated - public Collection getEntitiesByClass(final Class... classes) { + public @NotNull Collection getEntitiesByClass(final Class... classes) { return TaskManager.IMP.sync(() -> parent.getEntitiesByClass(classes)); } @Override - public Collection getEntitiesByClass(final Class cls) { + public @NotNull Collection getEntitiesByClass(final @NotNull Class cls) { return TaskManager.IMP.sync(() -> parent.getEntitiesByClass(cls)); } @Override - public Collection getEntitiesByClasses(final Class... classes) { + public @NotNull Collection getEntitiesByClasses(final Class... classes) { return TaskManager.IMP.sync(() -> parent.getEntitiesByClasses(classes)); } @Override - public List getPlayers() { + public @NotNull List getPlayers() { return TaskManager.IMP.sync(() -> parent.getPlayers()); } @Override - public Collection getNearbyEntities(final Location location, final double x, final double y, final double z) { + public @NotNull Collection getNearbyEntities(final @NotNull Location location, final double x, final double y, final double z) { return TaskManager.IMP.sync(() -> parent.getNearbyEntities(location, x, y, z)); } @Override - public String getName() { + public @NotNull String getName() { return parent.getName(); } @Override - public UUID getUID() { + public @NotNull UUID getUID() { return parent.getUID(); } @Override - public Location getSpawnLocation() { + public @NotNull Location getSpawnLocation() { return parent.getSpawnLocation(); } @@ -590,7 +590,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public boolean createExplosion(Location loc, float power) { + public boolean createExplosion(@NotNull Location loc, float power) { return this.createExplosion(loc, power, false); } @@ -636,17 +636,17 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public List getPopulators() { + public @NotNull List getPopulators() { return parent.getPopulators(); } @Override - public T spawn(final Location location, final Class clazz) throws IllegalArgumentException { + public @NotNull T spawn(final @NotNull Location location, final @NotNull Class clazz) throws IllegalArgumentException { return TaskManager.IMP.sync(() -> parent.spawn(location, clazz)); } @Override - public T spawn(Location location, Class clazz, Consumer function) throws IllegalArgumentException { + public @NotNull T spawn(@NotNull Location location, @NotNull Class clazz, Consumer function) throws IllegalArgumentException { return TaskManager.IMP.sync(() -> parent.spawn(location, clazz, function)); } @@ -656,28 +656,28 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public FallingBlock spawnFallingBlock(Location location, MaterialData data) throws IllegalArgumentException { + public @NotNull FallingBlock spawnFallingBlock(@NotNull Location location, @NotNull MaterialData data) throws IllegalArgumentException { return TaskManager.IMP.sync(() -> parent.spawnFallingBlock(location, data)); } @Override @Deprecated - public FallingBlock spawnFallingBlock(Location location, Material material, byte data) throws IllegalArgumentException { + public @NotNull FallingBlock spawnFallingBlock(@NotNull Location location, @NotNull Material material, byte data) throws IllegalArgumentException { return TaskManager.IMP.sync(() -> parent.spawnFallingBlock(location, material, data)); } @Override - public FallingBlock spawnFallingBlock(Location location, BlockData blockData) throws IllegalArgumentException { + public @NotNull FallingBlock spawnFallingBlock(@NotNull Location location, @NotNull BlockData blockData) throws IllegalArgumentException { return TaskManager.IMP.sync(() -> parent.spawnFallingBlock(location, blockData)); } @Override - public void playEffect(Location location, Effect effect, int data) { + public void playEffect(@NotNull Location location, @NotNull Effect effect, int data) { this.playEffect(location, effect, data, 64); } @Override - public void playEffect(final Location location, final Effect effect, final int data, final int radius) { + public void playEffect(final @NotNull Location location, final @NotNull Effect effect, final int data, final int radius) { TaskManager.IMP.sync(new RunnableVal() { @Override public void run(Object value) { @@ -687,12 +687,12 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public void playEffect(Location loc, Effect effect, T data) { + public void playEffect(@NotNull Location loc, @NotNull Effect effect, T data) { this.playEffect(loc, effect, data, 64); } @Override - public void playEffect(final Location location, final Effect effect, final T data, final int radius) { + public void playEffect(final @NotNull Location location, final @NotNull Effect effect, final T data, final int radius) { TaskManager.IMP.sync(new RunnableVal() { @Override public void run(Object value) { @@ -702,7 +702,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public ChunkSnapshot getEmptyChunkSnapshot(final int x, final int z, final boolean includeBiome, final boolean includeBiomeTempRain) { + public @NotNull ChunkSnapshot getEmptyChunkSnapshot(final int x, final int z, final boolean includeBiome, final boolean includeBiomeTempRain) { return TaskManager.IMP.sync( () -> parent.getEmptyChunkSnapshot(x, z, includeBiome, includeBiomeTempRain)); } @@ -723,7 +723,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public Biome getBiome(int x, int z) { + public @NotNull Biome getBiome(int x, int z) { return adapter.adapt(getExtent().getBiomeType(x, 0, z)); } @@ -733,7 +733,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public void setBiome(int x, int z, Biome bio) { + public void setBiome(int x, int z, @NotNull Biome bio) { BiomeType biome = adapter.adapt(bio); getExtent().setBiome(x, 0, z, biome); } @@ -800,17 +800,17 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public void setDifficulty(Difficulty difficulty) { + public void setDifficulty(@NotNull Difficulty difficulty) { parent.setDifficulty(difficulty); } @Override - public Difficulty getDifficulty() { + public @NotNull Difficulty getDifficulty() { return parent.getDifficulty(); } @Override - public File getWorldFolder() { + public @NotNull File getWorldFolder() { return parent.getWorldFolder(); } @@ -884,14 +884,12 @@ public class AsyncWorld extends PassthroughExtent implements World { parent.setWaterAnimalSpawnLimit(limit); } - @Override - public int getWaterAmbientSpawnLimit() { - return 0; + @Override public int getWaterAmbientSpawnLimit() { + return parent.getWaterAmbientSpawnLimit(); } - @Override - public void setWaterAmbientSpawnLimit(int limit) { - + @Override public void setWaterAmbientSpawnLimit(int limit) { + parent.setWaterAmbientSpawnLimit(limit); } @Override @@ -905,7 +903,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public void playSound(final Location location, final Sound sound, final float volume, final float pitch) { + public void playSound(final @NotNull Location location, final @NotNull Sound sound, final float volume, final float pitch) { TaskManager.IMP.sync(new RunnableVal() { @Override public void run(Object value) { @@ -915,7 +913,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public void playSound(final Location location, final String sound, final float volume, final float pitch) { + public void playSound(final @NotNull Location location, final @NotNull String sound, final float volume, final float pitch) { TaskManager.IMP.sync(new RunnableVal() { @Override public void run(Object value) { @@ -925,7 +923,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public void playSound(Location location, Sound sound, SoundCategory category, float volume, float pitch) { + public void playSound(@NotNull Location location, @NotNull Sound sound, @NotNull SoundCategory category, float volume, float pitch) { TaskManager.IMP.sync(new RunnableVal() { @Override public void run(Object value) { @@ -935,7 +933,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public void playSound(Location location, String sound, SoundCategory category, float volume, float pitch) { + public void playSound(@NotNull Location location, @NotNull String sound, @NotNull SoundCategory category, float volume, float pitch) { TaskManager.IMP.sync(new RunnableVal() { @Override public void run(Object value) { @@ -955,32 +953,32 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public boolean setGameRuleValue(String rule, String value) { + public boolean setGameRuleValue(@NotNull String rule, @NotNull String value) { return parent.setGameRuleValue(rule, value); } @Override - public boolean isGameRule(String rule) { + public boolean isGameRule(@NotNull String rule) { return parent.isGameRule(rule); } @Override - public T getGameRuleValue(GameRule gameRule) { + public T getGameRuleValue(@NotNull GameRule gameRule) { return parent.getGameRuleValue(gameRule); } @Override - public T getGameRuleDefault(GameRule gameRule) { + public T getGameRuleDefault(@NotNull GameRule gameRule) { return parent.getGameRuleDefault(gameRule); } @Override - public boolean setGameRule(GameRule gameRule, T t) { + public boolean setGameRule(@NotNull GameRule gameRule, @NotNull T t) { return parent.setGameRule(gameRule, t); } @Override - public Spigot spigot() { + public @NotNull Spigot spigot() { return parent.spigot(); } @@ -995,7 +993,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public void setMetadata(final String key, final MetadataValue meta) { + public void setMetadata(final @NotNull String key, final @NotNull MetadataValue meta) { TaskManager.IMP.sync(new RunnableVal() { @Override public void run(Object value) { @@ -1005,17 +1003,17 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public List getMetadata(String key) { + public @NotNull List getMetadata(@NotNull String key) { return parent.getMetadata(key); } @Override - public boolean hasMetadata(String key) { + public boolean hasMetadata(@NotNull String key) { return parent.hasMetadata(key); } @Override - public void removeMetadata(final String key, final Plugin plugin) { + public void removeMetadata(final @NotNull String key, final @NotNull Plugin plugin) { TaskManager.IMP.sync(new RunnableVal() { @Override public void run(Object value) { @@ -1025,12 +1023,12 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public void sendPluginMessage(Plugin source, String channel, byte[] message) { + public void sendPluginMessage(@NotNull Plugin source, @NotNull String channel, byte[] message) { parent.sendPluginMessage(source, channel, message); } @Override - public Set getListeningPluginChannels() { + public @NotNull Set getListeningPluginChannels() { return parent.getListeningPluginChannels(); } @@ -1039,17 +1037,17 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public Collection getNearbyEntities(BoundingBox arg0) { + public @NotNull Collection getNearbyEntities(@NotNull BoundingBox arg0) { return parent.getNearbyEntities(arg0); } @Override - public Collection getNearbyEntities(BoundingBox arg0, Predicate arg1) { + public @NotNull Collection getNearbyEntities(@NotNull BoundingBox arg0, Predicate arg1) { return parent.getNearbyEntities(arg0, arg1); } @Override - public Collection getNearbyEntities(Location arg0, double arg1, double arg2, double arg3, + public @NotNull Collection getNearbyEntities(@NotNull Location arg0, double arg1, double arg2, double arg3, Predicate arg4) { return parent.getNearbyEntities(arg0, arg1, arg2, arg3, arg4); } @@ -1060,7 +1058,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public Location locateNearestStructure(Location arg0, StructureType arg1, int arg2, boolean arg3) { + public Location locateNearestStructure(@NotNull Location arg0, @NotNull StructureType arg1, int arg2, boolean arg3) { return parent.locateNearestStructure(arg0, arg1, arg2, arg3); } @@ -1085,44 +1083,45 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public RayTraceResult rayTrace(Location arg0, Vector arg1, double arg2, FluidCollisionMode arg3, boolean arg4, + public RayTraceResult rayTrace( + @NotNull Location arg0, @NotNull Vector arg1, double arg2, @NotNull FluidCollisionMode arg3, boolean arg4, double arg5, Predicate arg6) { return parent.rayTrace(arg0, arg1, arg2, arg3, arg4, arg5, arg6); } @Override - public RayTraceResult rayTraceBlocks(Location arg0, Vector arg1, double arg2) { + public RayTraceResult rayTraceBlocks(@NotNull Location arg0, @NotNull Vector arg1, double arg2) { return parent.rayTraceBlocks(arg0, arg1, arg2); } @Override - public RayTraceResult rayTraceBlocks(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode) { + public RayTraceResult rayTraceBlocks(@NotNull Location start, @NotNull Vector direction, double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode) { return parent.rayTraceBlocks(start, direction, maxDistance, fluidCollisionMode); } @Override - public RayTraceResult rayTraceBlocks(Location start, Vector direction, double arg2, FluidCollisionMode fluidCollisionMode, + public RayTraceResult rayTraceBlocks(@NotNull Location start, @NotNull Vector direction, double arg2, @NotNull FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks) { return parent.rayTraceBlocks(start, direction, arg2, fluidCollisionMode, ignorePassableBlocks); } @Override - public RayTraceResult rayTraceEntities(Location start, Vector direction, double maxDistance) { + public RayTraceResult rayTraceEntities(@NotNull Location start, @NotNull Vector direction, double maxDistance) { return parent.rayTraceEntities(start, direction, maxDistance); } @Override - public RayTraceResult rayTraceEntities(Location arg0, Vector arg1, double arg2, double arg3) { + public RayTraceResult rayTraceEntities(@NotNull Location arg0, @NotNull Vector arg1, double arg2, double arg3) { return parent.rayTraceEntities(arg0, arg1, arg2, arg3); } @Override - public RayTraceResult rayTraceEntities(Location arg0, Vector arg1, double arg2, Predicate arg3) { + public RayTraceResult rayTraceEntities(@NotNull Location arg0, @NotNull Vector arg1, double arg2, Predicate arg3) { return parent.rayTraceEntities(arg0, arg1, arg2, arg3); } @Override - public RayTraceResult rayTraceEntities(Location arg0, Vector arg1, double arg2, double arg3, + public RayTraceResult rayTraceEntities(@NotNull Location arg0, @NotNull Vector arg1, double arg2, double arg3, Predicate arg4) { return parent.rayTraceEntities(arg0, arg1, arg2, arg3, arg4); } @@ -1141,7 +1140,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public Collection getForceLoadedChunks() { + public @NotNull Collection getForceLoadedChunks() { return parent.getForceLoadedChunks(); } @@ -1171,7 +1170,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public int getHighestBlockYAt(int x, int z, com.destroystokyo.paper.HeightmapType heightmap) throws UnsupportedOperationException { + public int getHighestBlockYAt(int x, int z, com.destroystokyo.paper.@NotNull HeightmapType heightmap) throws UnsupportedOperationException { return TaskManager.IMP.sync(() -> parent.getHighestBlockYAt(x, z, heightmap)); } @@ -1201,7 +1200,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public CompletableFuture getChunkAtAsync(int arg0, int arg1, boolean arg2) { + public @NotNull CompletableFuture getChunkAtAsync(int arg0, int arg1, boolean arg2) { return parent.getChunkAtAsync(arg0, arg1, arg2); } @@ -1216,22 +1215,22 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public void getChunkAtAsync(int x, int z, ChunkLoadCallback cb) { + public void getChunkAtAsync(int x, int z, @NotNull ChunkLoadCallback cb) { parent.getChunkAtAsync(x, z, cb); } @Override - public void getChunkAtAsync(Location location, ChunkLoadCallback cb) { + public void getChunkAtAsync(@NotNull Location location, @NotNull ChunkLoadCallback cb) { parent.getChunkAtAsync(location, cb); } @Override - public void getChunkAtAsync(Block block, ChunkLoadCallback cb) { + public void getChunkAtAsync(@NotNull Block block, @NotNull ChunkLoadCallback cb) { parent.getChunkAtAsync(block, cb); } @Override - public Entity getEntity(UUID uuid) { + public Entity getEntity(@NotNull UUID uuid) { return TaskManager.IMP.sync(() -> parent.getEntity(uuid)); } @@ -1242,7 +1241,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public boolean createExplosion(Entity source, Location loc, float power, boolean setFire, boolean breakBlocks) { + public boolean createExplosion(Entity source, @NotNull Location loc, float power, boolean setFire, boolean breakBlocks) { return TaskManager.IMP.sync(() -> parent.createExplosion(source, loc, power, setFire, breakBlocks)); } @@ -1260,12 +1259,13 @@ public class AsyncWorld extends PassthroughExtent implements World { @Override - public void spawnParticle(Particle particle, List receivers, Player source, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, T data) { + public void spawnParticle( + @NotNull Particle particle, List receivers, @NotNull Player source, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, T data) { parent.spawnParticle(particle, receivers, source, x, y, z, count, offsetX, offsetY, offsetZ, extra, data); } @Override - public void spawnParticle(Particle particle, List list, Player player, double v, double v1, double v2, int i, double v3, double v4, double v5, double v6, T t, boolean b) { + public void spawnParticle(@NotNull Particle particle, List list, Player player, double v, double v1, double v2, int i, double v3, double v4, double v5, double v6, T t, boolean b) { parent.spawnParticle(particle, list, player, v, v1, v2, i, v3, v4, v5, v6, t, b); } @@ -1294,10 +1294,12 @@ public class AsyncWorld extends PassthroughExtent implements World { return parent.getHighestBlockAt(location, heightmap); } + @Override public long getTicksPerWaterSpawns() { return parent.getTicksPerWaterSpawns(); } + @Override public void setTicksPerWaterSpawns(int ticksPerWaterSpawns) { parent.setTicksPerWaterSpawns(ticksPerWaterSpawns); } @@ -1312,11 +1314,14 @@ public class AsyncWorld extends PassthroughExtent implements World { parent.setTicksPerWaterAmbientSpawns(ticksPerAmbientSpawns); } + @Override public long getTicksPerAmbientSpawns() { return parent.getTicksPerAmbientSpawns(); } + @Override public void setTicksPerAmbientSpawns(int ticksPerAmbientSpawns) { parent.setTicksPerAmbientSpawns(ticksPerAmbientSpawns); } + } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java index e64db1124..f504932db 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java @@ -4,7 +4,12 @@ import com.boydti.fawe.FaweCache; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; import org.apache.commons.lang.Validate; import org.bukkit.NamespacedKey; diff --git a/worldedit-bukkit/src/main/resources/DummyFawe.src b/worldedit-bukkit/src/main/resources/DummyFawe.src index c0bd44906..53af3b007 100644 Binary files a/worldedit-bukkit/src/main/resources/DummyFawe.src and b/worldedit-bukkit/src/main/resources/DummyFawe.src differ diff --git a/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java b/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java index 6f61bdda8..6675390eb 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java @@ -305,7 +305,7 @@ public class Fawe { br.close(); this.version = FaweVersion.tryParse(versionString, commitString, dateString); Settings.IMP.DATE = new Date(100 + version.year, version.month, version.day).toGMTString(); - Settings.IMP.BUILD = "https://ci.athion.net/job/FastAsyncWorldEdit-1.15/" + version.build; + Settings.IMP.BUILD = "https://ci.athion.net/job/FastAsyncWorldEdit-1.16/" + version.build; Settings.IMP.COMMIT = "https://github.com/IntellectualSites/FastAsyncWorldEdit/commit/" + Integer.toHexString(version.hash); } catch (Throwable ignore) {} try { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java b/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java index ee43fdc36..859503291 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java @@ -1,7 +1,6 @@ package com.boydti.fawe; import static com.google.common.base.Preconditions.checkNotNull; -import static org.slf4j.LoggerFactory.getLogger; import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.Trimable; @@ -9,6 +8,7 @@ import com.boydti.fawe.beta.implementation.queue.Pool; import com.boydti.fawe.beta.implementation.queue.QueuePool; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.BitArray; +import com.boydti.fawe.object.collection.BitArrayUnstretched; import com.boydti.fawe.object.collection.CleanableThreadLocal; import com.boydti.fawe.object.collection.VariableThreadLocal; import com.boydti.fawe.object.exception.FaweBlockBagException; @@ -299,6 +299,102 @@ public enum FaweCache implements Trimable { } } + /** + * Convert raw int array to unstretched palette (1.16) + * @param layerOffset + * @param blocks + * @return palette + */ + public Palette toPaletteUnstretched(int layerOffset, char[] blocks) { + return toPaletteUnstretched(layerOffset, null, blocks); + } + + /** + * Convert raw int array to unstretched palette (1.16) + * @param layerOffset + * @param blocks + * @return palette + */ + public Palette toPaletteUnstretched(int layerOffset, int[] blocks) { + return toPaletteUnstretched(layerOffset, blocks, null); + } + + private Palette toPaletteUnstretched(int layerOffset, int[] blocksInts, char[] blocksChars) { + int[] blockToPalette = BLOCK_TO_PALETTE.get(); + int[] paletteToBlock = PALETTE_TO_BLOCK.get(); + long[] blockStates = BLOCK_STATES.get(); + int[] blocksCopy = SECTION_BLOCKS.get(); + + try { + int num_palette = 0; + int blockIndexStart = layerOffset << 12; + int blockIndexEnd = blockIndexStart + 4096; + if (blocksChars != null) { + for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) { + int ordinal = blocksChars[i]; + int palette = blockToPalette[ordinal]; + if (palette == Integer.MAX_VALUE) { + blockToPalette[ordinal] = palette = num_palette; + paletteToBlock[num_palette] = ordinal; + num_palette++; + } + blocksCopy[j] = palette; + } + } else if (blocksInts != null) { + for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) { + int ordinal = blocksInts[i]; + int palette = blockToPalette[ordinal]; + if (palette == Integer.MAX_VALUE) { + // BlockState state = BlockTypesCache.states[ordinal]; + blockToPalette[ordinal] = palette = num_palette; + paletteToBlock[num_palette] = ordinal; + num_palette++; + } + blocksCopy[j] = palette; + } + } else { + throw new IllegalArgumentException(); + } + + for (int i = 0; i < num_palette; i++) { + blockToPalette[paletteToBlock[i]] = Integer.MAX_VALUE; + } + + // BlockStates + int bitsPerEntry = MathMan.log2nlz(num_palette - 1); + if (Settings.IMP.PROTOCOL_SUPPORT_FIX || num_palette != 1) { + bitsPerEntry = Math.max(bitsPerEntry, 4); // Protocol support breaks <4 bits per entry + } else { + bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries + } + int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntry); + int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong); + if (num_palette == 1) { + // Set a value, because minecraft needs it for some reason + blockStates[0] = 0; + blockBitArrayEnd = 1; + } else { + BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntry, blockStates); + bitArray.fromRaw(blocksCopy); + } + + // Construct palette + Palette palette = PALETTE_CACHE.get(); + palette.bitsPerEntry = bitsPerEntry; + palette.paletteToBlockLength = num_palette; + palette.paletteToBlock = paletteToBlock; + + palette.blockStatesLength = blockBitArrayEnd; + palette.blockStates = blockStates; + + return palette; + } catch (Throwable e) { + e.printStackTrace(); + Arrays.fill(blockToPalette, Integer.MAX_VALUE); + throw e; + } + } + /* * Vector cache */ diff --git a/worldedit-core/src/main/java/com/boydti/fawe/FaweVersion.java b/worldedit-core/src/main/java/com/boydti/fawe/FaweVersion.java index 7fd480dfb..3c4f08062 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/FaweVersion.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/FaweVersion.java @@ -32,9 +32,9 @@ public class FaweVersion { @Override public String toString() { if (hash == 0 && build == 0) { - return "FastAsyncWorldEdit-1.15-NoVer-SNAPSHOT"; + return "FastAsyncWorldEdit-1.16-NoVer-SNAPSHOT"; } else { - return "FastAsyncWorldEdit-1.15" + build; + return "FastAsyncWorldEdit-1.16" + build; } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java b/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java index 20a15faab..6941fb728 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java @@ -41,4 +41,8 @@ public interface IFawe { Preloader getPreloader(); + default boolean isChunksStretched() { + return true; + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java index c042e7a23..42fbac580 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java @@ -45,11 +45,11 @@ public interface IBlocks extends Trimable { IBlocks reset(); - default byte[] toByteArray(boolean full) { - return toByteArray(null, getBitMask(), full); + default byte[] toByteArray(boolean full, boolean stretched) { + return toByteArray(null, getBitMask(), full, stretched); } - default byte[] toByteArray(byte[] buffer, int bitMask, boolean full) { + default byte[] toByteArray(byte[] buffer, int bitMask, boolean full, boolean stretched) { if (buffer == null) { buffer = new byte[1024]; } @@ -81,7 +81,12 @@ public interface IBlocks extends Trimable { } sectionWriter.writeShort(nonEmpty); // non empty - FaweCache.Palette palette = FaweCache.IMP.toPalette(0, ids); + FaweCache.Palette palette; + if (stretched) { + palette = FaweCache.IMP.toPalette(0, ids); + } else { + palette = FaweCache.IMP.toPaletteUnstretched(0, ids); + } sectionWriter.writeByte(palette.bitsPerEntry); // bits per block sectionWriter.writeVarInt(palette.paletteToBlockLength); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java index 1dda73c3a..744e92f06 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java @@ -181,6 +181,18 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { } @Override public void setFullBright(int layer) { + if (light == null) { + light = new char[16][]; + } + if (light[layer] == null) { + light[layer] = new char[4096]; + } + if (skyLight == null) { + skyLight = new char[16][]; + } + if (skyLight[layer] == null) { + skyLight[layer] = new char[4096]; + } Arrays.fill(light[layer], (char) 15); Arrays.fill(skyLight[layer], (char) 15); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/packet/ChunkPacket.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/packet/ChunkPacket.java index b904c5cd6..5638c962e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/packet/ChunkPacket.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/packet/ChunkPacket.java @@ -1,10 +1,12 @@ package com.boydti.fawe.beta.implementation.packet; +import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IBlocks; import com.boydti.fawe.object.FaweOutputStream; import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEdit; import java.util.HashMap; import java.util.function.Function; @@ -64,7 +66,7 @@ public class ChunkPacket implements Function, Supplier { if (sectionBytes == null) { IBlocks tmpChunk = getChunk(); byte[] buf = FaweCache.IMP.BYTE_BUFFER_8192.get(); - sectionBytes = tmpChunk.toByteArray(buf, tmpChunk.getBitMask(), this.full); + sectionBytes = tmpChunk.toByteArray(buf, tmpChunk.getBitMask(), this.full, Fawe.imp().isChunksStretched()); } tmp = sectionBytes; } @@ -72,6 +74,7 @@ public class ChunkPacket implements Function, Supplier { return tmp; } + public Object getNativePacket() { return nativePacket; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BitArrayUnstretched.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BitArrayUnstretched.java new file mode 100644 index 000000000..9572eecc9 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BitArrayUnstretched.java @@ -0,0 +1,115 @@ +package com.boydti.fawe.object.collection; + +import com.boydti.fawe.util.MathMan; + +public final class BitArrayUnstretched { + + private final long[] data; + private final int bitsPerEntry; + private final int maxSeqLocIndex; + private final int emptyBitCount; + private final long mask; + private final int longLen; + + public BitArrayUnstretched(int bitsPerEntry, long[] buffer) { + this.bitsPerEntry = bitsPerEntry; + this.mask = (1L << bitsPerEntry) - 1L; + this.emptyBitCount = 64 % bitsPerEntry; + this.maxSeqLocIndex = 64 - (bitsPerEntry + emptyBitCount); + final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntry); + this.longLen = MathMan.ceilZero((float) 4096 / blocksPerLong); + if (buffer.length < longLen) { + this.data = new long[longLen]; + } else { + this.data = buffer; + } + } + + public long[] getData() { + return data; + } + + public final void set(int index, int value) { + if (longLen == 0) return; + int bitIndexStart = index * bitsPerEntry + MathMan.floorZero((double) index / longLen) * emptyBitCount; + int longIndexStart = bitIndexStart >> 6; + int localBitIndexStart = bitIndexStart & 63; + this.data[longIndexStart] = this.data[longIndexStart] & ~(mask << localBitIndexStart) | (long) value << localBitIndexStart; + } + + public final int get(int index) { + if (longLen == 0) return 0; + int bitIndexStart = index * bitsPerEntry + MathMan.floorZero((double) index / longLen) * emptyBitCount; + + int longIndexStart = bitIndexStart >> 6; + + int localBitIndexStart = bitIndexStart & 63; + return (int)(this.data[longIndexStart] >>> localBitIndexStart & mask); + } + + public int getLength() { + return longLen; + } + + public final void fromRaw(int[] arr) { + final long[] data = this.data; + final int bitsPerEntry = this.bitsPerEntry; + final int maxSeqLocIndex = this.maxSeqLocIndex; + + int localStart = 0; + int arrI = 0; + long l = 0; + for (int i = 0; i < longLen; i++) { + int lastVal; + for (; localStart <= maxSeqLocIndex && arrI < 4096; localStart += bitsPerEntry) { + lastVal = arr[arrI++]; + l |= ((long) lastVal << localStart); + } + localStart = 0; + data[i] = l; + l = 0; + } + } + + public final int[] toRaw() { + return toRaw(new int[4096]); + } + + public final int[] toRaw(int[] buffer) { + final long[] data = this.data; + final int bitsPerEntry = this.bitsPerEntry; + final int maxSeqLocIndex = this.maxSeqLocIndex; + + int localStart = 0; + int arrI = 0; + for (int i = 0; i < longLen; i++) { + long l = data[i]; + char lastVal; + for (; localStart <= maxSeqLocIndex && arrI < 4096; localStart += bitsPerEntry) { + lastVal = (char) (l >>> localStart & this.mask); + buffer[arrI++] = lastVal; + } + localStart = 0; + } + return buffer; + } + + public final char[] toRaw(char[] buffer) { + final long[] data = this.data; + final int bitsPerEntry = this.bitsPerEntry; + final int maxSeqLocIndex = this.maxSeqLocIndex; + + int localStart = 0; + int arrI = 0; + for (int i = 0; i < longLen; i++) { + long l = data[i]; + char lastVal; + for (; localStart <= maxSeqLocIndex && arrI < 4096; localStart += bitsPerEntry) { + lastVal = (char) (l >>> localStart & this.mask); + buffer[arrI++] = lastVal; + } + localStart = 0; + } + return buffer; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java b/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java index 1aceda930..8231d3aa8 100644 --- a/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java +++ b/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java @@ -20,6 +20,7 @@ package com.sk89q.util; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Locale; @@ -331,4 +332,37 @@ public final class StringUtil { return parsableBlocks; } + + /** + * Splits a string respecting enclosing quotes. + * + * @param input the input to split. + * @param delimiter the delimiter to split on. + * @param open the opening quote character. + * @param close the closing quote character. + * @return a list of split strings. + */ + public static List split(String input, char delimiter, char open, char close) { + if (input.indexOf(open) == -1 && input.indexOf(close) == -1) { + return Arrays.asList(input.split(String.valueOf(delimiter))); + } + int level = 0; + int begin = 0; + List split = new ArrayList<>(); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c == delimiter && level == 0) { + split.add(input.substring(begin, i)); + begin = i + 1; + } else if (c == open) { + level++; + } else if (c == close) { + level--; + } + } + if (begin < input.length()) { + split.add(input.substring(begin)); + } + return split; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java index 4f5a4fdbc..693cd9b8e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java @@ -58,8 +58,8 @@ public class BlockFactory extends AbstractFactory { */ public Set parseFromListInput(String input, ParserContext context) throws InputParseException { Set blocks = new HashSet<>(); - String[] splits = input.split(","); - for (String token : StringUtil.parseListInQuotes(splits, ',', '[', ']', true)) { + // String[] splits = input.split(","); + for (String token : StringUtil.split(input, ',', '[', ']')) { blocks.add(parseFromInput(token, context)); } return blocks; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java index 014229a3e..41bece423 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java @@ -24,6 +24,7 @@ import com.sk89q.worldedit.extension.factory.parser.pattern.BlockCategoryPattern import com.sk89q.worldedit.extension.factory.parser.pattern.ClipboardPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.RandomPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.RandomStatePatternParser; +import com.sk89q.worldedit.extension.factory.parser.pattern.SimplexPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.SingleBlockPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.TypeOrStateApplyingPatternParser; import com.sk89q.worldedit.function.pattern.Pattern; @@ -54,6 +55,9 @@ public final class PatternFactory extends AbstractFactory { register(new TypeOrStateApplyingPatternParser(worldEdit)); register(new RandomStatePatternParser(worldEdit)); register(new BlockCategoryPatternParser(worldEdit)); + + // FAWE + register(new SimplexPatternParser(worldEdit)); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/RichParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/RichParser.java new file mode 100644 index 000000000..6bd9261fa --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/RichParser.java @@ -0,0 +1,118 @@ +package com.sk89q.worldedit.extension.factory.parser; + +import com.sk89q.util.StringUtil; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.internal.registry.InputParser; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; +import java.util.stream.Stream; + +/** + * A rich parser allows parsing of patterns and masks with extra arguments, + * e.g. #simplex[scale][pattern]. + * + * @param the parse result. + */ +public abstract class RichParser extends InputParser { + private final String prefix; + private final String required; + + /** + * Create a new rich parser with a defined prefix for the result, e.g. {@code #simplex}. + * + * @param worldEdit the worldedit instance. + * @param prefix the prefix of this parser result. + */ + protected RichParser(WorldEdit worldEdit, String prefix) { + super(worldEdit); + this.prefix = prefix; + this.required = prefix + "["; + } + + @Override + public Stream getSuggestions(String input) { + // we don't even want to start suggesting if it's not meant to be this parser result + if (input.length() > this.required.length() && !input.startsWith(this.required)) { + return Stream.empty(); + } + // suggest until the first [ as long as it isn't fully typed + if (input.length() < this.required.length()) { + return Stream.of(this.required).filter(s -> s.startsWith(input)); + } + // we know that it is at least "" + String[] strings = extractArguments(input.substring(this.prefix.length()), false); + StringJoiner joiner = new StringJoiner(","); + for (int i = 0; i < strings.length - 1; i++) { + joiner.add("[" + strings[i] + "]"); + } + String previous = this.prefix + joiner; + return getSuggestions(strings[strings.length - 1], strings.length - 1).map(s -> previous + "[" + s + "]"); + } + + @Override + public E parseFromInput(String input, ParserContext context) throws InputParseException { + if (!input.startsWith(this.prefix)) return null; + if (input.length() < this.prefix.length()) return null; + String[] arguments = extractArguments(input.substring(prefix.length()), true); + return parseFromInput(arguments, context); + } + + /** + * Returns a stream of suggestions for the argument at the given index. + * + * @param argumentInput the already provided input for the argument at the given index. + * @param index the index of the argument to get suggestions for. + * @return a stream of suggestions matching the given input for the argument at the given index. + */ + protected abstract Stream getSuggestions(String argumentInput, int index); + + /** + * Parses the already split arguments. + * + * @param arguments the array of arguments that were split (can be empty). + * @param context the context of this parsing process. + * @return the resulting parsed type. + * @throws InputParseException if the input couldn't be parsed correctly. + */ + protected abstract E parseFromInput(@NotNull String[] arguments, ParserContext context) throws InputParseException; + + /** + * Extracts arguments enclosed by {@code []} into an array. + * Example: {@code [Hello][World]} results in a list containing {@code Hello} and {@code World}. + * + * @param input the input to extract arguments from. + * @param requireClosing whether or not the extraction requires valid bracketing. + * @return an array of extracted arguments. + * @throws InputParseException if {@code requireClosing == true} and the count of [ != the count of ] + */ + protected String[] extractArguments(String input, boolean requireClosing) throws InputParseException { + int open = 0; // the "level" + int openIndex = 0; + int i = 0; + List arguments = new ArrayList<>(); + for (; i < input.length(); i++) { + if (input.charAt(i) == '[') { + if (open++ == 0) { + openIndex = i; + } + } + if (input.charAt(i) == ']') { + if (--open == 0) { + arguments.add(input.substring(openIndex + 1, i)); + } + } + } + if (!requireClosing && open > 0) { + arguments.add(input.substring(openIndex + 1)); + } + if (requireClosing && open != 0) { + throw new InputParseException("Invalid bracketing, are you missing a '[' or ']'?"); + } + return arguments.toArray(new String[0]); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java index 81b1b11e3..66c55be3e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java @@ -38,8 +38,9 @@ public class RandomPatternParser extends InputParser { @Override public Stream getSuggestions(String input) { - String[] splits = input.split(",", -1); - List patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true); + List patterns = StringUtil.split(input, ',', '[', ']'); + /*String[] splits = input.split(",", -1); + List patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true);*/ if (patterns.size() == 1) { return Stream.empty(); } @@ -63,8 +64,9 @@ public class RandomPatternParser extends InputParser { public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { RandomPattern randomPattern = new RandomPattern(); - String[] splits = input.split(",", -1); - List patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true); + List patterns = StringUtil.split(input, ',', '[', ']'); + /*String[] splits = input.split(",", -1); + List patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true);*/ if (patterns.size() == 1) { return null; // let a 'single'-pattern parser handle it } @@ -74,7 +76,7 @@ public class RandomPatternParser extends InputParser { // Parse special percentage syntax if (token.matches("[0-9]+(\\.[0-9]*)?%.*")) { - String[] p = token.split("%"); + String[] p = token.split("%", 2); if (p.length < 2) { throw new InputParseException("Missing the type after the % symbol for '" + input + "'"); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SimplexPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SimplexPatternParser.java new file mode 100644 index 000000000..547f96390 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SimplexPatternParser.java @@ -0,0 +1,75 @@ +package com.sk89q.worldedit.extension.factory.parser.pattern; + +import com.boydti.fawe.object.random.SimplexRandom; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.factory.parser.RichParser; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.pattern.RandomPattern; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import org.jetbrains.annotations.NotNull; + +import java.util.stream.Stream; + +public class SimplexPatternParser extends RichParser { + private static final String SIMPLEX_PREFIX = "#simplex"; + + public SimplexPatternParser(WorldEdit worldEdit) { + super(worldEdit, SIMPLEX_PREFIX); + } + + @Override + protected Stream getSuggestions(String argumentInput, int index) { + if (index == 0) { + if (argumentInput.isEmpty()) { + return Stream.of("1", "2", "3", "4", "5", "6", "7", "8", "9"); + } + // if already a valid number, suggest more digits + if (isDouble(argumentInput)) { + Stream numbers = Stream.of("", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"); + if (argumentInput.indexOf('.') == -1) { + numbers = Stream.concat(numbers, Stream.of(".")); + } + return numbers.map(s -> argumentInput + s); + } + // no valid input anymore + return Stream.empty(); + } + if (index == 1) { + return worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); + } + return Stream.empty(); + } + + @Override + protected Pattern parseFromInput(@NotNull String[] arguments, ParserContext context) { + if (arguments.length != 2) { + throw new InputParseException("Simplex requires a scale and a pattern, e.g. #simplex[5][dirt,stone]"); + } + double scale = Double.parseDouble(arguments[0]); + scale = 1d / Math.max(1, scale); + Pattern inner = worldEdit.getPatternFactory().parseFromInput(arguments[1], context); + if (inner instanceof RandomPattern) { + return new RandomPattern(new SimplexRandom(scale), (RandomPattern) inner); + } else if (inner instanceof BlockStateHolder) { + return inner; // single blocks won't have any impact on how simplex behaves + } else { + throw new InputParseException("Pattern " + inner.getClass().getSimpleName() + " cannot be used with #simplex"); + } + } + + private static boolean isDouble(String input) { + boolean point = false; + for (char c : input.toCharArray()) { + if (!Character.isDigit(c)) { + if (c == '.' && !point) { + point = true; + } else { + return false; + } + } + } + return true; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java index d5ecc2b44..3e02cdfaa 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java @@ -52,6 +52,19 @@ public class RandomPattern extends AbstractPattern { this.random = random; } + /** + * Create a random pattern from an existing one but with a different random. + * + * @param random the new random to use. + * @param parent the existing random pattern. + */ + public RandomPattern(SimpleRandom random, RandomPattern parent) { + this.random = random; + this.weights = parent.weights; + this.collection = RandomCollection.of(weights, random); + this.patterns = parent.patterns; + } + /** * Add a pattern to the weight list of patterns. *