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/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index ab1c6d499..8dbf07f8f 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -68,6 +68,6 @@ - + diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index 403a55709..305807b53 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -41,12 +41,13 @@ dependencies { "compile"("org.spigotmcv1_14_r1:spigotmcv1_14_r1:1_14_r1") "compile"("org.spigotmcv1_15_r1:spigotmcv1_15_r1:1_15_r1") "compile"("it.unimi.dsi:fastutil:8.2.1") - "api"("com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT") { + "api"("com.destroystokyo.paper:paper-api:1.16.1-R0.1-SNAPSHOT") { exclude("junit", "junit") isTransitive = false } "compileOnly"("org.spigotmc:spigot:1.14.4-R0.1-SNAPSHOT") "compileOnly"("org.spigotmc:spigot:1.15.2-R0.1-SNAPSHOT") + "compileOnly"("org.spigotmc:spigot:1.16.1-R0.1-SNAPSHOT") "implementation"("io.papermc:paperlib:1.0.2") "compileOnly"("com.sk89q:dummypermscompat:1.10") "implementation"("org.apache.logging.log4j:log4j-slf4j-impl:2.8.1") 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..b6a8a7755 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; @@ -99,6 +100,8 @@ public class FaweBukkit implements IFawe, Listener { // The tick limiter new ChunkListener_9(); }); + + chunksStretched = Integer.parseInt(Bukkit.getMinecraftVersion().split("\\.")[1]) >= 16; } @Override // Please don't delete this again, it's WIP @@ -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_14/FAWEWorldNativeAccess_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/FAWEWorldNativeAccess_1_14.java new file mode 100644 index 000000000..9bcfc1ddd --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/FAWEWorldNativeAccess_1_14.java @@ -0,0 +1,176 @@ +package com.boydti.fawe.bukkit.adapter.mc1_14; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_14_R4; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import net.minecraft.server.v1_14_R1.Block; +import net.minecraft.server.v1_14_R1.BlockPosition; +import net.minecraft.server.v1_14_R1.Chunk; +import net.minecraft.server.v1_14_R1.ChunkProviderServer; +import net.minecraft.server.v1_14_R1.EnumDirection; +import net.minecraft.server.v1_14_R1.IBlockData; +import net.minecraft.server.v1_14_R1.NBTBase; +import net.minecraft.server.v1_14_R1.NBTTagCompound; +import net.minecraft.server.v1_14_R1.PlayerChunk; +import net.minecraft.server.v1_14_R1.TileEntity; +import net.minecraft.server.v1_14_R1.World; +import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_14_R1.block.data.CraftBlockData; +import org.bukkit.event.block.BlockPhysicsEvent; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.util.Objects; + +public class FAWEWorldNativeAccess_1_14 implements WorldNativeAccess { + private static final int UPDATE = 1, NOTIFY = 2; + + private final FAWE_Spigot_v1_14_R4 adapter; + private final WeakReference world; + private SideEffectSet sideEffectSet; + + public FAWEWorldNativeAccess_1_14(FAWE_Spigot_v1_14_R4 adapter, WeakReference world) { + this.adapter = adapter; + this.world = world; + } + + private World getWorld() { + return Objects.requireNonNull(world.get(), "The reference to the world was lost"); + } + + @Override + public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + + @Override + public Chunk getChunk(int x, int z) { + return getWorld().getChunkAt(x, z); + } + + @Override + public IBlockData toNative(com.sk89q.worldedit.world.block.BlockState state) { + int stateId = BlockStateIdAccess.getBlockStateId(state); + return BlockStateIdAccess.isValidInternalId(stateId) + ? Block.getByCombinedId(stateId) + : ((CraftBlockData) BukkitAdapter.adapt(state)).getState(); + } + + @Override + public IBlockData getBlockState(Chunk chunk, BlockPosition position) { + return chunk.getType(position); + } + + @Nullable + @Override + public IBlockData setBlockState(Chunk chunk, BlockPosition position, IBlockData state) { + return chunk.setType(position, state, false); + } + + @Override + public IBlockData getValidBlockForPosition(IBlockData block, BlockPosition position) { + return Block.b(block, getWorld(), position); + } + + @Override + public BlockPosition getPosition(int x, int y, int z) { + return new BlockPosition(x, y, z); + } + + @Override + public void updateLightingForBlock(BlockPosition position) { + getWorld().getChunkProvider().getLightEngine().a(position); + } + + @Override + public boolean updateTileEntity(BlockPosition position, CompoundTag tag) { + // We will assume that the tile entity was created for us, + // though we do not do this on the other versions + TileEntity tileEntity = getWorld().getTileEntity(position); + if (tileEntity == null) { + return false; + } + NBTBase nativeTag = adapter.fromNative(tag); + tileEntity.load((NBTTagCompound) nativeTag); + return true; + } + + @Override + public void notifyBlockUpdate(BlockPosition position, IBlockData oldState, IBlockData newState) { + getWorld().notify(position, oldState, newState, UPDATE | NOTIFY); + } + + @Override + public boolean isChunkTicking(Chunk chunk) { + return chunk.getState().isAtLeast(PlayerChunk.State.TICKING); + } + + @Override + public void markBlockChanged(BlockPosition position) { + ((ChunkProviderServer) getWorld().getChunkProvider()).flagDirty(position); + } + + private static final EnumDirection[] NEIGHBOUR_ORDER = { + EnumDirection.WEST, EnumDirection.EAST, + EnumDirection.DOWN, EnumDirection.UP, + EnumDirection.NORTH, EnumDirection.SOUTH + }; + + @Override + public void notifyNeighbors(BlockPosition pos, IBlockData oldState, IBlockData newState) { + World world = getWorld(); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + world.update(pos, oldState.getBlock()); + } else { + // When we don't want events, manually run the physics without them. + // Un-nest neighbour updating + for (EnumDirection direction : NEIGHBOUR_ORDER) { + BlockPosition shifted = pos.shift(direction); + world.getType(shifted).doPhysics(world, shifted, oldState.getBlock(), pos, false); + } + } + if (newState.isComplexRedstone()) { + world.updateAdjacentComparators(pos, newState.getBlock()); + } + } + + @Override + public void updateNeighbors(BlockPosition pos, IBlockData oldState, IBlockData newState, int recursionLimit) { + World world = getWorld(); + // a == updateNeighbors + // b == updateDiagonalNeighbors + oldState.b(world, pos, NOTIFY); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + CraftWorld craftWorld = world.getWorld(); + if (craftWorld != null) { + BlockPhysicsEvent event = new BlockPhysicsEvent( + craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), + CraftBlockData.fromData(newState)); + world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + } + } + newState.a(world, pos, NOTIFY); + newState.b(world, pos, NOTIFY); + } + + @Override + public void onBlockStateChange(BlockPosition pos, IBlockData oldState, IBlockData newState) { + getWorld().a(pos, oldState, newState); + } + + + @Override + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + return this.adapter.setBlock(this.getChunk(position.getBlockX() >> 4, position.getBlockZ() >> 4).bukkitChunk, position.getBlockX(), position.getBlockY(), position.getBlockZ(), block, sideEffectSet.shouldApply(SideEffect.LIGHTING)); + } +} \ No newline at end of file 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_15_2/FAWEWorldNativeAccess_1_15_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/FAWEWorldNativeAccess_1_15_2.java new file mode 100644 index 000000000..9ce24d789 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/FAWEWorldNativeAccess_1_15_2.java @@ -0,0 +1,173 @@ +package com.boydti.fawe.bukkit.adapter.mc1_15_2; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_15_R2; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import net.minecraft.server.v1_15_R1.Block; +import net.minecraft.server.v1_15_R1.BlockPosition; +import net.minecraft.server.v1_15_R1.Chunk; +import net.minecraft.server.v1_15_R1.ChunkProviderServer; +import net.minecraft.server.v1_15_R1.EnumDirection; +import net.minecraft.server.v1_15_R1.IBlockData; +import net.minecraft.server.v1_15_R1.NBTBase; +import net.minecraft.server.v1_15_R1.NBTTagCompound; +import net.minecraft.server.v1_15_R1.PlayerChunk; +import net.minecraft.server.v1_15_R1.TileEntity; +import net.minecraft.server.v1_15_R1.World; +import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_15_R1.block.data.CraftBlockData; +import org.bukkit.event.block.BlockPhysicsEvent; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.util.Objects; + +public class FAWEWorldNativeAccess_1_15_2 implements WorldNativeAccess { + private static final int UPDATE = 1, NOTIFY = 2; + + private final FAWE_Spigot_v1_15_R2 adapter; + private final WeakReference world; + private SideEffectSet sideEffectSet; + + public FAWEWorldNativeAccess_1_15_2(FAWE_Spigot_v1_15_R2 adapter, WeakReference world) { + this.adapter = adapter; + this.world = world; + } + + private World getWorld() { + return Objects.requireNonNull(world.get(), "The reference to the world was lost"); + } + + @Override + public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + + @Override + public Chunk getChunk(int x, int z) { + return getWorld().getChunkAt(x, z); + } + + @Override + public IBlockData toNative(com.sk89q.worldedit.world.block.BlockState state) { + int stateId = BlockStateIdAccess.getBlockStateId(state); + return BlockStateIdAccess.isValidInternalId(stateId) + ? Block.getByCombinedId(stateId) + : ((CraftBlockData) BukkitAdapter.adapt(state)).getState(); + } + + @Override + public IBlockData getBlockState(Chunk chunk, BlockPosition position) { + return chunk.getType(position); + } + + @Nullable + @Override + public IBlockData setBlockState(Chunk chunk, BlockPosition position, IBlockData state) { + return chunk.setType(position, state, false); + } + + @Override + public IBlockData getValidBlockForPosition(IBlockData block, BlockPosition position) { + return Block.b(block, getWorld(), position); + } + + @Override + public BlockPosition getPosition(int x, int y, int z) { + return new BlockPosition(x, y, z); + } + + @Override + public void updateLightingForBlock(BlockPosition position) { + getWorld().getChunkProvider().getLightEngine().a(position); + } + + @Override + public boolean updateTileEntity(BlockPosition position, CompoundTag tag) { + // We will assume that the tile entity was created for us, + // though we do not do this on the other versions + TileEntity tileEntity = getWorld().getTileEntity(position); + if (tileEntity == null) { + return false; + } + NBTBase nativeTag = adapter.fromNative(tag); + tileEntity.load((NBTTagCompound) nativeTag); + return true; + } + + @Override + public void notifyBlockUpdate(BlockPosition position, IBlockData oldState, IBlockData newState) { + getWorld().notify(position, oldState, newState, UPDATE | NOTIFY); + } + + @Override + public boolean isChunkTicking(Chunk chunk) { + return chunk.getState().isAtLeast(PlayerChunk.State.TICKING); + } + + @Override + public void markBlockChanged(BlockPosition position) { + ((ChunkProviderServer) getWorld().getChunkProvider()).flagDirty(position); + } + + private static final EnumDirection[] NEIGHBOUR_ORDER = { + EnumDirection.WEST, EnumDirection.EAST, + EnumDirection.DOWN, EnumDirection.UP, + EnumDirection.NORTH, EnumDirection.SOUTH + }; + + @Override + public void notifyNeighbors(BlockPosition pos, IBlockData oldState, IBlockData newState) { + World world = getWorld(); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + world.update(pos, oldState.getBlock()); + } else { + // When we don't want events, manually run the physics without them. + // Un-nest neighbour updating + for (EnumDirection direction : NEIGHBOUR_ORDER) { + BlockPosition shifted = pos.shift(direction); + world.getType(shifted).doPhysics(world, shifted, oldState.getBlock(), pos, false); + } + } + if (newState.isComplexRedstone()) { + world.updateAdjacentComparators(pos, newState.getBlock()); + } + } + + @Override + public void updateNeighbors(BlockPosition pos, IBlockData oldState, IBlockData newState, int recursionLimit) { + World world = getWorld(); + // a == updateNeighbors + // b == updateDiagonalNeighbors + oldState.b(world, pos, NOTIFY); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + CraftWorld craftWorld = world.getWorld(); + if (craftWorld != null) { + BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState)); + world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + } + } + newState.a(world, pos, NOTIFY); + newState.b(world, pos, NOTIFY); + } + + @Override + public void onBlockStateChange(BlockPosition pos, IBlockData oldState, IBlockData newState) { + getWorld().a(pos, oldState, newState); + } + + @Override + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + return this.adapter.setBlock(this.getChunk(position.getBlockX() >> 4, position.getBlockZ() >> 4).bukkitChunk, position.getBlockX(), position.getBlockY(), position.getBlockZ(), block, sideEffectSet.shouldApply(SideEffect.LIGHTING)); + } +} \ No newline at end of file diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BlockMaterial_1_15.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BlockMaterial_1_16_1.java similarity index 79% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BlockMaterial_1_15.java rename to worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BlockMaterial_1_16_1.java index 3a7c205ac..dfa348a78 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BlockMaterial_1_15.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BlockMaterial_1_16_1.java @@ -1,15 +1,11 @@ -package com.boydti.fawe.bukkit.adapter.mc1_15; +package com.boydti.fawe.bukkit.adapter.mc1_16_1; import com.sk89q.util.ReflectionUtil; import com.sk89q.worldedit.world.registry.BlockMaterial; -import net.minecraft.server.v1_15_R1.Block; -import net.minecraft.server.v1_15_R1.EnumPistonReaction; -import net.minecraft.server.v1_15_R1.IBlockData; -import net.minecraft.server.v1_15_R1.ITileEntity; -import net.minecraft.server.v1_15_R1.Material; -import org.bukkit.craftbukkit.v1_15_R1.block.data.CraftBlockData; +import net.minecraft.server.v1_16_R1.*; +import org.bukkit.craftbukkit.v1_16_R1.block.data.CraftBlockData; -public class BlockMaterial_1_15 implements BlockMaterial { +public class BlockMaterial_1_16_1 implements BlockMaterial { private final Block block; private final IBlockData defaultState; private final Material material; @@ -17,17 +13,17 @@ public class BlockMaterial_1_15 implements BlockMaterial { private final CraftBlockData craftBlockData; private final org.bukkit.Material craftMaterial; - public BlockMaterial_1_15(Block block) { + public BlockMaterial_1_16_1(Block block) { this(block, block.getBlockData()); } - public BlockMaterial_1_15(Block block, IBlockData defaultState) { + public BlockMaterial_1_16_1(Block block, IBlockData defaultState) { this.block = block; this.defaultState = defaultState; this.material = defaultState.getMaterial(); this.craftBlockData = CraftBlockData.fromData(defaultState); this.craftMaterial = craftBlockData.getMaterial(); - this.isTranslucent = !(boolean) ReflectionUtil.getField(Block.class, block, "v"); + this.isTranslucent = !(boolean) ReflectionUtil.getField(Block.class, block, "at"); //TODO Update Mapping for 1.16.1 } public Block getBlock() { @@ -78,7 +74,7 @@ public class BlockMaterial_1_15 implements BlockMaterial { @Override public float getHardness() { - return block.strength; + return craftBlockData.getState().strength; } @Override @@ -88,12 +84,12 @@ public class BlockMaterial_1_15 implements BlockMaterial { @Override public float getSlipperiness() { - return block.m(); + return block.getFrictionFactor(); } @Override public int getLightValue() { - return defaultState.h(); + return defaultState.f(); } @Override @@ -128,7 +124,8 @@ public class BlockMaterial_1_15 implements BlockMaterial { @Override public boolean isToolRequired() { - return !material.isAlwaysDestroyable(); + //TODO Removed in 1.16.1 Replacement not found. + return true; } @Override @@ -148,6 +145,6 @@ public class BlockMaterial_1_15 implements BlockMaterial { @Override public int getMapColor() { - return material.i().rgb; + return material.h().rgb; } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitAdapter_1_15.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java similarity index 84% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitAdapter_1_15.java rename to worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java index be791aa66..a8e096e0c 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitAdapter_1_15.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java @@ -1,11 +1,11 @@ -package com.boydti.fawe.bukkit.adapter.mc1_15; +package com.boydti.fawe.bukkit.adapter.mc1_16_1; import com.boydti.fawe.Fawe; 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; @@ -14,22 +14,9 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; import net.jpountz.util.UnsafeUtils; -import net.minecraft.server.v1_15_R1.Block; -import net.minecraft.server.v1_15_R1.Chunk; -import net.minecraft.server.v1_15_R1.ChunkCoordIntPair; -import net.minecraft.server.v1_15_R1.ChunkSection; -import net.minecraft.server.v1_15_R1.DataBits; -import net.minecraft.server.v1_15_R1.DataPalette; -import net.minecraft.server.v1_15_R1.DataPaletteBlock; -import net.minecraft.server.v1_15_R1.DataPaletteLinear; -import net.minecraft.server.v1_15_R1.GameProfileSerializer; -import net.minecraft.server.v1_15_R1.IBlockData; -import net.minecraft.server.v1_15_R1.PacketPlayOutLightUpdate; -import net.minecraft.server.v1_15_R1.PlayerChunk; -import net.minecraft.server.v1_15_R1.PlayerChunkMap; -import net.minecraft.server.v1_15_R1.World; -import org.bukkit.craftbukkit.v1_15_R1.CraftChunk; -import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; +import net.minecraft.server.v1_16_R1.*; +import org.bukkit.craftbukkit.v1_16_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_16_R1.CraftWorld; import sun.misc.Unsafe; import java.lang.invoke.MethodHandle; @@ -43,7 +30,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; -public final class BukkitAdapter_1_15 extends NMSAdapter { +public final class BukkitAdapter_1_16_1 extends NMSAdapter { /* NMS fields */ @@ -51,6 +38,8 @@ public final class BukkitAdapter_1_15 extends NMSAdapter { public final static Field fieldPalette; public final static Field fieldSize; + public final static Field fieldBitsPerEntry; + public final static Field fieldFluidCount; public final static Field fieldTickingBlockCount; public final static Field fieldNonEmptyBlockCount; @@ -74,6 +63,9 @@ public final class BukkitAdapter_1_15 extends NMSAdapter { fieldPalette = DataPaletteBlock.class.getDeclaredField("h"); fieldPalette.setAccessible(true); + fieldBitsPerEntry = DataBits.class.getDeclaredField("c"); + fieldBitsPerEntry.setAccessible(true); + fieldFluidCount = ChunkSection.class.getDeclaredField("e"); fieldFluidCount.setAccessible(true); fieldTickingBlockCount = ChunkSection.class.getDeclaredField("tickingBlockCount"); @@ -86,8 +78,6 @@ public final class BukkitAdapter_1_15 extends NMSAdapter { fieldDirtyBits = PlayerChunk.class.getDeclaredField("r"); fieldDirtyBits.setAccessible(true); - fieldTickingBlockCount.setAccessible(true); - Method declaredGetVisibleChunk = PlayerChunkMap.class.getDeclaredMethod("getVisibleChunk", long.class); declaredGetVisibleChunk.setAccessible(true); methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk); @@ -120,6 +110,7 @@ public final class BukkitAdapter_1_15 extends NMSAdapter { } protected static DelegateLock applyLock(ChunkSection section) { + //todo there has to be a better way to do this. Maybe using a() in DataPaletteBlock which acquires the lock in NMS? try { synchronized (section) { DataPaletteBlock blocks = section.getBlocks(); @@ -138,7 +129,7 @@ public final class BukkitAdapter_1_15 extends NMSAdapter { } public static Chunk ensureLoaded(World nmsWorld, int X, int Z) { - Chunk nmsChunk = nmsWorld.getChunkIfLoaded(X, Z); + Chunk nmsChunk = nmsWorld.getChunkProvider().getChunkAt(X, Z, false); if (nmsChunk != null) { return nmsChunk; } @@ -159,7 +150,7 @@ public final class BukkitAdapter_1_15 extends NMSAdapter { return TaskManager.IMP.sync(() -> nmsWorld.getChunkAt(X, Z)); } - public static PlayerChunk getPlayerChunk(net.minecraft.server.v1_15_R1.WorldServer nmsWorld, final int cx, final int cz) { + public static PlayerChunk getPlayerChunk(WorldServer nmsWorld, final int cx, final int cz) { PlayerChunkMap chunkMap = nmsWorld.getChunkProvider().playerChunkMap; try { return (PlayerChunk)methodGetVisibleChunk.invoke(chunkMap, ChunkCoordIntPair.pair(cx, cz)); @@ -168,7 +159,7 @@ public final class BukkitAdapter_1_15 extends NMSAdapter { } } - public static void sendChunk(net.minecraft.server.v1_15_R1.WorldServer nmsWorld, int X, int Z, int mask, boolean lighting) { + public static void sendChunk(WorldServer nmsWorld, int X, int Z, int mask, boolean lighting) { PlayerChunk playerChunk = getPlayerChunk(nmsWorld, X, Z); if (playerChunk == null) { return; @@ -191,7 +182,8 @@ public final class BukkitAdapter_1_15 extends NMSAdapter { if (lighting) { ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(X, Z); - PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine()); + boolean trustEdges = false; //Added in 1.16.1 Not sure what it does. + PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine(), trustEdges); playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> { p.playerConnection.sendPacket(packet); }); @@ -202,9 +194,7 @@ public final class BukkitAdapter_1_15 extends NMSAdapter { } return null; }); - return; } - return; } /* @@ -227,9 +217,11 @@ public final class BukkitAdapter_1_15 extends NMSAdapter { Map ticking_blocks = new HashMap<>(); int air; if (get == null) { - air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, set, ticking_blocks, fastmode); + air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, + set, ticking_blocks, fastmode); } else { - air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, get, set, ticking_blocks, fastmode); + air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, + num_palette_buffer, get, set, ticking_blocks, fastmode); } int num_palette = num_palette_buffer[0]; // BlockStates @@ -240,11 +232,13 @@ public final class BukkitAdapter_1_15 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); } @@ -257,14 +251,14 @@ public final class BukkitAdapter_1_15 extends NMSAdapter { final DataBits nmsBits = new DataBits(bitsPerEntry, 4096, bits); final DataPalette palette; // palette = new DataPaletteHash<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d, GameProfileSerializer::a); - palette = new DataPaletteLinear<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d); + palette = new DataPaletteLinear<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::c); // set palette for (int i = 0; i < num_palette; i++) { final int ordinal = paletteToBlock[i]; blockToPalette[ordinal] = Integer.MAX_VALUE; final BlockState state = BlockTypesCache.states[ordinal]; - final IBlockData ibd = ((BlockMaterial_1_15) state.getMaterial()).getState(); + final IBlockData ibd = ((BlockMaterial_1_16_1) state.getMaterial()).getState(); palette.a(ibd); } try { @@ -272,10 +266,11 @@ public final class BukkitAdapter_1_15 extends NMSAdapter { fieldPalette.set(dataPaletteBlocks, palette); fieldSize.set(dataPaletteBlocks, bitsPerEntry); setCount(ticking_blocks.size(), 4096 - air, section); - ticking_blocks.forEach((pos, ordinal) -> { - section.setType(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ(), - Block.getByCombinedId(ordinal)); - }); + if (!fastmode) { + ticking_blocks.forEach((pos, ordinal) -> section + .setType(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ(), + Block.getByCombinedId(ordinal))); + } } catch (final IllegalAccessException | NoSuchFieldException e) { throw new RuntimeException(e); } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java similarity index 81% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java rename to worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java index c9fbca585..d6dada109 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java @@ -1,6 +1,4 @@ -package com.boydti.fawe.bukkit.adapter.mc1_15; - -import static org.slf4j.LoggerFactory.getLogger; +package com.boydti.fawe.bukkit.adapter.mc1_16_1; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; @@ -9,71 +7,46 @@ import com.boydti.fawe.beta.implementation.blocks.CharBlocks; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; import com.boydti.fawe.beta.implementation.queue.QueueHandler; import com.boydti.fawe.bukkit.adapter.DelegateLock; -import com.boydti.fawe.bukkit.adapter.mc1_15.nbt.LazyCompoundTag_1_15; +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.CompoundTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.LongTag; -import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; +import com.sk89q.jnbt.*; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; -import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_15_R1; +import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_16_R1; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockTypes; -import java.util.AbstractSet; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.HashMap; -import java.util.Set; -import java.util.UUID; +import net.minecraft.server.v1_16_R1.*; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.craftbukkit.v1_16_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R1.block.CraftBlock; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; -import net.minecraft.server.v1_15_R1.BiomeBase; -import net.minecraft.server.v1_15_R1.BiomeStorage; -import net.minecraft.server.v1_15_R1.BlockPosition; -import net.minecraft.server.v1_15_R1.Chunk; -import net.minecraft.server.v1_15_R1.ChunkSection; -import net.minecraft.server.v1_15_R1.DataBits; -import net.minecraft.server.v1_15_R1.DataPalette; -import net.minecraft.server.v1_15_R1.DataPaletteBlock; -import net.minecraft.server.v1_15_R1.DataPaletteHash; -import net.minecraft.server.v1_15_R1.DataPaletteLinear; -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.LightEngineThreaded; -import net.minecraft.server.v1_15_R1.NBTTagCompound; -import net.minecraft.server.v1_15_R1.NBTTagInt; -import net.minecraft.server.v1_15_R1.NibbleArray; -import net.minecraft.server.v1_15_R1.SectionPosition; -import net.minecraft.server.v1_15_R1.TileEntity; -import net.minecraft.server.v1_15_R1.WorldServer; -import org.bukkit.World; -import org.bukkit.block.Biome; -import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_15_R1.block.CraftBlock; -import org.bukkit.event.entity.CreatureSpawnEvent; -import org.jetbrains.annotations.NotNull; -public class BukkitGetBlocks_1_15 extends CharGetBlocks { +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 Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); - private final static Function nmsTile2We = tileEntity -> new LazyCompoundTag_1_15(Suppliers.memoize(() -> tileEntity.save(new NBTTagCompound()))); + private final static Function nmsTile2We = tileEntity -> new LazyCompoundTag_1_16_1(Suppliers.memoize(() -> tileEntity.save(new NBTTagCompound()))); public ChunkSection[] sections; public Chunk nmsChunk; public WorldServer world; @@ -81,11 +54,11 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { public NibbleArray[] blockLight = new NibbleArray[16]; public NibbleArray[] skyLight = new NibbleArray[16]; - public BukkitGetBlocks_1_15(World world, int X, int Z) { + public BukkitGetBlocks_1_16_1(World world, int X, int Z) { this(((CraftWorld) world).getHandle(), X, Z); } - public BukkitGetBlocks_1_15(WorldServer world, int X, int Z) { + public BukkitGetBlocks_1_16_1(WorldServer world, int X, int Z) { this.world = world; this.X = X; this.Z = Z; @@ -120,7 +93,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { if (tileEntity == null) { return null; } - return new LazyCompoundTag_1_15(Suppliers.memoize(() -> tileEntity.save(new NBTTagCompound()))); + return new LazyCompoundTag_1_16_1(Suppliers.memoize(() -> tileEntity.save(new NBTTagCompound()))); } @Override @@ -136,7 +109,17 @@ public class BukkitGetBlocks_1_15 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))); @@ -145,8 +128,18 @@ public class BukkitGetBlocks_1_15 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))); @@ -219,19 +212,21 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { @NotNull @Override public Iterator iterator() { - Iterable result = StreamSupport - .stream(Iterables.concat(slices).spliterator(), false).map(input -> { - BukkitImplAdapter adapter = WorldEditPlugin.getInstance() - .getBukkitImplAdapter(); + Iterable result = Iterables.transform(Iterables.concat(slices), new com.google.common.base.Function() { + @Nullable + @Override + public CompoundTag apply(@Nullable Entity input) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); NBTTagCompound tag = new NBTTagCompound(); return (CompoundTag) adapter.toNative(input.save(tag)); - }).collect(Collectors.toList()); + } + }); return result.iterator(); } }; } - private void updateGet(BukkitGetBlocks_1_15 get, Chunk nmsChunk, ChunkSection[] sections, ChunkSection section, char[] arr, int layer) { + private void updateGet(BukkitGetBlocks_1_16_1 get, Chunk nmsChunk, ChunkSection[] sections, ChunkSection section, char[] arr, int layer) { synchronized (get) { if (this.nmsChunk != nmsChunk) { this.nmsChunk = nmsChunk; @@ -242,7 +237,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { this.sections = sections.clone(); } if (this.sections[layer] != section) { - this.sections[layer] = section; + this.sections[layer] = new ChunkSection[]{section}.clone()[0]; } this.blocks[layer] = arr; } @@ -253,8 +248,8 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { entity.valid = false; } - public Chunk ensureLoaded(net.minecraft.server.v1_15_R1.World nmsWorld, int X, int Z) { - return BukkitAdapter_1_15.ensureLoaded(nmsWorld, X, Z); + public Chunk ensureLoaded(net.minecraft.server.v1_16_R1.World nmsWorld, int X, int Z) { + return BukkitAdapter_1_16_1.ensureLoaded(nmsWorld, X, Z); } @Override @@ -266,6 +261,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { // Remove existing tiles { + // Create a copy so that we can remove blocks Map tiles = new HashMap<>(nmsChunk.getTileEntities()); if (!tiles.isEmpty()) { for (Map.Entry entry : tiles.entrySet()) { @@ -277,7 +273,9 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { if (!set.hasSection(layer)) { continue; } - if (set.getBlock(lx, ly, lz).getOrdinal() != 0) { + + int ordinal = set.getBlock(lx, ly, lz).getOrdinal(); + if (ordinal != 0) { TileEntity tile = entry.getValue(); nmsChunk.removeTileEntity(tile.getPosition()); } @@ -290,7 +288,9 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { ChunkSection[] sections = nmsChunk.getSections(); for (int layer = 0; layer < 16; layer++) { - if (!set.hasSection(layer)) continue; + if (!set.hasSection(layer)){ + continue; + } bitMask |= 1 << layer; @@ -298,23 +298,23 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { ChunkSection newSection; ChunkSection existingSection = sections[layer]; if (existingSection == null) { - newSection = BukkitAdapter_1_15.newChunkSection(layer, setArr, fastmode); - if (BukkitAdapter_1_15.setSectionAtomic(sections, null, newSection, layer)) { + newSection = BukkitAdapter_1_16_1.newChunkSection(layer, setArr, fastmode); + if (BukkitAdapter_1_16_1.setSectionAtomic(sections, null, newSection, layer)) { updateGet(this, nmsChunk, sections, newSection, setArr, layer); continue; } 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; } } } + BukkitAdapter_1_16_1.fieldTickingBlockCount.set(existingSection, (short) 0); //ensure that the server doesn't try to tick the chunksection while we're editing it. - BukkitAdapter_1_15.fieldTickingBlockCount.set(existingSection, (short) 0); + DelegateLock lock = BukkitAdapter_1_16_1.applyLock(existingSection); - DelegateLock lock = BukkitAdapter_1_15.applyLock(existingSection); synchronized (this) { synchronized (lock) { lock.untilFree(); @@ -330,9 +330,11 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { } else if (lock.isModified()) { this.reset(layer); } - newSection = BukkitAdapter_1_15.newChunkSection(layer, this::load, setArr, fastmode); - if (!BukkitAdapter_1_15.setSectionAtomic(sections, existingSection, newSection, layer)) { - System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); + newSection = BukkitAdapter_1_16_1 + .newChunkSection(layer, this::load, setArr, fastmode); + if (!BukkitAdapter_1_16_1 + .setSectionAtomic(sections, existingSection, newSection, layer)) { + log.error("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); continue; } else { updateGet(this, nmsChunk, sections, newSection, setArr, layer); @@ -421,7 +423,8 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { final ListTag posTag = (ListTag) entityTagMap.get("Pos"); final ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); if (idTag == null || posTag == null || rotTag == null) { - getLogger(BukkitGetBlocks_1_15.class).debug("Unknown entity tag: " + nativeTag); + getLogger( + BukkitGetBlocks_1_16_1.class).debug("Unknown entity tag: " + nativeTag); continue; } final double x = posTag.getDouble(0); @@ -444,7 +447,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { tag.remove(name); } - entity.f(tag); + entity.save(tag); } entity.setLocation(x, y, z, yaw, pitch); nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM); @@ -481,7 +484,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { tag.set("x", NBTTagInt.a(x)); tag.set("y", NBTTagInt.a(y)); tag.set("z", NBTTagInt.a(z)); - tileEntity.load(tag); + tileEntity.load(tileEntity.getBlock(), tag); } } } @@ -500,7 +503,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { nmsChunk.mustNotSave = false; nmsChunk.markDirty(); // send to player - BukkitAdapter_1_15.sendChunk(nmsWorld, X, Z, finalMask, finalLightUpdate); + BukkitAdapter_1_16_1.sendChunk(nmsWorld, X, Z, finalMask, finalLightUpdate); if (finalizer != null) finalizer.run(); }; } @@ -554,31 +557,27 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { if (data == null || data == FaweCache.IMP.EMPTY_CHAR_4096) { data = new char[4096]; } - DelegateLock lock = BukkitAdapter_1_15.applyLock(section); + DelegateLock lock = BukkitAdapter_1_16_1.applyLock(section); synchronized (lock) { lock.untilFree(); lock.setModified(false); // Efficiently convert ChunkSection to raw data try { - FAWE_Spigot_v1_15_R1 adapter = ((FAWE_Spigot_v1_15_R1) WorldEditPlugin.getInstance().getBukkitImplAdapter()); + FAWE_Spigot_v1_16_R1 adapter = ((FAWE_Spigot_v1_16_R1) WorldEditPlugin.getInstance().getBukkitImplAdapter()); final DataPaletteBlock blocks = section.getBlocks(); - final DataBits bits = (DataBits) BukkitAdapter_1_15.fieldBits.get(blocks); - final DataPalette palette = (DataPalette) BukkitAdapter_1_15.fieldPalette.get(blocks); + final DataBits bits = (DataBits) BukkitAdapter_1_16_1.fieldBits.get(blocks); + final DataPalette palette = (DataPalette) BukkitAdapter_1_16_1.fieldPalette.get(blocks); - //getBits() - final int bitsPerEntry = bits.c(); - //getRaw() + 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) { - //Get the size of the palette num_palette = ((DataPaletteLinear) palette).b(); } else if (palette instanceof DataPaletteHash) { - //Get the size of the palette num_palette = ((DataPaletteHash) palette).b(); } else { num_palette = 0; @@ -590,7 +589,6 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { char ordinal = paletteToBlockChars[paletteVal]; if (ordinal == Character.MAX_VALUE) { paletteToBlockInts[num_palette++] = paletteVal; - //palette.a(Object) is palette.idFor(Object) IBlockData ibd = palette.a(data[i]); if (ibd == null) { ordinal = BlockTypes.AIR.getDefaultState().getOrdinalChar(); @@ -614,7 +612,6 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { try { if (num_palette != 1) { for (int i = 0; i < num_palette; i++) { - //palette.a(Object) is palette.idFor(Object) char ordinal = ordinal(palette.a(i), adapter); paletteToOrdinal[i] = ordinal; } @@ -644,7 +641,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { } } - private final char ordinal(IBlockData ibd, FAWE_Spigot_v1_15_R1 adapter) { + private final char ordinal(IBlockData ibd, FAWE_Spigot_v1_16_R1 adapter) { if (ibd == null) { return BlockTypes.AIR.getDefaultState().getOrdinalChar(); } else { @@ -684,9 +681,13 @@ public class BukkitGetBlocks_1_15 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++) { @@ -705,6 +706,8 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { @Override public boolean trim(boolean aggressive) { + skyLight = new NibbleArray[16]; + blockLight = new NibbleArray[16]; if (aggressive) { sections = null; nmsChunk = null; @@ -718,7 +721,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { try { final DataPaletteBlock blocksExisting = existing.getBlocks(); - final DataPalette palette = (DataPalette) BukkitAdapter_1_15.fieldPalette.get(blocksExisting); + final DataPalette palette = (DataPalette) BukkitAdapter_1_16_1.fieldPalette.get(blocksExisting); int paletteSize; if (palette instanceof DataPaletteLinear) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/FAWEWorldNativeAccess_1_16.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/FAWEWorldNativeAccess_1_16.java new file mode 100644 index 000000000..6b68ae95d --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/FAWEWorldNativeAccess_1_16.java @@ -0,0 +1,174 @@ +package com.boydti.fawe.bukkit.adapter.mc1_16_1; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_16_R1; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import net.minecraft.server.v1_16_R1.Block; +import net.minecraft.server.v1_16_R1.BlockPosition; +import net.minecraft.server.v1_16_R1.Chunk; +import net.minecraft.server.v1_16_R1.ChunkProviderServer; +import net.minecraft.server.v1_16_R1.EnumDirection; +import net.minecraft.server.v1_16_R1.IBlockData; +import net.minecraft.server.v1_16_R1.NBTBase; +import net.minecraft.server.v1_16_R1.NBTTagCompound; +import net.minecraft.server.v1_16_R1.PlayerChunk; +import net.minecraft.server.v1_16_R1.TileEntity; +import net.minecraft.server.v1_16_R1.World; +import org.bukkit.craftbukkit.v1_16_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R1.block.data.CraftBlockData; +import org.bukkit.event.block.BlockPhysicsEvent; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.util.Objects; + +public class FAWEWorldNativeAccess_1_16 implements WorldNativeAccess { + private static final int UPDATE = 1, NOTIFY = 2; + + private final FAWE_Spigot_v1_16_R1 adapter; + private final WeakReference world; + private SideEffectSet sideEffectSet; + + public FAWEWorldNativeAccess_1_16(FAWE_Spigot_v1_16_R1 adapter, WeakReference world) { + this.adapter = adapter; + this.world = world; + } + + private World getWorld() { + return Objects.requireNonNull(world.get(), "The reference to the world was lost"); + } + + @Override + public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + + @Override + public Chunk getChunk(int x, int z) { + return getWorld().getChunkAt(x, z); + } + + @Override + public IBlockData toNative(BlockState state) { + int stateId = BlockStateIdAccess.getBlockStateId(state); + return BlockStateIdAccess.isValidInternalId(stateId) + ? Block.getByCombinedId(stateId) + : ((CraftBlockData) BukkitAdapter.adapt(state)).getState(); + } + + @Override + public IBlockData getBlockState(Chunk chunk, BlockPosition position) { + return chunk.getType(position); + } + + @Nullable + @Override + public IBlockData setBlockState(Chunk chunk, BlockPosition position, IBlockData state) { + return chunk.setType(position, state, false); + } + + @Override + public IBlockData getValidBlockForPosition(IBlockData block, BlockPosition position) { + return Block.b(block, getWorld(), position); + } + + @Override + public BlockPosition getPosition(int x, int y, int z) { + return new BlockPosition(x, y, z); + } + + @Override + public void updateLightingForBlock(BlockPosition position) { + getWorld().getChunkProvider().getLightEngine().a(position); + } + + @Override + public boolean updateTileEntity(BlockPosition position, CompoundTag tag) { + // We will assume that the tile entity was created for us, + // though we do not do this on the other versions + TileEntity tileEntity = getWorld().getTileEntity(position); + if (tileEntity == null) { + return false; + } + NBTBase nativeTag = adapter.fromNative(tag); + tileEntity.load(tileEntity.getBlock(), (NBTTagCompound) nativeTag); + return true; + } + + @Override + public void notifyBlockUpdate(BlockPosition position, IBlockData oldState, IBlockData newState) { + getWorld().notify(position, oldState, newState, UPDATE | NOTIFY); + } + + @Override + public boolean isChunkTicking(Chunk chunk) { + return chunk.getState().isAtLeast(PlayerChunk.State.TICKING); + } + + @Override + public void markBlockChanged(BlockPosition position) { + ((ChunkProviderServer) getWorld().getChunkProvider()).flagDirty(position); + } + + private static final EnumDirection[] NEIGHBOUR_ORDER = { + EnumDirection.WEST, EnumDirection.EAST, + EnumDirection.DOWN, EnumDirection.UP, + EnumDirection.NORTH, EnumDirection.SOUTH + }; + + @Override + public void notifyNeighbors(BlockPosition pos, IBlockData oldState, IBlockData newState) { + World world = getWorld(); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + world.update(pos, oldState.getBlock()); + } else { + // When we don't want events, manually run the physics without them. + // Un-nest neighbour updating + for (EnumDirection direction : NEIGHBOUR_ORDER) { + BlockPosition shifted = pos.shift(direction); + world.getType(shifted).doPhysics(world, shifted, oldState.getBlock(), pos, false); + } + } + if (newState.isComplexRedstone()) { + world.updateAdjacentComparators(pos, newState.getBlock()); + } + } + + @Override + public void updateNeighbors(BlockPosition pos, IBlockData oldState, IBlockData newState, int recursionLimit) { + World world = getWorld(); + // a == updateNeighbors + // b == updateDiagonalNeighbors + oldState.b(world, pos, NOTIFY, recursionLimit); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + CraftWorld craftWorld = world.getWorld(); + if (craftWorld != null) { + BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState)); + world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + } + } + newState.a(world, pos, NOTIFY, recursionLimit); + newState.b(world, pos, NOTIFY, recursionLimit); + } + + @Override + public void onBlockStateChange(BlockPosition pos, IBlockData oldState, IBlockData newState) { + getWorld().a(pos, oldState, newState); + } + + @Override + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + return this.adapter.setBlock(this.getChunk(position.getBlockX() >> 4, position.getBlockZ() >> 4).bukkitChunk, position.getBlockX(), position.getBlockY(), position.getBlockZ(), block, sideEffectSet.shouldApply(SideEffect.LIGHTING)); + } +} \ No newline at end of file diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/MapChunkUtil_1_15.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/MapChunkUtil_1_16_1.java similarity index 76% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/MapChunkUtil_1_15.java rename to worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/MapChunkUtil_1_16_1.java index afdebf1bc..062f623ac 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/MapChunkUtil_1_15.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/MapChunkUtil_1_16_1.java @@ -1,17 +1,17 @@ -package com.boydti.fawe.bukkit.adapter.mc1_15; +package com.boydti.fawe.bukkit.adapter.mc1_16_1; import com.boydti.fawe.bukkit.adapter.MapChunkUtil; -import net.minecraft.server.v1_15_R1.PacketPlayOutMapChunk; +import net.minecraft.server.v1_16_R1.PacketPlayOutMapChunk; -public class MapChunkUtil_1_15 extends MapChunkUtil { - public MapChunkUtil_1_15() throws NoSuchFieldException { +public class MapChunkUtil_1_16_1 extends MapChunkUtil { + public MapChunkUtil_1_16_1() throws NoSuchFieldException { fieldX = PacketPlayOutMapChunk.class.getDeclaredField("a"); fieldZ = PacketPlayOutMapChunk.class.getDeclaredField("b"); fieldBitMask = PacketPlayOutMapChunk.class.getDeclaredField("c"); fieldHeightMap = PacketPlayOutMapChunk.class.getDeclaredField("d"); - fieldChunkData = PacketPlayOutMapChunk.class.getDeclaredField("e"); - fieldBlockEntities = PacketPlayOutMapChunk.class.getDeclaredField("f"); - fieldFull = PacketPlayOutMapChunk.class.getDeclaredField("g"); + fieldChunkData = PacketPlayOutMapChunk.class.getDeclaredField("f"); + fieldBlockEntities = PacketPlayOutMapChunk.class.getDeclaredField("g"); + fieldFull = PacketPlayOutMapChunk.class.getDeclaredField("h"); fieldX.setAccessible(true); fieldZ.setAccessible(true); fieldBitMask.setAccessible(true); diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/nbt/LazyCompoundTag_1_15.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/nbt/LazyCompoundTag_1_16_1.java similarity index 88% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/nbt/LazyCompoundTag_1_15.java rename to worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/nbt/LazyCompoundTag_1_16_1.java index 6faa934f6..07a6da3d5 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/nbt/LazyCompoundTag_1_15.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/nbt/LazyCompoundTag_1_16_1.java @@ -1,14 +1,14 @@ -package com.boydti.fawe.bukkit.adapter.mc1_15.nbt; +package com.boydti.fawe.bukkit.adapter.mc1_16_1.nbt; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.bukkit.WorldEditPlugin; -import net.minecraft.server.v1_15_R1.NBTBase; -import net.minecraft.server.v1_15_R1.NBTNumber; -import net.minecraft.server.v1_15_R1.NBTTagCompound; -import net.minecraft.server.v1_15_R1.NBTTagList; +import net.minecraft.server.v1_16_R1.NBTBase; +import net.minecraft.server.v1_16_R1.NBTNumber; +import net.minecraft.server.v1_16_R1.NBTTagCompound; +import net.minecraft.server.v1_16_R1.NBTTagList; import java.util.ArrayList; import java.util.Collections; @@ -16,15 +16,15 @@ import java.util.List; import java.util.Map; import java.util.function.Supplier; -public class LazyCompoundTag_1_15 extends CompoundTag { +public class LazyCompoundTag_1_16_1 extends CompoundTag { private final Supplier nmsTag; - public LazyCompoundTag_1_15(Supplier tag) { + public LazyCompoundTag_1_16_1(Supplier tag) { super(null); this.nmsTag = tag; } - public LazyCompoundTag_1_15(NBTTagCompound tag) { + public LazyCompoundTag_1_16_1(NBTTagCompound tag) { this(() -> tag); } @@ -93,7 +93,7 @@ public class LazyCompoundTag_1_15 extends CompoundTag { NBTTagList nbtList = (NBTTagList) tag; for (NBTBase elem : nbtList) { if (elem instanceof NBTTagCompound) { - list.add(new LazyCompoundTag_1_15((NBTTagCompound) elem)); + list.add(new LazyCompoundTag_1_16_1((NBTTagCompound) elem)); } else { list.add(WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(elem)); } @@ -149,4 +149,4 @@ public class LazyCompoundTag_1_15 extends CompoundTag { public String toString() { return nmsTag.get().toString(); } -} \ No newline at end of file +} 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 4252d3f32..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,6 +884,14 @@ public class AsyncWorld extends PassthroughExtent implements World { parent.setWaterAnimalSpawnLimit(limit); } + @Override public int getWaterAmbientSpawnLimit() { + return parent.getWaterAmbientSpawnLimit(); + } + + @Override public void setWaterAmbientSpawnLimit(int limit) { + parent.setWaterAmbientSpawnLimit(limit); + } + @Override public int getAmbientSpawnLimit() { return parent.getAmbientSpawnLimit(); @@ -895,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) { @@ -905,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) { @@ -915,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) { @@ -925,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) { @@ -945,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(); } @@ -985,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) { @@ -995,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) { @@ -1015,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(); } @@ -1029,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); } @@ -1050,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); } @@ -1075,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); } @@ -1131,7 +1140,7 @@ public class AsyncWorld extends PassthroughExtent implements World { } @Override - public Collection getForceLoadedChunks() { + public @NotNull Collection getForceLoadedChunks() { return parent.getForceLoadedChunks(); } @@ -1161,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)); } @@ -1191,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); } @@ -1206,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)); } @@ -1232,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)); } @@ -1250,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); } @@ -1284,19 +1294,34 @@ public class AsyncWorld extends PassthroughExtent implements World { return parent.getHighestBlockAt(location, heightmap); } + @Override public long getTicksPerWaterSpawns() { - throw new UnsupportedOperationException(); + return parent.getTicksPerWaterSpawns(); } + @Override public void setTicksPerWaterSpawns(int ticksPerWaterSpawns) { - throw new UnsupportedOperationException(); + parent.setTicksPerWaterSpawns(ticksPerWaterSpawns); } + @Override + public long getTicksPerWaterAmbientSpawns() { + return parent.getTicksPerWaterAmbientSpawns(); + } + + @Override + public void setTicksPerWaterAmbientSpawns(int ticksPerAmbientSpawns) { + parent.setTicksPerWaterAmbientSpawns(ticksPerAmbientSpawns); + } + + @Override public long getTicksPerAmbientSpawns() { - throw new UnsupportedOperationException(); + return parent.getTicksPerAmbientSpawns(); } + @Override public void setTicksPerAmbientSpawns(int ticksPerAmbientSpawns) { - throw new UnsupportedOperationException(); + 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 f1fbbcbb2..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 @@ -3,15 +3,20 @@ package com.boydti.fawe.bukkit.wrapper.state; import com.boydti.fawe.FaweCache; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; + 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; import org.bukkit.persistence.PersistentDataAdapterContext; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; public final class AsyncDataContainer implements PersistentDataContainer { private final CompoundTag root; @@ -69,6 +74,20 @@ public final class AsyncDataContainer implements PersistentDataContainer { return z != null ? z : defaultValue; } + @NotNull + @Override + public Set getKeys() { + Set keys = new HashSet<>(); + this.get(false).keySet().forEach(key -> { + String[] keyData = key.split(":", 2); + if (keyData.length == 2) { + keys.add(new NamespacedKey(keyData[0], keyData[1])); + } + + }); + return keys; + } + public void remove(NamespacedKey key) { Validate.notNull(key, "The provided key for the custom value was null"); get(false).remove(key.toString()); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index d948d0e0a..99a4b183b 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.bukkit; +import com.google.common.collect.Sets; import com.sk89q.bukkit.util.CommandInfo; import com.sk89q.bukkit.util.CommandRegistration; import com.sk89q.worldedit.LocalConfiguration; @@ -30,6 +31,7 @@ import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; import com.sk89q.worldedit.extension.platform.Watchdog; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.concurrency.LazyReference; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.registry.Registries; @@ -44,8 +46,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.EnumMap; import java.util.List; -import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -218,6 +220,18 @@ public class BukkitServerInterface implements MultiUserPlatform { return capabilities; } + private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( + SideEffect.NEIGHBORS + ); + + @Override + public Set getSupportedSideEffects() { + if (plugin.getBukkitImplAdapter() != null) { + return plugin.getBukkitImplAdapter().getSupportedSideEffects(); + } + return SUPPORTED_SIDE_EFFECTS; + } + public void unregisterCommands() { dynamicCommands.unregisterCommands(); } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index 458958852..b40256059 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -24,6 +24,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.boydti.fawe.Fawe; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.implementation.packet.ChunkPacket; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEdit; @@ -33,11 +35,14 @@ import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.biome.BiomeType; @@ -48,15 +53,7 @@ import com.sk89q.worldedit.world.weather.WeatherTypes; import io.papermc.lib.PaperLib; import java.lang.ref.WeakReference; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.function.Supplier; +import java.util.*; import javax.annotation.Nullable; import org.bukkit.Effect; import org.bukkit.TreeType; @@ -83,6 +80,7 @@ public class BukkitWorld extends AbstractWorld { } private final WeakReference worldRef; + private final WorldNativeAccess worldNativeAccess; /** * Construct the object. @@ -91,6 +89,12 @@ public class BukkitWorld extends AbstractWorld { */ public BukkitWorld(World world) { this.worldRef = new WeakReference<>(world); + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + if (adapter != null) { + this.worldNativeAccess = adapter.createWorldNativeAccess(world); + } else { + this.worldNativeAccess = null; + } } @Override @@ -479,26 +483,22 @@ public class BukkitWorld extends AbstractWorld { } @Override - public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException { - BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - if (adapter != null) { + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) { + if (worldNativeAccess != null) { try { - return adapter.setBlock(BukkitAdapter.adapt(getWorld(), position), block, notifyAndLight); + return worldNativeAccess.setBlock(position, block, sideEffects); } catch (Exception e) { if (block instanceof BaseBlock && ((BaseBlock) block).getNbtData() != null) { - logger.warn("Tried to set a corrupt tile entity at " + position.toString()); - logger.warn(((BaseBlock) block).getNbtData().toString()); + logger.warn("Tried to set a corrupt tile entity at " + position.toString() + + ": " + ((BaseBlock) block).getNbtData(), e); + } else { + logger.warn("Failed to set block via adapter, falling back to generic", e); } - e.printStackTrace(); - Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()); - bukkitBlock.setBlockData(BukkitAdapter.adapt(block), notifyAndLight); - return true; } - } else { - Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()); - bukkitBlock.setBlockData(BukkitAdapter.adapt(block), false); - return true; } + Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()); + bukkitBlock.setBlockData(BukkitAdapter.adapt(block), sideEffects.doesApplyAny()); + return true; } @Override @@ -512,14 +512,17 @@ public class BukkitWorld extends AbstractWorld { } @Override - public boolean notifyAndLightBlock(BlockVector3 position, com.sk89q.worldedit.world.block.BlockState previousType) throws WorldEditException { - BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - if (adapter != null) { - adapter.notifyAndLightBlock(BukkitAdapter.adapt(getWorld(), position), previousType); - return true; + public Set applySideEffects(BlockVector3 position, com.sk89q.worldedit.world.block.BlockState previousType, + SideEffectSet sideEffectSet) { + if (worldNativeAccess != null) { + worldNativeAccess.applySideEffects(position, previousType, sideEffectSet); + return Sets.intersection( + WorldEditPlugin.getInstance().getInternalPlatform().getSupportedSideEffects(), + sideEffectSet.getSideEffectsToApply() + ); } - return false; + return ImmutableSet.of(); } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index 376111663..3124b04f6 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -33,8 +33,8 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.bukkit.adapter.AdapterLoadException; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplLoader; -import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_15_R1; import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_15_R2; +import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_16_R1; import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; import com.sk89q.worldedit.event.platform.PlatformReadyEvent; @@ -371,8 +371,8 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter BukkitImplLoader adapterLoader = new BukkitImplLoader(); try { adapterLoader.addClass(FAWE_Spigot_v1_14_R4.class); - adapterLoader.addClass(FAWE_Spigot_v1_15_R1.class); adapterLoader.addClass(FAWE_Spigot_v1_15_R2.class); + adapterLoader.addClass(FAWE_Spigot_v1_16_R1.class); } catch (Throwable throwable) { throwable.printStackTrace(); } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java index 591626f0f..d77073045 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -30,19 +30,21 @@ import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.registry.BlockMaterial; import java.util.Map; import java.util.OptionalInt; +import java.util.Set; import javax.annotation.Nullable; import org.bukkit.Location; import org.bukkit.World; @@ -95,23 +97,12 @@ public interface BukkitImplAdapter extends IBukkitAdapter { BaseBlock getBlock(Location location); /** - * Set the block at the given location. + * Create a {@link WorldNativeAccess} for the given world reference. * - * @param location the location - * @param state the block - * @param notifyAndLight notify and light if set - * @return true if a block was likely changed + * @param world the world reference + * @return the native access object */ - > boolean setBlock(Location location, B state, boolean notifyAndLight); - - /** - * Notifies the simulation that the block at the given location has - * been changed and it must be re-lighted (and issue other events). - * - * @param position position of the block - * @param previousType the type of the previous block that was there - */ - void notifyAndLightBlock(Location position, BlockState previousType); + WorldNativeAccess createWorldNativeAccess(World world); /** * Get the state for the given entity. @@ -186,6 +177,13 @@ public interface BukkitImplAdapter extends IBukkitAdapter { */ BaseItemStack adapt(ItemStack itemStack); + /** + * Get the {@link SideEffect}s that this adapter supports. + * + * @return The side effects that are supported + */ + Set getSupportedSideEffects(); + default OptionalInt getInternalBlockStateId(BlockData data) { // return OptionalInt.empty(); return getInternalBlockStateId(BukkitAdapter.adapt(data)); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java index 172aaa2e7..00807b4b5 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java @@ -155,11 +155,20 @@ public class BukkitImplLoader { */ public BukkitImplAdapter loadAdapter() throws AdapterLoadException { for (String className : adapterCandidates) { + System.out.println("Candidate: " + className); try { Class cls = Class.forName(className); - if (cls.isSynthetic()) continue; + if (cls.isSynthetic()){ + System.out.println(className + " is synthetic, continuing"); + continue; + }else{ + System.out.println(className + " is not synthetic"); + } if (BukkitImplAdapter.class.isAssignableFrom(cls)) { + System.out.println(className + " is assignable from BukkitImplAdapter, returning"); return (BukkitImplAdapter) cls.newInstance(); + }else{ + System.out.println(className + " is NOT assignable from BukkitImplAdapter, returning"); } } catch (ClassNotFoundException e) { log.warn("Failed to load the Bukkit adapter class '" + className + @@ -170,6 +179,8 @@ public class BukkitImplLoader { } catch (Throwable e) { if (className.equals(customCandidate)) { log.warn("Failed to load the Bukkit adapter class '" + className + "'", e); + }else{ + log.warn(className + " is not custom candidate.", e); } } } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/IDelegateBukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/IDelegateBukkitImplAdapter.java index f488a1617..4d7dc9740 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/IDelegateBukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/IDelegateBukkitImplAdapter.java @@ -12,6 +12,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; @@ -65,15 +66,6 @@ public interface IDelegateBukkitImplAdapter extends BukkitImplAdapter { return getParent().getBlock(location); } - default > boolean setBlock(Location location, B state, boolean notifyAndLight) { - return getParent().setBlock(location, state, notifyAndLight); - } - - @Override - default void notifyAndLightBlock(Location position, BlockState previousType) { - getParent().notifyAndLightBlock(position, previousType); - } - @Override @Nullable default BaseEntity getEntity(Entity entity) { diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_14_R4.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_14_R4.java index b5094f0db..09aa43ca3 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_14_R4.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_14_R4.java @@ -24,10 +24,7 @@ import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.beta.implementation.queue.SingleThreadQueueExtent; -import com.boydti.fawe.bukkit.adapter.mc1_14.BlockMaterial_1_14; -import com.boydti.fawe.bukkit.adapter.mc1_14.BukkitAdapter_1_14; -import com.boydti.fawe.bukkit.adapter.mc1_14.BukkitGetBlocks_1_14; -import com.boydti.fawe.bukkit.adapter.mc1_14.MapChunkUtil_1_14; +import com.boydti.fawe.bukkit.adapter.mc1_14.*; import com.boydti.fawe.bukkit.adapter.mc1_14.nbt.LazyCompoundTag_1_14; import com.google.common.io.Files; import com.sk89q.jnbt.CompoundTag; @@ -42,11 +39,13 @@ import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.LazyBaseEntity; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.*; import com.sk89q.worldedit.world.entity.EntityType; @@ -69,8 +68,10 @@ import org.bukkit.generator.ChunkGenerator; import javax.annotation.Nullable; import java.io.File; import java.io.IOException; +import java.lang.ref.WeakReference; import java.util.Map; import java.util.OptionalInt; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.function.Supplier; @@ -149,8 +150,8 @@ public final class FAWE_Spigot_v1_14_R4 extends CachedBukkitAdapter implements I } @Override - public > boolean setBlock(Location location, B state, boolean notifyAndLight) { - return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, notifyAndLight); + public Set getSupportedSideEffects() { + return SideEffectSet.defaults().getSideEffectsToApply(); } public boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, BlockStateHolder state, boolean update) { @@ -204,6 +205,12 @@ public final class FAWE_Spigot_v1_14_R4 extends CachedBukkitAdapter implements I return true; } + @Override + public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { + return new FAWEWorldNativeAccess_1_14(this, + new WeakReference<>(((CraftWorld) world).getHandle())); + } + @Nullable private static String getEntityId(Entity entity) { MinecraftKey minecraftkey = EntityTypes.getName(entity.getEntityType()); @@ -292,11 +299,6 @@ public final class FAWE_Spigot_v1_14_R4 extends CachedBukkitAdapter implements I return material.getCraftBlockData(); } - @Override - public void notifyAndLightBlock(Location position, BlockState previousType) { - this.setBlock(position.getChunk(), position.getBlockX(), position.getBlockY(), position.getBlockZ(), previousType, true); - } - private MapChunkUtil_1_14 mapUtil = new MapChunkUtil_1_14(); @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java index 3ee354bd6..85dd26089 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java @@ -26,10 +26,7 @@ import com.boydti.fawe.beta.IQueueChunk; import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.beta.implementation.queue.SingleThreadQueueExtent; -import com.boydti.fawe.bukkit.adapter.mc1_15_2.BlockMaterial_1_15_2; -import com.boydti.fawe.bukkit.adapter.mc1_15_2.BukkitAdapter_1_15_2; -import com.boydti.fawe.bukkit.adapter.mc1_15_2.BukkitGetBlocks_1_15_2; -import com.boydti.fawe.bukkit.adapter.mc1_15_2.MapChunkUtil_1_15_2; +import com.boydti.fawe.bukkit.adapter.mc1_15_2.*; import com.boydti.fawe.bukkit.adapter.mc1_15_2.nbt.LazyCompoundTag_1_15_2; import com.google.common.io.Files; import com.sk89q.jnbt.CompoundTag; @@ -44,9 +41,12 @@ import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.LazyBaseEntity; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; @@ -71,8 +71,10 @@ import org.bukkit.generator.ChunkGenerator; import javax.annotation.Nullable; import java.io.File; import java.io.IOException; +import java.lang.ref.WeakReference; import java.util.Map; import java.util.OptionalInt; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -158,11 +160,11 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I } @Override - public > boolean setBlock(Location location, B state, boolean notifyAndLight) { - return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, notifyAndLight); + public Set getSupportedSideEffects() { + return SideEffectSet.defaults().getSideEffectsToApply(); } - public > boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, B state, boolean update) { + public boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, BlockStateHolder state, boolean update) { CraftChunk craftChunk = (CraftChunk) chunk; Chunk nmsChunk = craftChunk.getHandle(); World nmsWorld = nmsChunk.getWorld(); @@ -213,6 +215,12 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I return true; } + @Override + public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { + return new FAWEWorldNativeAccess_1_15_2(this, + new WeakReference<>(((CraftWorld) world).getHandle())); + } + @Nullable private static String getEntityId(Entity entity) { MinecraftKey minecraftkey = EntityTypes.getName(entity.getEntityType()); @@ -301,11 +309,6 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I return material.getCraftBlockData(); } - @Override - public void notifyAndLightBlock(Location position, BlockState previousType) { - this.setBlock(position.getChunk(), position.getBlockX(), position.getBlockY(), position.getBlockZ(), previousType, true); - } - private MapChunkUtil_1_15_2 mapUtil = new MapChunkUtil_1_15_2(); @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R1.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java similarity index 66% rename from worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R1.java rename to worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java index 49dfea72f..3a5f2af11 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R1.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java @@ -26,11 +26,8 @@ import com.boydti.fawe.beta.IQueueChunk; import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.beta.implementation.queue.SingleThreadQueueExtent; -import com.boydti.fawe.bukkit.adapter.mc1_15.BlockMaterial_1_15; -import com.boydti.fawe.bukkit.adapter.mc1_15.BukkitAdapter_1_15; -import com.boydti.fawe.bukkit.adapter.mc1_15.BukkitGetBlocks_1_15; -import com.boydti.fawe.bukkit.adapter.mc1_15.MapChunkUtil_1_15; -import com.boydti.fawe.bukkit.adapter.mc1_15.nbt.LazyCompoundTag_1_15; +import com.boydti.fawe.bukkit.adapter.mc1_16_1.*; +import com.boydti.fawe.bukkit.adapter.mc1_16_1.nbt.LazyCompoundTag_1_16_1; import com.google.common.io.Files; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; @@ -44,35 +41,39 @@ import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.LazyBaseEntity; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.*; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.registry.BlockMaterial; -import net.minecraft.server.v1_15_R1.*; +import net.minecraft.server.v1_16_R1.*; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World.Environment; import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.v1_15_R1.CraftChunk; -import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_15_R1.block.CraftBlock; -import org.bukkit.craftbukkit.v1_15_R1.block.data.CraftBlockData; -import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity; -import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_16_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_16_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R1.block.CraftBlock; +import org.bukkit.craftbukkit.v1_16_R1.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; import org.bukkit.entity.Player; import org.bukkit.generator.ChunkGenerator; import javax.annotation.Nullable; import java.io.File; import java.io.IOException; +import java.lang.ref.WeakReference; import java.util.Map; import java.util.OptionalInt; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -81,16 +82,16 @@ import java.util.stream.Stream; import static com.google.common.base.Preconditions.checkNotNull; -public final class FAWE_Spigot_v1_15_R1 extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter { - private final Spigot_v1_15_R1 parent; +public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter { + private final Spigot_v1_16_R1 parent; private char[] ibdToStateOrdinal; // ------------------------------------------------------------------------ // Code that may break between versions of Minecraft // ------------------------------------------------------------------------ - public FAWE_Spigot_v1_15_R1() throws NoSuchFieldException, NoSuchMethodException { - this.parent = new Spigot_v1_15_R1(); + public FAWE_Spigot_v1_16_R1() throws NoSuchFieldException, NoSuchMethodException { + this.parent = new Spigot_v1_16_R1(); } @Override @@ -99,11 +100,11 @@ public final class FAWE_Spigot_v1_15_R1 extends CachedBukkitAdapter implements I } private synchronized boolean init() { - if (ibdToStateOrdinal != null) return false; + if (ibdToStateOrdinal != null && ibdToStateOrdinal[1] != 0) return false; ibdToStateOrdinal = new char[Block.REGISTRY_ID.a()]; // size for (int i = 0; i < ibdToStateOrdinal.length; i++) { BlockState state = BlockTypesCache.states[i]; - BlockMaterial_1_15 material = (BlockMaterial_1_15) state.getMaterial(); + BlockMaterial_1_16_1 material = (BlockMaterial_1_16_1) state.getMaterial(); int id = Block.REGISTRY_ID.getId(material.getState()); ibdToStateOrdinal[id] = state.getOrdinalChar(); } @@ -113,13 +114,15 @@ public final class FAWE_Spigot_v1_15_R1 extends CachedBukkitAdapter implements I @Override public BlockMaterial getMaterial(BlockType blockType) { Block block = getBlock(blockType); - return new BlockMaterial_1_15(block); + return new BlockMaterial_1_16_1(block); } @Override public BlockMaterial getMaterial(BlockState state) { IBlockData bs = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); - return new BlockMaterial_1_15(bs.getBlock(), bs); + return new BlockMaterial_1_16_1(bs.getBlock(), bs); + + } public Block getBlock(BlockType blockType) { @@ -156,24 +159,24 @@ public final class FAWE_Spigot_v1_15_R1 extends CachedBukkitAdapter implements I } @Override - public > boolean setBlock(Location location, B state, boolean notifyAndLight) { - return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, notifyAndLight); + public Set getSupportedSideEffects() { + return SideEffectSet.defaults().getSideEffectsToApply(); } - public > boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, B state, boolean update) { + public boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, BlockStateHolder state, boolean update) { CraftChunk craftChunk = (CraftChunk) chunk; Chunk nmsChunk = craftChunk.getHandle(); World nmsWorld = nmsChunk.getWorld(); BlockPosition blockPos = new BlockPosition(x, y, z); - IBlockData blockData = ((BlockMaterial_1_15) state.getMaterial()).getState(); + IBlockData blockData = ((BlockMaterial_1_16_1) state.getMaterial()).getState(); ChunkSection[] sections = nmsChunk.getSections(); int y4 = y >> 4; ChunkSection section = sections[y4]; IBlockData existing; if (section == null) { - existing = ((BlockMaterial_1_15) BlockTypes.AIR.getDefaultState().getMaterial()).getState(); + existing = ((BlockMaterial_1_16_1) BlockTypes.AIR.getDefaultState().getMaterial()).getState(); } else { existing = section.getType(x & 15, y & 15, z & 15); } @@ -194,7 +197,7 @@ public final class FAWE_Spigot_v1_15_R1 extends CachedBukkitAdapter implements I tag.set("x", NBTTagInt.a(x)); tag.set("y", NBTTagInt.a(y)); tag.set("z", NBTTagInt.a(z)); - tileEntity.load(tag); // readTagIntoTileEntity - load data + tileEntity.load(tileEntity.getBlock(), tag); // readTagIntoTileEntity - load data } } } else { @@ -211,6 +214,12 @@ public final class FAWE_Spigot_v1_15_R1 extends CachedBukkitAdapter implements I return true; } + @Override + public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { + return new FAWEWorldNativeAccess_1_16(this, + new WeakReference<>(((CraftWorld)world).getHandle())); + } + @Nullable private static String getEntityId(Entity entity) { MinecraftKey minecraftkey = EntityTypes.getName(entity.getEntityType()); @@ -245,7 +254,7 @@ public final class FAWE_Spigot_v1_15_R1 extends CachedBukkitAdapter implements I @Override public OptionalInt getInternalBlockStateId(BlockState state) { - BlockMaterial_1_15 material = (BlockMaterial_1_15) state.getMaterial(); + BlockMaterial_1_16_1 material = (BlockMaterial_1_16_1) state.getMaterial(); IBlockData mcState = material.getCraftBlockData().getState(); return OptionalInt.of(Block.REGISTRY_ID.getId(mcState)); } @@ -286,7 +295,7 @@ public final class FAWE_Spigot_v1_15_R1 extends CachedBukkitAdapter implements I } catch (NullPointerException e) { init(); return adaptToChar(ibd); - } catch (ArrayIndexOutOfBoundsException e1) { + } catch(ArrayIndexOutOfBoundsException e1){ Fawe.debug("Attempted to convert " + ibd.getBlock() + " with ID " + Block.REGISTRY_ID.getId(ibd) + " to char. ibdToStateOrdinal length: " + ibdToStateOrdinal.length + ". Defaulting to air!"); return 0; } @@ -295,21 +304,16 @@ public final class FAWE_Spigot_v1_15_R1 extends CachedBukkitAdapter implements I @Override public > BlockData adapt(B state) { - BlockMaterial_1_15 material = (BlockMaterial_1_15) state.getMaterial(); + BlockMaterial_1_16_1 material = (BlockMaterial_1_16_1) state.getMaterial(); return material.getCraftBlockData(); } - @Override - public void notifyAndLightBlock(Location position, BlockState previousType) { - this.setBlock(position.getChunk(), position.getBlockX(), position.getBlockY(), position.getBlockZ(), previousType, true); - } - - private MapChunkUtil_1_15 mapUtil = new MapChunkUtil_1_15(); + private MapChunkUtil_1_16_1 mapUtil = new MapChunkUtil_1_16_1(); @Override public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket packet) { WorldServer nmsWorld = ((CraftWorld) world).getHandle(); - PlayerChunk map = BukkitAdapter_1_15.getPlayerChunk(nmsWorld, packet.getChunkX(), packet.getChunkZ()); + PlayerChunk map = BukkitAdapter_1_16_1.getPlayerChunk(nmsWorld, packet.getChunkX(), packet.getChunkZ()); if (map != null && map.hasBeenLoaded()) { boolean flag = false; PlayerChunk.d players = map.players; @@ -362,91 +366,93 @@ public final class FAWE_Spigot_v1_15_R1 extends CachedBukkitAdapter implements I @Override public NBTBase fromNative(Tag foreign) { - if (foreign instanceof LazyCompoundTag_1_15) { - return ((LazyCompoundTag_1_15) foreign).get(); + if (foreign instanceof LazyCompoundTag_1_16_1) { + return ((LazyCompoundTag_1_16_1) foreign).get(); } return parent.fromNative(foreign); } @Override public boolean regenerate(org.bukkit.World world, Region region, EditSession editSession) { - WorldServer originalWorld = ((CraftWorld) world).getHandle(); - ChunkProviderServer provider = originalWorld.getChunkProvider(); - if (!(provider instanceof ChunkProviderServer)) { - return false; - } +// WorldServer originalWorld = ((CraftWorld) world).getHandle(); +// ChunkProviderServer provider = originalWorld.getChunkProvider(); +// if (!(provider instanceof ChunkProviderServer)) { +// return false; +// } +// +// File saveFolder = Files.createTempDir(); +// // register this just in case something goes wrong +// // normally it should be deleted at the end of this method +// saveFolder.deleteOnExit(); +// try { +// MinecraftServer server = originalWorld.getServer().getServer(); +// Convertable.ConversionSession originalDataManager = server.convertable; +//// Convertable.ConversionSession saveHandler = new Convertable.ConversionSession(world.getName(), world.); +// WorldData newWorldData = new WorldData(originalWorld.worldData.a((NBTTagCompound) null), +// server.dataConverterManager, getDataVersion(), null); +// newWorldData.setName(UUID.randomUUID().toString()); +// +// ChunkGenerator gen = world.getGenerator(); +// Environment env = world.getEnvironment(); +// try (WorldServer freshWorld = new WorldServer(server, +// server.executorService, originalDataManager, +// newWorldData, +// originalWorld.worldProvider.getDimensionManager(), +// originalWorld.getMethodProfiler(), +// server.worldLoadListenerFactory.create(11), +// env, +// gen){ +// @Override +// public boolean addEntityChunk(Entity entity) { +// //Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously +// return false; +// } +// }) { +// +// // Pre-gen all the chunks +// // We need to also pull one more chunk in every direction +// Fawe.get().getQueueHandler().startSet(true); +// try { +// IQueueExtent extent = new SingleThreadQueueExtent(); +// extent.init(null, (x, z) -> new BukkitGetBlocks_1_16_1(freshWorld, x, z) { +// @Override +// public Chunk ensureLoaded(World nmsWorld, int X, int Z) { +// Chunk cached = nmsWorld.getChunkIfLoaded(X, Z); +// if (cached != null) return cached; +// Future future = Fawe.get().getQueueHandler().sync((Supplier) () -> freshWorld.getChunkAt(X, Z)); +// while (!future.isDone()) { +// // this feels so dirty +// freshWorld.getChunkProvider().runTasks(); +// } +// try { +// return future.get(); +// } catch (InterruptedException | ExecutionException e) { +// throw new RuntimeException(e); +// } +// } +// }, null); +// for (BlockVector3 vec : region) { +// editSession.setBlock(vec, extent.getFullBlock(vec)); +// } +// } finally { +// Fawe.get().getQueueHandler().endSet(true); +// } +// } catch (IOException e) { +// throw new RuntimeException(e); +// } +// } catch (MaxChangedBlocksException e) { +// throw new RuntimeException(e); +// } finally { +// saveFolder.delete(); +// } +// return true; - File saveFolder = Files.createTempDir(); - // register this just in case something goes wrong - // normally it should be deleted at the end of this method - saveFolder.deleteOnExit(); - try { - MinecraftServer server = originalWorld.getServer().getServer(); - WorldNBTStorage originalDataManager = originalWorld.getDataManager(); - WorldNBTStorage saveHandler = new WorldNBTStorage(saveFolder, originalDataManager.getDirectory().getName(), server, originalDataManager.getDataFixer()); - WorldData newWorldData = new WorldData(originalWorld.worldData.a((NBTTagCompound) null), - server.dataConverterManager, getDataVersion(), null); - newWorldData.setName(UUID.randomUUID().toString()); - - ChunkGenerator gen = world.getGenerator(); - Environment env = world.getEnvironment(); - try (WorldServer freshWorld = new WorldServer(server, - server.executorService, saveHandler, - newWorldData, - originalWorld.worldProvider.getDimensionManager(), - originalWorld.getMethodProfiler(), - server.worldLoadListenerFactory.create(11), - env, - gen){ - @Override - public boolean addEntityChunk(net.minecraft.server.v1_15_R1.Entity entity) { - //Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously - return false; - } - }) { - - // Pre-gen all the chunks - // We need to also pull one more chunk in every direction - Fawe.get().getQueueHandler().startSet(true); - try { - IQueueExtent extent = new SingleThreadQueueExtent(); - extent.init(null, (x, z) -> new BukkitGetBlocks_1_15(freshWorld, x, z) { - @Override - public Chunk ensureLoaded(World nmsWorld, int X, int Z) { - Chunk cached = nmsWorld.getChunkIfLoaded(X, Z); - if (cached != null) return cached; - Future future = Fawe.get().getQueueHandler().sync((Supplier) () -> freshWorld.getChunkAt(X, Z)); - while (!future.isDone()) { - // this feels so dirty - freshWorld.getChunkProvider().runTasks(); - } - try { - return future.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } - }, null); - for (BlockVector3 vec : region) { - editSession.setBlock(vec, extent.getFullBlock(vec)); - } - } finally { - Fawe.get().getQueueHandler().endSet(true); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } catch (MaxChangedBlocksException e) { - throw new RuntimeException(e); - } finally { - saveFolder.delete(); - } - return true; + return false; //TODO: rework or remove for 1.16 } @Override public IChunkGet get(org.bukkit.World world, int chunkX, int chunkZ) { - return new BukkitGetBlocks_1_15(world, chunkX, chunkZ); + return new BukkitGetBlocks_1_16_1(world, chunkX, chunkZ); } @Override 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-bukkit/src/main/resources/worldedit-adapters.jar b/worldedit-bukkit/src/main/resources/worldedit-adapters.jar index 3024c70c5..f64be9496 100644 Binary files a/worldedit-bukkit/src/main/resources/worldedit-adapters.jar and b/worldedit-bukkit/src/main/resources/worldedit-adapters.jar differ diff --git a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIPlatform.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIPlatform.java index c2e9eaba9..7dcfa2daf 100644 --- a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIPlatform.java +++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIPlatform.java @@ -19,10 +19,12 @@ package com.sk89q.worldedit.cli; +import com.google.common.collect.ImmutableSet; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.AbstractPlatform; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Preference; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.entity.EntityTypes; @@ -33,6 +35,7 @@ import java.util.ArrayList; import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.Timer; import java.util.TimerTask; @@ -153,6 +156,11 @@ class CLIPlatform extends AbstractPlatform { return capabilities; } + @Override + public Set getSupportedSideEffects() { + return ImmutableSet.of(); + } + public void addWorld(World world) { worlds.add(world); } diff --git a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java index 69d1cb1c9..6be24d545 100644 --- a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java +++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.cli.schematic; +import com.google.common.collect.ImmutableSet; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; @@ -34,6 +35,8 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.biome.BiomeType; @@ -46,6 +49,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.List; import java.util.Locale; +import java.util.Set; import javax.annotation.Nullable; @@ -74,15 +78,14 @@ public class ClipboardWorld extends AbstractWorld implements Clipboard, CLIWorld } @Override - public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) - throws WorldEditException { + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { dirty = true; return clipboard.setBlock(position, block); } @Override - public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException { - return false; + public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException { + return ImmutableSet.of(); } @Override 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/jnbt/anvil/MCAWorld.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWorld.java index a13cd75b6..6046c5928 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWorld.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWorld.java @@ -13,12 +13,16 @@ import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; import javax.annotation.Nullable; import java.io.File; +import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; @@ -35,6 +39,11 @@ public class MCAWorld extends AbstractWorld { return path.getName(); } + @Override + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + return false; + } + @Override public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { @@ -46,6 +55,11 @@ public class MCAWorld extends AbstractWorld { return false; } + @Override + public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException { + return SideEffectSet.none().getSideEffectsToApply(); + } + @Override public boolean clearContainerBlockContents(BlockVector3 position) { return false; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/CopyPastaBrush.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/CopyPastaBrush.java index 582136f57..8a74f5f3d 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/CopyPastaBrush.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/CopyPastaBrush.java @@ -31,7 +31,8 @@ public class CopyPastaBrush implements Brush, ResettableTool { private final LocalSession session; private final Player player; - public boolean autoRotate, randomRotate; + public boolean autoRotate; + public boolean randomRotate; public CopyPastaBrush(Player player, LocalSession session, boolean randomRotate, boolean autoRotate) { session.setClipboard(null); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java index 1e90346c4..b50bce169 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java @@ -29,10 +29,7 @@ import com.boydti.fawe.util.TextureUtil; import com.boydti.fawe.util.image.Drawable; import com.boydti.fawe.util.image.ImageViewer; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.MaxChangedBlocksException; -import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extent.clipboard.Clipboard; @@ -48,9 +45,7 @@ import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.session.ClipboardHolder; -import com.sk89q.worldedit.util.Identifiable; -import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.util.*; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; @@ -68,6 +63,7 @@ import java.lang.reflect.Field; import java.nio.file.Path; import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Supplier; @@ -750,12 +746,24 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr return BlockVector3.at(getWidth() - 1, 255, getLength() - 1); } + @Override + public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) + throws WorldEditException{ + return SideEffectSet.none().getSideEffectsToApply(); + } + @Override public > boolean setBlock(BlockVector3 position, B block) throws WorldEditException { return setBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ(), block); } + @Override + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) + throws WorldEditException { + return setBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ(), block); + } + private boolean setBlock(int x, int y, int z, char combined) { int index = z * getWidth() + x; if (index < 0 || index >= getArea()) { 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/boydti/fawe/wrappers/WorldWrapper.java b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java index 771262350..812b440dc 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java @@ -24,9 +24,7 @@ import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.util.Direction; -import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.util.*; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; @@ -38,6 +36,7 @@ import com.sk89q.worldedit.world.weather.WeatherType; import javax.annotation.Nullable; import java.util.List; +import java.util.Set; public class WorldWrapper extends AbstractWorld { @@ -88,6 +87,12 @@ public class WorldWrapper extends AbstractWorld { return parent.useItem(position, item, face); } + @Override + public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) + throws WorldEditException{ + return parent.applySideEffects(position, previousType, sideEffectSet); + } + @Override public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException { return parent.setBlock(position, block, notifyAndLight); @@ -99,6 +104,11 @@ public class WorldWrapper extends AbstractWorld { return parent.setBlock(x, y, z, block); } + @Override + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + return parent.setBlock(position, block, sideEffects); + } + @Override public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { return parent.setTile(x, y, z, tile); 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/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index e289dd249..29caa5b9c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -119,7 +119,7 @@ import com.sk89q.worldedit.regions.shape.RegionShape; import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Direction; -import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.eventbus.EventBus; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; @@ -575,6 +575,20 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { disableHistory(enabled); } + /** + * Set which block updates should occur. + * + * @param sideEffectSet side effects to enable + */ + public void setSideEffectApplier(SideEffectSet sideEffectSet) { + //Do nothing; TODO: SideEffects currently not fully implemented in FAWE. + } + + public SideEffectSet getSideEffectApplier() { + //Do nothing; TODO: SideEffects currently not fully implemented in FAWE. + return SideEffectSet.defaults(); + } + /** * Disable history (or re-enable) * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index cc22a2972..eeb297713 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -72,6 +72,7 @@ import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.Identifiable; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; @@ -145,7 +146,7 @@ public class LocalSession implements TextureHolder { private transient Snapshot snapshotExperimental; private transient boolean hasCUISupport = false; private transient int cuiVersion = -1; - private transient boolean fastMode = false; + private transient SideEffectSet sideEffectSet = SideEffectSet.defaults(); private transient Mask mask; private transient Mask sourceMask; private transient TextureUtil texture; @@ -1494,7 +1495,7 @@ public class LocalSession implements TextureHolder { builder.blockBag(blockBag); } builder.command(command); - builder.fastmode(fastMode); + builder.fastmode(!this.sideEffectSet.doesApplyAny()); editSession = builder.build(); @@ -1513,7 +1514,7 @@ public class LocalSession implements TextureHolder { } private void prepareEditingExtents(EditSession editSession, Actor actor) { - editSession.setFastMode(fastMode); + editSession.setSideEffectApplier(sideEffectSet); editSession.setReorderMode(reorderMode); if (editSession.getSurvivalExtent() != null) { editSession.getSurvivalExtent().setStripNbt(!actor.hasPermission("worldedit.setnbt")); @@ -1521,13 +1522,32 @@ public class LocalSession implements TextureHolder { editSession.setTickingWatchdog(tickingWatchdog); } + /** + * Gets the side effect applier of this session. + * + * @return the side effect applier + */ + public SideEffectSet getSideEffectSet() { + return this.sideEffectSet; + } + + /** + * Sets the side effect applier for this session + * + * @param sideEffectSet the side effect applier + */ + public void setSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + /** * Checks if the session has fast mode enabled. * * @return true if fast mode is enabled */ + @Deprecated public boolean hasFastMode() { - return fastMode; + return !this.sideEffectSet.doesApplyAny(); } /** @@ -1535,8 +1555,9 @@ public class LocalSession implements TextureHolder { * * @param fastMode true if fast mode is enabled */ + @Deprecated public void setFastMode(boolean fastMode) { - this.fastMode = fastMode; + this.sideEffectSet = fastMode ? SideEffectSet.none() : SideEffectSet.defaults(); } /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java index 91e6c76b4..923add56e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java @@ -49,10 +49,13 @@ import java.util.ArrayList; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.formatting.component.PaginationBox; +import com.sk89q.worldedit.util.formatting.component.SideEffectBox; +import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.format.TextColor; -import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.item.ItemType; import java.io.FileNotFoundException; @@ -142,24 +145,62 @@ public class GeneralCommands { @Command( name = "/fast", - desc = "Toggle fast mode" + desc = "Toggle fast mode side effects" ) @CommandPermissions("worldedit.fast") public void fast(Actor actor, LocalSession session, - @Arg(desc = "The new fast mode state", def = "") - Boolean fastMode) { - boolean hasFastMode = session.hasFastMode(); - if (fastMode != null && fastMode == hasFastMode) { - actor.printError(TranslatableComponent.of(fastMode ? "worldedit.fast.enabled.already" : "worldedit.fast.disabled.already")); - return; + @Arg(desc = "The side effect", def = "") + SideEffect sideEffect, + @Arg(desc = "The new side effect state", def = "") + SideEffect.State newState, + @Switch(name = 'h', desc = "Show the info box") + boolean showInfoBox) throws WorldEditException { + if (sideEffect != null) { + SideEffect.State currentState = session.getSideEffectSet().getState(sideEffect); + if (newState != null && newState == currentState) { + if (!showInfoBox) { + actor.printError(TranslatableComponent.of( + "worldedit.fast.sideeffect.already-set", + TranslatableComponent.of(sideEffect.getDisplayName()), + TranslatableComponent.of(newState.getDisplayName()) + )); + } + return; + } + + if (newState != null) { + session.setSideEffectSet(session.getSideEffectSet().with(sideEffect, newState)); + if (!showInfoBox) { + actor.printInfo(TranslatableComponent.of( + "worldedit.fast.sideeffect.set", + TranslatableComponent.of(sideEffect.getDisplayName()), + TranslatableComponent.of(newState.getDisplayName()) + )); + } + } else { + actor.printInfo(TranslatableComponent.of( + "worldedit.fast.sideeffect.get", + TranslatableComponent.of(sideEffect.getDisplayName()), + TranslatableComponent.of(currentState.getDisplayName()) + )); + } + } else if (newState != null) { + SideEffectSet applier = session.getSideEffectSet(); + for (SideEffect sideEffectEntry : SideEffect.values()) { + applier = applier.with(sideEffectEntry, newState); + } + session.setSideEffectSet(applier); + if (!showInfoBox) { + actor.printInfo(TranslatableComponent.of( + "worldedit.fast.sideeffect.set-all", + TranslatableComponent.of(newState.getDisplayName()) + )); + } } - if (hasFastMode) { - session.setFastMode(false); - actor.printInfo(TranslatableComponent.of("worldedit.fast.disabled")); - } else { - session.setFastMode(true); - actor.printInfo(TranslatableComponent.of("worldedit.fast.enabled")); + if (sideEffect == null || showInfoBox) { + SideEffectBox sideEffectBox = new SideEffectBox(session.getSideEffectSet()); + actor.print(sideEffectBox.create(1)); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java index 7dc2192c7..f0f8e8ddd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java @@ -23,6 +23,7 @@ import com.boydti.fawe.object.brush.scroll.Scroll; import com.google.common.collect.ImmutableSet; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.command.util.HookMode; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.TreeGenerator; import org.enginehub.piston.CommandManager; import org.enginehub.piston.converter.ArgumentConverter; @@ -30,6 +31,7 @@ import org.enginehub.piston.converter.MultiKeyConverter; import org.enginehub.piston.inject.Key; import javax.annotation.Nullable; + import java.util.EnumSet; import java.util.Locale; import java.util.Set; @@ -48,6 +50,11 @@ public final class EnumConverter { full(EditSession.ReorderMode.class, r -> ImmutableSet.of(r.getDisplayName()), null)); + commandManager.registerConverter(Key.of(SideEffect.State.class), + MultiKeyConverter.from( + EnumSet.of(SideEffect.State.OFF, SideEffect.State.ON), + r -> ImmutableSet.of(r.name().toLowerCase(Locale.US)), + null)); commandManager.registerConverter(Key.of(HookMode.class), basic(HookMode.class)); commandManager.registerConverter(Key.of(Scroll.Action.class), diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/SideEffectConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/SideEffectConverter.java new file mode 100644 index 000000000..fd0ab684f --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/SideEffectConverter.java @@ -0,0 +1,75 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix; + +import com.google.common.collect.ImmutableList; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.EntityRemover; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.converter.ArgumentConverter; +import org.enginehub.piston.converter.ConversionResult; +import org.enginehub.piston.converter.FailedConversion; +import org.enginehub.piston.converter.SuccessfulConversion; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.Key; + +import java.util.Collection; +import java.util.List; +import java.util.Locale; + +public class SideEffectConverter implements ArgumentConverter { + + public static void register(CommandManager commandManager) { + commandManager.registerConverter(Key.of(SideEffect.class), new SideEffectConverter()); + } + + private final TextComponent choices = TextComponent.of("any side effect"); + + private SideEffectConverter() { + } + + private Collection getSideEffects() { + return WorldEdit.getInstance().getPlatformManager().getSupportedSideEffects(); + } + + @Override + public Component describeAcceptableArguments() { + return choices; + } + + @Override + public List getSuggestions(String input, InjectedValueAccess context) { + return limitByPrefix(getSideEffects().stream().map(sideEffect -> sideEffect.name().toLowerCase(Locale.US)), input); + } + + @Override + public ConversionResult convert(String argument, InjectedValueAccess context) { + try { + return SuccessfulConversion.fromSingle(SideEffect.valueOf(argument.toUpperCase(Locale.US))); + } catch (Exception e) { + return FailedConversion.from(e); + } + } +} 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/extension/platform/Platform.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java index d82ed52c1..1f92543aa 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java @@ -21,14 +21,17 @@ package com.sk89q.worldedit.extension.platform; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.registry.Registries; import org.enginehub.piston.CommandManager; import javax.annotation.Nullable; + import java.util.List; import java.util.Map; +import java.util.Set; /** * Represents a platform that WorldEdit has been implemented for. @@ -174,4 +177,5 @@ public interface Platform { */ Map getCapabilities(); + Set getSupportedSideEffects(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java index 305c7f540..2b533f170 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java @@ -94,6 +94,7 @@ import com.sk89q.worldedit.command.argument.ExpressionConverter; import com.sk89q.worldedit.command.argument.FactoryConverter; import com.sk89q.worldedit.command.argument.RegionFactoryConverter; import com.sk89q.worldedit.command.argument.RegistryConverter; +import com.sk89q.worldedit.command.argument.SideEffectConverter; import com.sk89q.worldedit.command.argument.VectorConverter; import com.sk89q.worldedit.command.argument.WorldConverter; import com.sk89q.worldedit.command.argument.ZonedDateTimeConverter; @@ -255,6 +256,7 @@ public final class PlatformCommandManager { RegionFactoryConverter.register(commandManager); WorldConverter.register(commandManager); ExpressionConverter.register(commandManager); + SideEffectConverter.register(commandManager); registerBindings(new ConsumeBindings(worldEdit, this)); registerBindings(new PrimitiveBindings(worldEdit)); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 9422a9abf..6ce088ac3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -48,9 +48,11 @@ import com.sk89q.worldedit.event.platform.PlayerInputEvent; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.eventbus.Subscribe; import com.sk89q.worldedit.world.World; import java.util.ArrayList; +import java.util.Collection; import java.util.EnumMap; import java.util.Iterator; import java.util.List; @@ -304,6 +306,10 @@ public class PlatformManager { return queryCapability(Capability.CONFIGURATION).getConfiguration(); } + public Collection getSupportedSideEffects() { + return queryCapability(Capability.WORLD_EDITING).getSupportedSideEffects(); + } + @Subscribe public void handlePlatformReady(PlatformReadyEvent event) { choosePreferred(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ExtentBuffer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ExtentBuffer.java index 4ae9a3ad5..699b73d43 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ExtentBuffer.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ExtentBuffer.java @@ -21,17 +21,16 @@ package com.sk89q.worldedit.extent.buffer; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.collect.Maps; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.AbstractBufferingExtent; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.collection.BlockMap; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; import java.util.Map; -import java.util.Optional; /** * Buffers changes to an {@link Extent} and allows retrieval of the changed blocks, @@ -39,7 +38,7 @@ import java.util.Optional; */ public class ExtentBuffer extends AbstractBufferingExtent { - private final Map buffer = Maps.newHashMap(); + private final Map buffer = BlockMap.create(); private final Mask mask; /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java index 60cc68cf7..8b34e4778 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.extent.reorder; -import com.google.common.collect.ImmutableSortedSet; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.AbstractBufferingExtent; import com.sk89q.worldedit.extent.Extent; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/FastModeExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SideEffectExtent.java similarity index 63% rename from worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/FastModeExtent.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SideEffectExtent.java index a29eff306..1a6b78505 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/FastModeExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SideEffectExtent.java @@ -27,67 +27,38 @@ import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.RunContext; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.util.collection.BlockMap; import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockTypes; import java.util.HashSet; import java.util.Iterator; -import java.util.List; +import java.util.Map; import java.util.Set; /** - * Implements "fast mode" which may skip physics, lighting, etc. + * An extent that sets blocks in the world, with a {@link SideEffectSet}. */ -public class FastModeExtent extends AbstractDelegateExtent { +public class SideEffectExtent extends AbstractDelegateExtent { private final World world; - private final Set positions = new HashSet<>(); + private final Map positions = BlockMap.create(); private final Set dirtyChunks = new HashSet<>(); - private boolean enabled = true; + private SideEffectSet sideEffectSet = SideEffectSet.defaults(); private boolean postEditSimulation; - /** - * Create a new instance with fast mode enabled. - * - * @param world the world - */ - public FastModeExtent(World world) { - this(world, true); - } - /** * Create a new instance. * * @param world the world - * @param enabled true to enable fast mode */ - public FastModeExtent(World world, boolean enabled) { + public SideEffectExtent(World world) { super(world); checkNotNull(world); this.world = world; - this.enabled = enabled; - if (enabled) { - this.postEditSimulation = true; - } - } - - /** - * Return whether fast mode is enabled. - * - * @return true if fast mode is enabled - */ - public boolean isEnabled() { - return enabled; - } - - /** - * Set fast mode enable status. - * - * @param enabled true to enable fast mode - */ - public void setEnabled(boolean enabled) { - this.enabled = enabled; } public boolean isPostEditSimulationEnabled() { @@ -98,26 +69,28 @@ public class FastModeExtent extends AbstractDelegateExtent { this.postEditSimulation = enabled; } + public SideEffectSet getSideEffectSet() { + return this.sideEffectSet; + } + + public void setSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + @Override public > boolean setBlock(BlockVector3 location, B block) throws WorldEditException { - if (enabled || postEditSimulation) { + if (sideEffectSet.getState(SideEffect.LIGHTING) == SideEffect.State.DELAYED) { dirtyChunks.add(BlockVector2.at(location.getBlockX() >> 4, location.getBlockZ() >> 4)); - - if (world.setBlock(location, block, false)) { - if (!enabled && postEditSimulation) { - positions.add(location); - } - return true; - } - - return false; - } else { - return world.setBlock(location, block, true); } + if (postEditSimulation) { + positions.put(location, world.getBlock(location)); + } + + return world.setBlock(location, block, postEditSimulation ? SideEffectSet.none() : sideEffectSet); } public boolean commitRequired() { - return enabled || postEditSimulation; + return postEditSimulation || !dirtyChunks.isEmpty(); } @Override @@ -132,11 +105,11 @@ public class FastModeExtent extends AbstractDelegateExtent { world.fixAfterFastMode(dirtyChunks); } - if (!enabled && postEditSimulation) { - Iterator positionIterator = positions.iterator(); + if (postEditSimulation) { + Iterator> positionIterator = positions.entrySet().iterator(); while (run.shouldContinue() && positionIterator.hasNext()) { - BlockVector3 position = positionIterator.next(); - world.notifyAndLightBlock(position, BlockTypes.AIR.getDefaultState()); + Map.Entry position = positionIterator.next(); + world.applySideEffects(position.getKey(), position.getValue(), sideEffectSet); positionIterator.remove(); } @@ -151,5 +124,4 @@ public class FastModeExtent extends AbstractDelegateExtent { } }; } - } 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. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java new file mode 100644 index 000000000..ac32171e7 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java @@ -0,0 +1,184 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.wna; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Natively access and perform operations on the world. + * + * @param the native chunk type + * @param the native block state type + * @param the native position type + */ +public interface WorldNativeAccess { + + default > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + checkNotNull(position); + checkNotNull(block); + setCurrentSideEffectSet(sideEffects); + + int x = position.getBlockX(); + int y = position.getBlockY(); + int z = position.getBlockZ(); + + // First set the block + NC chunk = getChunk(x >> 4, z >> 4); + NP pos = getPosition(x, y, z); + NBS old = getBlockState(chunk, pos); + NBS newState = toNative(block.toImmutableState()); + // change block prior to placing if it should be fixed + if (sideEffects.shouldApply(SideEffect.VALIDATION)) { + newState = getValidBlockForPosition(newState, pos); + } + NBS successState = setBlockState(chunk, pos, newState); + boolean successful = successState != null; + + // Create the TileEntity + if (successful || old == newState) { + if (block instanceof BaseBlock) { + BaseBlock baseBlock = (BaseBlock) block; + CompoundTag tag = baseBlock.getNbtData(); + if (tag != null) { + tag = tag.createBuilder() + .putString("id", baseBlock.getNbtId()) + .putInt("x", position.getX()) + .putInt("y", position.getY()) + .putInt("z", position.getZ()) + .build(); + // update if TE changed as well + successful = updateTileEntity(pos, tag); + } + } + } + + if (successful) { + if (sideEffects.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { + updateLightingForBlock(pos); + } + markAndNotifyBlock(pos, chunk, old, newState, sideEffects); + } + + return successful; + } + + default void applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) { + setCurrentSideEffectSet(sideEffectSet); + NP pos = getPosition(position.getX(), position.getY(), position.getZ()); + NC chunk = getChunk(position.getX() >> 4, position.getZ() >> 4); + NBS oldData = toNative(previousType); + NBS newData = getBlockState(chunk, pos); + + if (sideEffectSet.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { + updateLightingForBlock(pos); + } + + markAndNotifyBlock(pos, chunk, oldData, newData, sideEffectSet); + } + + // state-keeping functions for WNA + // may be thread-unsafe, as this is single-threaded code + + /** + * Receive the current side-effect set from the high level call. + * + * This allows the implementation to branch on the side-effects internally. + * + * @param sideEffectSet the set of side-effects + */ + default void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { + } + + // access functions + + NC getChunk(int x, int z); + + NBS toNative(BlockState state); + + NBS getBlockState(NC chunk, NP position); + + @Nullable + NBS setBlockState(NC chunk, NP position, NBS state); + + NBS getValidBlockForPosition(NBS block, NP position); + + NP getPosition(int x, int y, int z); + + void updateLightingForBlock(NP position); + + boolean updateTileEntity(NP position, CompoundTag tag); + + void notifyBlockUpdate(NP position, NBS oldState, NBS newState); + + boolean isChunkTicking(NC chunk); + + void markBlockChanged(NP position); + + void notifyNeighbors(NP pos, NBS oldState, NBS newState); + + void updateNeighbors(NP pos, NBS oldState, NBS newState, int recursionLimit); + + void onBlockStateChange(NP pos, NBS oldState, NBS newState); + + /** + * This is a heavily modified function stripped from MC to apply worldedit-modifications. + * + * See Forge's World.markAndNotifyBlock + */ + default void markAndNotifyBlock(NP pos, NC chunk, NBS oldState, NBS newState, SideEffectSet sideEffectSet) { + NBS blockState1 = getBlockState(chunk, pos); + if (blockState1 != newState) { + return; + } + + // Remove redundant branches + if (isChunkTicking(chunk)) { + if (sideEffectSet.shouldApply(SideEffect.ENTITY_AI)) { + notifyBlockUpdate(pos, oldState, newState); + } else { + // If we want to skip entity AI, just mark the block for sending + markBlockChanged(pos); + } + } + + if (sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) { + notifyNeighbors(pos, oldState, newState); + } + + // Make connection updates optional + if (sideEffectSet.shouldApply(SideEffect.VALIDATION)) { + updateNeighbors(pos, oldState, newState, 512); + } + + onBlockStateChange(pos, oldState, newState); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/package-info.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/package-info.java new file mode 100644 index 000000000..69009eb59 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/package-info.java @@ -0,0 +1,25 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +/** + * "WNA", or WorldEdit Native Access. + * + * Contains internal helper functions for sharing code between platforms. + */ +package com.sk89q.worldedit.internal.wna; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.java new file mode 100644 index 000000000..5b142a364 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.java @@ -0,0 +1,68 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util; + +import java.util.Locale; + +public enum SideEffect { + LIGHTING(State.ON), + NEIGHBORS(State.ON), + VALIDATION(State.ON), + ENTITY_AI(State.OFF), + EVENTS(State.OFF); + + private final String displayName; + private final String description; + private final State defaultValue; + + SideEffect(State defaultValue) { + this.displayName = "worldedit.sideeffect." + this.name().toLowerCase(Locale.US); + this.description = "worldedit.sideeffect." + this.name().toLowerCase(Locale.US) + ".description"; + this.defaultValue = defaultValue; + } + + public String getDisplayName() { + return this.displayName; + } + + public String getDescription() { + return this.description; + } + + public State getDefaultValue() { + return this.defaultValue; + } + + public enum State { + OFF, + ON, + DELAYED; + + private final String displayName; + + State() { + this.displayName = "worldedit.sideeffect.state." + this.name().toLowerCase(Locale.US); + } + + public String getDisplayName() { + return this.displayName; + } + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffectSet.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffectSet.java new file mode 100644 index 000000000..d88b37097 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffectSet.java @@ -0,0 +1,94 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +import java.util.Arrays; +import java.util.EnumMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class SideEffectSet { + private static final SideEffectSet DEFAULT = new SideEffectSet( + Arrays.stream(SideEffect.values()).collect(Collectors.toMap(Function.identity(), SideEffect::getDefaultValue)) + ); + private static final SideEffectSet NONE = new SideEffectSet(); + + private final Map sideEffects; + private final Set appliedSideEffects; + private final boolean appliesAny; + + private SideEffectSet() { + this(ImmutableMap.of()); + } + + public SideEffectSet(Map sideEffects) { + this.sideEffects = Maps.immutableEnumMap(sideEffects); + + appliedSideEffects = sideEffects.entrySet() + .stream() + .filter(entry -> entry.getValue() != SideEffect.State.OFF) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + appliesAny = !appliedSideEffects.isEmpty(); + } + + public SideEffectSet with(SideEffect sideEffect, SideEffect.State state) { + Map entries = this.sideEffects.isEmpty() ? Maps.newEnumMap(SideEffect.class) : new EnumMap<>(this.sideEffects); + entries.put(sideEffect, state); + return new SideEffectSet(entries); + } + + public boolean doesApplyAny() { + return this.appliesAny; + } + + public SideEffect.State getState(SideEffect effect) { + return sideEffects.getOrDefault(effect, effect.getDefaultValue()); + } + + /** + * Gets whether this side effect is not off. + * + * This returns whether it is either delayed or on. + * + * @param effect The side effect + * @return Whether it should apply + */ + public boolean shouldApply(SideEffect effect) { + return getState(effect) != SideEffect.State.OFF; + } + + public Set getSideEffectsToApply() { + return this.appliedSideEffects; + } + + public static SideEffectSet defaults() { + return DEFAULT; + } + + public static SideEffectSet none() { + return NONE; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/SideEffectBox.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/SideEffectBox.java new file mode 100644 index 000000000..14403313e --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/SideEffectBox.java @@ -0,0 +1,88 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.formatting.component; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; +import com.sk89q.worldedit.util.formatting.text.event.HoverEvent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; + +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +public class SideEffectBox extends PaginationBox { + + private static List sideEffects; + + private SideEffectSet sideEffectSet; + + private static List getSideEffects() { + if (sideEffects == null) { + sideEffects = WorldEdit.getInstance().getPlatformManager().getSupportedSideEffects() + .stream() + .sorted(Comparator.comparing(Enum::name)) + .collect(Collectors.toList()); + } + + return sideEffects; + } + + public SideEffectBox(SideEffectSet sideEffectSet) { + super("Side Effects"); + + this.sideEffectSet = sideEffectSet; + } + + private static final SideEffect.State[] SHOWN_VALUES = {SideEffect.State.OFF, SideEffect.State.ON}; + + @Override + public Component getComponent(int number) { + SideEffect effect = getSideEffects().get(number); + SideEffect.State state = this.sideEffectSet.getState(effect); + + TextComponent.Builder builder = TextComponent.builder(); + builder = builder.append(TranslatableComponent.of(effect.getDisplayName(), TextColor.YELLOW) + .hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TranslatableComponent.of(effect.getDescription())))); + for (SideEffect.State uiState : SHOWN_VALUES) { + builder = builder.append(TextComponent.space()); + builder = builder.append(TranslatableComponent.of(uiState.getDisplayName(), uiState == state ? TextColor.WHITE : TextColor.GRAY) + .clickEvent(ClickEvent.runCommand("//fast -h " + effect.name().toLowerCase(Locale.US) + " " + uiState.name().toLowerCase(Locale.US))) + .hoverEvent(HoverEvent.showText(uiState == state + ? TranslatableComponent.of("worldedit.sideeffect.box.current") + : TranslatableComponent.of("worldedit.sideeffect.box.change-to", TranslatableComponent.of(uiState.getDisplayName())) + )) + ); + } + + return builder.build(); + } + + @Override + public int getComponentsSize() { + return getSideEffects().size(); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java index c333f1269..34e7179b0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java @@ -32,6 +32,7 @@ import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.weather.WeatherType; import com.sk89q.worldedit.world.block.BlockType; @@ -57,7 +58,7 @@ public abstract class AbstractWorld implements World { @Override public final > boolean setBlock(BlockVector3 pt, B block) throws WorldEditException { - return setBlock(pt, block, true); + return setBlock(pt, block, SideEffectSet.defaults()); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java index 446e564af..81e3c9d7a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java @@ -23,6 +23,7 @@ import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.beta.implementation.blocks.NullChunkGet; import com.sk89q.jnbt.CompoundTag; +import com.google.common.collect.ImmutableSet; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; @@ -35,6 +36,8 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; @@ -48,6 +51,7 @@ import com.sk89q.worldedit.world.weather.WeatherTypes; import javax.annotation.Nullable; import java.util.Collections; import java.util.List; +import java.util.Set; /** * A null implementation of {@link World} that drops all changes and @@ -71,13 +75,14 @@ public class NullWorld extends AbstractWorld { } @Override - public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException { + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { return false; } @Override - public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException { - return false; + public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) + throws WorldEditException { + return ImmutableSet.of(); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java index 5c6ba7df3..9388dcace 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java @@ -37,15 +37,19 @@ import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.Keyed; import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.weather.WeatherType; -import javax.annotation.Nullable; import java.nio.file.Path; import java.util.Locale; +import java.util.Set; + +import javax.annotation.Nullable; /** * Represents a world (dimension). @@ -112,10 +116,28 @@ public interface World extends Extent, Keyed, IChunkCache { * @param notifyAndLight true to to notify and light * @return true if the block was successfully set (return value may not be accurate) */ + @Deprecated default > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException { - return setBlock(position, block); + return setBlock(position, block, notifyAndLight ? SideEffectSet.defaults() : SideEffectSet.none()); } + /** + * Similar to {@link Extent#setBlock(BlockVector3, BlockStateHolder)} but a + * {@code sideEffects} parameter indicates which side effects should be applied + * to the block. This includes block updates, lighting, and others. See {@link SideEffect} + * for a full list. + * + *

Not all implementations support all side effects. Use + * {@link Platform#getSupportedSideEffects()} for a list of supported side effects. + * Non-supported side effects will be ignored.

+ * + * @param position position of the block + * @param block block to set + * @param sideEffects which side effects to perform + * @return true if the block was successfully set (return value may not be accurate) + */ + > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException; + /** * Notifies the simulation that the block at the given location has * been changed and it must be re-lighted (and issue other events). @@ -124,7 +146,20 @@ public interface World extends Extent, Keyed, IChunkCache { * @param previousType the type of the previous block that was there * @return true if the block was successfully notified */ - boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException; + @Deprecated + default boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException { + return !applySideEffects(position, previousType, SideEffectSet.defaults()).isEmpty(); + } + + /** + * Applies a set of side effects on the given block. + * + * @param position position of the block + * @param previousType the type of the previous block that was there + * @param sideEffectSet which side effects to perform + * @return a set of side effects that were applied + */ + Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException; /** * Get the light level at the given block. diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index fe79f060d..016a3d60d 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -210,6 +210,10 @@ "worldedit.fast.enabled": "Fast mode enabled. Lighting in the affected chunks may be wrong and/or you may need to rejoin to see changes.", "worldedit.fast.disabled.already": "Fast mode already disabled.", "worldedit.fast.enabled.already": "Fast mode already enabled.", + "worldedit.fast.sideeffect.set": "Side effect \"{0}\" set to {1}", + "worldedit.fast.sideeffect.get": "Side effect \"{0}\" is set to {1}", + "worldedit.fast.sideeffect.already-set": "Side effect \"{0}\" is already {1}", + "worldedit.fast.sideeffect.set-all": "All side effects set to {0}", "worldedit.reorder.current": "The reorder mode is {0}", "worldedit.reorder.set": "The reorder mode is now {0}", "worldedit.gmask.disabled": "Global mask disabled.", @@ -494,6 +498,22 @@ "worldedit.selection.sphere.explain.secondary": "Radius set to {0}.", "worldedit.selection.sphere.explain.secondary-defined": "Radius set to {0} ({1}).", + "worldedit.sideeffect.lighting": "Lighting", + "worldedit.sideeffect.lighting.description": "Updates block lighting", + "worldedit.sideeffect.neighbors": "Neighbors", + "worldedit.sideeffect.neighbors.description": "Notifies nearby blocks of changes", + "worldedit.sideeffect.validation": "Validation", + "worldedit.sideeffect.validation.description": "Validates and fixes inconsistent world state, such as disconnected blocks", + "worldedit.sideeffect.entity_ai": "Entity AI", + "worldedit.sideeffect.entity_ai.description": "Updates Entity AI paths for the block changes", + "worldedit.sideeffect.events": "Mod/Plugin Events", + "worldedit.sideeffect.events.description": "Tells other mods/plugins about these changes when applicable", + "worldedit.sideeffect.state.on": "On", + "worldedit.sideeffect.state.delayed": "Delayed", + "worldedit.sideeffect.state.off": "Off", + "worldedit.sideeffect.box.current": "Current", + "worldedit.sideeffect.box.change-to": "Click to set to {0}", + "worldedit.help.command-not-found": "The command '{0}' could not be found.", "worldedit.help.no-subcommands": "'{0}' has no sub-commands. (Maybe '{1}' is for a parameter?)", "worldedit.help.subcommand-not-found": "The sub-command '{0}' under '{1}' could not be found.", diff --git a/worldedit-fabric/build.gradle.kts b/worldedit-fabric/build.gradle.kts index be85099fa..4b7a7b079 100644 --- a/worldedit-fabric/build.gradle.kts +++ b/worldedit-fabric/build.gradle.kts @@ -7,8 +7,8 @@ applyShadowConfiguration() apply(plugin = "fabric-loom") val minecraftVersion = "1.15.2" -val yarnMappings = "1.15.2+build.8:v2" -val loaderVersion = "0.7.6+build.180" +val yarnMappings = "1.15.2+build.14:v2" +val loaderVersion = "0.7.8+build.189" configurations.all { resolutionStrategy { @@ -61,16 +61,12 @@ tasks.named("processResources") { } } -<<<<<<< HEAD -addJarManifest(includeClasspath = true) -======= tasks.named("jar") { manifest { attributes("Class-Path" to CLASSPATH, "WorldEdit-Version" to project.version) } } ->>>>>>> 18a55bc14... Add new experimental snapshot API (#524) tasks.named("shadowJar") { archiveClassifier.set("dist-dev") diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java index 317274331..33219c7a3 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java @@ -25,6 +25,7 @@ import com.google.common.collect.ImmutableList; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.registry.state.BooleanProperty; diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricDataFixer.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricDataFixer.java index f307b789c..d82bb8f75 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricDataFixer.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricDataFixer.java @@ -36,9 +36,10 @@ import com.mojang.datafixers.DataFixerBuilder; import com.mojang.datafixers.Dynamic; import com.mojang.datafixers.schemas.Schema; import com.sk89q.jnbt.CompoundTag; -import net.minecraft.datafixers.NbtOps; -import net.minecraft.datafixers.Schemas; -import net.minecraft.datafixers.TypeReferences; +import com.sk89q.worldedit.fabric.internal.NBTConverter; +import net.minecraft.datafixer.NbtOps; +import net.minecraft.datafixer.Schemas; +import net.minecraft.datafixer.TypeReferences; import net.minecraft.nbt.FloatTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.StringTag; diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java index d1fb75a04..4ea3969fd 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.metadata.EntityProperties; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.NullWorld; diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java index beb6d5208..bef543f6e 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.fabric; +import com.google.common.collect.Sets; import com.sk89q.worldedit.command.util.PermissionCondition; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.AbstractPlatform; @@ -27,13 +28,13 @@ import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; import com.sk89q.worldedit.extension.platform.Watchdog; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.registry.Registries; import net.minecraft.SharedConstants; import net.minecraft.server.MinecraftServer; import net.minecraft.server.PlayerManager; -import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.MinecraftDedicatedServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; @@ -200,6 +201,18 @@ class FabricPlatform extends AbstractPlatform implements MultiUserPlatform { return capabilities; } + private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( + SideEffect.VALIDATION, + SideEffect.ENTITY_AI, + SideEffect.LIGHTING, + SideEffect.NEIGHBORS + ); + + @Override + public Set getSupportedSideEffects() { + return SUPPORTED_SIDE_EFFECTS; + } + @Override public Collection getConnectedUsers() { List users = new ArrayList<>(); diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlayer.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlayer.java index 48507f8fe..1ee00d311 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlayer.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlayer.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extension.platform.AbstractPlayerActor; import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.fabric.mixin.AccessorServerPlayerEntity; import com.sk89q.worldedit.fabric.net.handler.WECUIPacketHandler; import com.sk89q.worldedit.internal.cui.CUIEvent; @@ -42,10 +43,10 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; import io.netty.buffer.Unpooled; import net.minecraft.block.Block; -import net.minecraft.client.network.packet.BlockEntityUpdateS2CPacket; -import net.minecraft.client.network.packet.BlockUpdateS2CPacket; -import net.minecraft.client.network.packet.CustomPayloadS2CPacket; import net.minecraft.item.ItemStack; +import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket; +import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket; +import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.LiteralText; import net.minecraft.text.Text; diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java index 886523df5..93e05a820 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java @@ -24,6 +24,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.common.collect.Sets; import com.google.common.io.Files; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; @@ -33,6 +34,8 @@ import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.fabric.internal.FabricWorldNativeAccess; +import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; import com.sk89q.worldedit.math.BlockVector2; @@ -42,6 +45,8 @@ import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.biome.BiomeType; @@ -71,6 +76,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.world.World; import net.minecraft.world.WorldSaveHandler; +import net.minecraft.world.biome.DefaultBiomeFeatures; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.ChunkManager; import net.minecraft.world.chunk.ChunkStatus; @@ -104,6 +110,7 @@ import java.util.Locale; import java.util.Optional; import java.util.OptionalInt; import java.util.Random; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; @@ -123,6 +130,7 @@ public class FabricWorld extends AbstractWorld { private static final net.minecraft.block.BlockState JUNGLE_SHRUB = Blocks.OAK_LEAVES.getDefaultState().with(LeavesBlock.PERSISTENT, Boolean.TRUE); private final WeakReference worldRef; + private final FabricWorldNativeAccess worldNativeAccess; /** * Construct a new world. @@ -132,6 +140,7 @@ public class FabricWorld extends AbstractWorld { FabricWorld(World world) { checkNotNull(world); this.worldRef = new WeakReference<>(world); + this.worldNativeAccess = new FabricWorldNativeAccess(worldRef); } /** @@ -186,61 +195,14 @@ public class FabricWorld extends AbstractWorld { } @Override - public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException { - checkNotNull(position); - checkNotNull(block); - - World world = getWorldChecked(); - int x = position.getBlockX(); - int y = position.getBlockY(); - int z = position.getBlockZ(); - - // First set the block - Chunk chunk = world.getChunk(x >> 4, z >> 4); - BlockPos pos = new BlockPos(x, y, z); - net.minecraft.block.BlockState old = chunk.getBlockState(pos); - OptionalInt stateId = BlockStateIdAccess.getBlockStateId(block.toImmutableState()); - net.minecraft.block.BlockState newState = stateId.isPresent() ? Block.getStateFromRawId(stateId.getAsInt()) : FabricAdapter.adapt(block.toImmutableState()); - net.minecraft.block.BlockState successState = chunk.setBlockState(pos, newState, false); - boolean successful = successState != null; - - // Create the TileEntity - if (successful || old == newState) { - if (block instanceof BaseBlock) { - CompoundTag tag = ((BaseBlock) block).getNbtData(); - if (tag != null) { - net.minecraft.nbt.CompoundTag nativeTag = NBTConverter.toNative(tag); - BlockEntity tileEntity = getWorld().getWorldChunk(pos).getBlockEntity(pos); - if (tileEntity != null) { - tileEntity.fromTag(nativeTag); - tileEntity.setPos(pos); - tileEntity.setWorld(world); - successful = true; // update if TE changed as well - } - } - } - } - - if (successful && notifyAndLight) { - world.getChunkManager().getLightingProvider().enqueueLightUpdate(pos); - world.scheduleBlockRender(pos, old, newState); - world.updateListeners(pos, old, newState, UPDATE | NOTIFY); - world.updateNeighbors(pos, newState.getBlock()); - if (old.hasComparatorOutput()) { - world.updateHorizontalAdjacent(pos, newState.getBlock()); - } - } - - return successful; + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + return worldNativeAccess.setBlock(position, block, sideEffects); } @Override - public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException { - BlockPos pos = new BlockPos(position.getX(), position.getY(), position.getZ()); - net.minecraft.block.BlockState state = getWorld().getBlockState(pos); - getWorld().updateListeners(pos, FabricAdapter.adapt(previousType), state, 1 | 2); - getWorld().updateNeighbors(pos, state.getBlock()); - return true; + public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException { + worldNativeAccess.applySideEffects(position, previousType, sideEffectSet); + return Sets.intersection(FabricWorldEdit.inst.getPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply()); } @Override diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java new file mode 100644 index 000000000..6d64e39de --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java @@ -0,0 +1,139 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric.internal; + +import com.sk89q.worldedit.fabric.FabricAdapter; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.world.ChunkHolder; +import net.minecraft.server.world.ServerChunkManager; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.chunk.WorldChunk; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.util.Objects; + +public class FabricWorldNativeAccess implements WorldNativeAccess { + private static final int UPDATE = 1, NOTIFY = 2; + + private final WeakReference world; + + public FabricWorldNativeAccess(WeakReference world) { + this.world = world; + } + + private World getWorld() { + return Objects.requireNonNull(world.get(), "The reference to the world was lost"); + } + + @Override + public WorldChunk getChunk(int x, int z) { + return getWorld().getChunk(x, z); + } + + @Override + public BlockState toNative(com.sk89q.worldedit.world.block.BlockState state) { + int stateId = BlockStateIdAccess.getBlockStateId(state); + return BlockStateIdAccess.isValidInternalId(stateId) + ? Block.getStateFromRawId(stateId) + : FabricAdapter.adapt(state); + } + + @Override + public BlockState getBlockState(WorldChunk chunk, BlockPos position) { + return chunk.getBlockState(position); + } + + @Nullable + @Override + public BlockState setBlockState(WorldChunk chunk, BlockPos position, BlockState state) { + return chunk.setBlockState(position, state, false); + } + + @Override + public BlockState getValidBlockForPosition(BlockState block, BlockPos position) { + return Block.getRenderingState(block, getWorld(), position); + } + + @Override + public BlockPos getPosition(int x, int y, int z) { + return new BlockPos(x, y, z); + } + + @Override + public void updateLightingForBlock(BlockPos position) { + getWorld().getChunkManager().getLightingProvider().checkBlock(position); + } + + @Override + public boolean updateTileEntity(BlockPos position, com.sk89q.jnbt.CompoundTag tag) { + CompoundTag nativeTag = NBTConverter.toNative(tag); + BlockEntity tileEntity = getWorld().getWorldChunk(position).getBlockEntity(position); + if (tileEntity == null) { + return false; + } + tileEntity.setLocation(getWorld(), position); + tileEntity.fromTag(nativeTag); + return true; + } + + @Override + public void notifyBlockUpdate(BlockPos position, BlockState oldState, BlockState newState) { + getWorld().updateListeners(position, oldState, newState, UPDATE | NOTIFY); + } + + @Override + public boolean isChunkTicking(WorldChunk chunk) { + return chunk.getLevelType().isAfter(ChunkHolder.LevelType.TICKING); + } + + @Override + public void markBlockChanged(BlockPos position) { + ((ServerChunkManager) getWorld().getChunkManager()).markForUpdate(position); + } + + @Override + public void notifyNeighbors(BlockPos pos, BlockState oldState, BlockState newState) { + getWorld().updateNeighbors(pos, oldState.getBlock()); + if (newState.hasComparatorOutput()) { + getWorld().updateHorizontalAdjacent(pos, newState.getBlock()); + } + } + + @Override + public void updateNeighbors(BlockPos pos, BlockState oldState, BlockState newState) { + World world = getWorld(); + // method_11637 = updateDiagonalNeighbors + oldState.method_11637(world, pos, NOTIFY); + newState.updateNeighborStates(world, pos, NOTIFY); + newState.method_11637(world, pos, NOTIFY); + } + + @Override + public void onBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) { + getWorld().onBlockChanged(pos, oldState, newState); + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/NBTConverter.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/NBTConverter.java similarity index 99% rename from worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/NBTConverter.java rename to worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/NBTConverter.java index 450dfcb65..2cdc6be4e 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/NBTConverter.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/NBTConverter.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.fabric; +package com.sk89q.worldedit.fabric.internal; import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.ByteTag; @@ -44,7 +44,7 @@ import java.util.Set; /** * Converts between JNBT and Minecraft NBT classes. */ -final class NBTConverter { +public final class NBTConverter { private NBTConverter() { } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/net/handler/WECUIPacketHandler.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/net/handler/WECUIPacketHandler.java index 816d6c76f..4571e50d2 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/net/handler/WECUIPacketHandler.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/net/handler/WECUIPacketHandler.java @@ -23,15 +23,9 @@ import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.fabric.FabricAdapter; import com.sk89q.worldedit.fabric.FabricPlayer; import com.sk89q.worldedit.fabric.FabricWorldEdit; -import net.fabricmc.fabric.api.network.PacketConsumer; -import net.fabricmc.fabric.api.network.PacketContext; import net.fabricmc.fabric.api.network.ServerSidePacketRegistry; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.packet.CustomPayloadS2CPacket; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.network.packet.CustomPayloadC2SPacket; import net.minecraft.util.Identifier; -import net.minecraft.util.PacketByteBuf; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -58,4 +52,4 @@ public final class WECUIPacketHandler { session.describeCUI(actor); }); } -} \ No newline at end of file +} diff --git a/worldedit-forge/build.gradle.kts b/worldedit-forge/build.gradle.kts index 23d52162d..f3b938ca7 100644 --- a/worldedit-forge/build.gradle.kts +++ b/worldedit-forge/build.gradle.kts @@ -77,7 +77,12 @@ tasks.named("processResources") { } } -addJarManifest(includeClasspath = false) +tasks.named("jar") { + manifest { + attributes("Class-Path" to CLASSPATH, + "WorldEdit-Version" to project.version) + } +} tasks.named("shadowJar") { dependencies { diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeEntity.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeEntity.java index a7f99e1a7..d522107d6 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeEntity.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeEntity.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.metadata.EntityProperties; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.forge.internal.NBTConverter; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.NullWorld; diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java index e1af09064..7c682b81d 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java @@ -27,6 +27,7 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.registry.Registries; @@ -206,6 +207,18 @@ class ForgePlatform extends AbstractPlatform implements MultiUserPlatform { return capabilities; } + private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( + SideEffect.VALIDATION, + SideEffect.ENTITY_AI, + SideEffect.LIGHTING, + SideEffect.NEIGHBORS + ); + + @Override + public Set getSupportedSideEffects() { + return SUPPORTED_SIDE_EFFECTS; + } + @Override public Collection getConnectedUsers() { List users = new ArrayList<>(); diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java index f9096578b..4aa3b7a92 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java @@ -32,6 +32,9 @@ import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.forge.internal.ForgeWorldNativeAccess; +import com.sk89q.worldedit.forge.internal.NBTConverter; +import com.sk89q.worldedit.forge.internal.TileEntityUtils; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; import com.sk89q.worldedit.internal.util.BiomeMath; @@ -96,6 +99,7 @@ import net.minecraft.world.gen.feature.SwampTreeFeature; import net.minecraft.world.gen.feature.TallTaigaTreeFeature; import net.minecraft.world.gen.feature.TreeFeature; +import net.minecraft.world.server.ChunkHolder; import net.minecraft.world.server.ServerChunkProvider; import net.minecraft.world.server.ServerWorld; import net.minecraft.world.storage.SaveHandler; @@ -125,13 +129,13 @@ import static com.google.common.base.Preconditions.checkNotNull; public class ForgeWorld extends AbstractWorld { private static final Random random = new Random(); - private static final int UPDATE = 1, NOTIFY = 2; private static final net.minecraft.block.BlockState JUNGLE_LOG = Blocks.JUNGLE_LOG.getDefaultState(); private static final net.minecraft.block.BlockState JUNGLE_LEAF = Blocks.JUNGLE_LEAVES.getDefaultState().with(LeavesBlock.PERSISTENT, Boolean.TRUE); private static final net.minecraft.block.BlockState JUNGLE_SHRUB = Blocks.OAK_LEAVES.getDefaultState().with(LeavesBlock.PERSISTENT, Boolean.TRUE); - + private final WeakReference worldRef; + private final ForgeWorldNativeAccess nativeAccess; /** * Construct a new world. @@ -141,6 +145,7 @@ public class ForgeWorld extends AbstractWorld { ForgeWorld(World world) { checkNotNull(world); this.worldRef = new WeakReference<>(world); + this.nativeAccess = new ForgeWorldNativeAccess(worldRef); } /** @@ -193,50 +198,14 @@ public class ForgeWorld extends AbstractWorld { } @Override - public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException { - checkNotNull(position); - checkNotNull(block); - - World world = getWorldChecked(); - int x = position.getBlockX(); - int y = position.getBlockY(); - int z = position.getBlockZ(); - - // First set the block - Chunk chunk = world.getChunk(x >> 4, z >> 4); - BlockPos pos = new BlockPos(x, y, z); - net.minecraft.block.BlockState old = chunk.getBlockState(pos); - OptionalInt stateId = BlockStateIdAccess.getBlockStateId(block.toImmutableState()); - net.minecraft.block.BlockState newState = stateId.isPresent() ? Block.getStateById(stateId.getAsInt()) : ForgeAdapter.adapt(block.toImmutableState()); - net.minecraft.block.BlockState successState = chunk.setBlockState(pos, newState, false); - boolean successful = successState != null; - - // Create the TileEntity - if (successful || old == newState) { - if (block instanceof BaseBlock) { - CompoundTag tag = ((BaseBlock) block).getNbtData(); - if (tag != null) { - CompoundNBT nativeTag = NBTConverter.toNative(tag); - nativeTag.putString("id", ((BaseBlock) block).getNbtId()); - TileEntityUtils.setTileEntity(world, position, nativeTag); - successful = true; // update if TE changed as well - } - } - } - - if (successful && notifyAndLight) { - world.getChunkProvider().getLightManager().checkBlock(pos); - world.markAndNotifyBlock(pos, chunk, old, newState, UPDATE | NOTIFY); - } - - return successful; + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + return nativeAccess.setBlock(position, block, sideEffects); } @Override - public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException { - BlockPos pos = new BlockPos(position.getX(), position.getY(), position.getZ()); - getWorld().notifyBlockUpdate(pos, ForgeAdapter.adapt(previousType), getWorld().getBlockState(pos), 1 | 2); - return true; + public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException { + nativeAccess.applySideEffects(position, previousType, sideEffectSet); + return Sets.intersection(ForgeWorldEdit.inst.getPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply()); } @Override diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeWorldNativeAccess.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeWorldNativeAccess.java new file mode 100644 index 000000000..33784f1e7 --- /dev/null +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeWorldNativeAccess.java @@ -0,0 +1,152 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.forge.internal; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.forge.ForgeAdapter; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.server.ChunkHolder; +import net.minecraft.world.server.ServerChunkProvider; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.util.Objects; + +public class ForgeWorldNativeAccess implements WorldNativeAccess { + private static final int UPDATE = 1, NOTIFY = 2; + + private final WeakReference world; + private SideEffectSet sideEffectSet; + + public ForgeWorldNativeAccess(WeakReference world) { + this.world = world; + } + + private World getWorld() { + return Objects.requireNonNull(world.get(), "The reference to the world was lost"); + } + + @Override + public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + + @Override + public Chunk getChunk(int x, int z) { + return getWorld().getChunk(x, z); + } + + @Override + public BlockState toNative(com.sk89q.worldedit.world.block.BlockState state) { + int stateId = BlockStateIdAccess.getBlockStateId(state); + return BlockStateIdAccess.isValidInternalId(stateId) + ? Block.getStateById(stateId) + : ForgeAdapter.adapt(state); + } + + @Override + public BlockState getBlockState(Chunk chunk, BlockPos position) { + return chunk.getBlockState(position); + } + + @Nullable + @Override + public BlockState setBlockState(Chunk chunk, BlockPos position, BlockState state) { + return chunk.setBlockState(position, state, false); + } + + @Override + public BlockState getValidBlockForPosition(BlockState block, BlockPos position) { + return Block.getValidBlockForPosition(block, getWorld(), position); + } + + @Override + public BlockPos getPosition(int x, int y, int z) { + return new BlockPos(x, y, z); + } + + @Override + public void updateLightingForBlock(BlockPos position) { + getWorld().getChunkProvider().getLightManager().checkBlock(position); + } + + @Override + public boolean updateTileEntity(BlockPos position, CompoundTag tag) { + CompoundNBT nativeTag = NBTConverter.toNative(tag); + return TileEntityUtils.setTileEntity(getWorld(), position, nativeTag); + } + + @Override + public void notifyBlockUpdate(BlockPos position, BlockState oldState, BlockState newState) { + getWorld().notifyBlockUpdate(position, oldState, newState, UPDATE | NOTIFY); + } + + @Override + public boolean isChunkTicking(Chunk chunk) { + return chunk.getLocationType().isAtLeast(ChunkHolder.LocationType.TICKING); + } + + @Override + public void markBlockChanged(BlockPos position) { + ((ServerChunkProvider) getWorld().getChunkProvider()).markBlockChanged(position); + } + + @Override + public void notifyNeighbors(BlockPos pos, BlockState oldState, BlockState newState) { + World world = getWorld(); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + world.notifyNeighbors(pos, oldState.getBlock()); + } else { + // Manually update each side + Block block = oldState.getBlock(); + world.neighborChanged(pos.west(), block, pos); + world.neighborChanged(pos.east(), block, pos); + world.neighborChanged(pos.down(), block, pos); + world.neighborChanged(pos.up(), block, pos); + world.neighborChanged(pos.north(), block, pos); + world.neighborChanged(pos.south(), block, pos); + } + if (newState.hasComparatorInputOverride()) { + world.updateComparatorOutputLevel(pos, newState.getBlock()); + } + } + + @Override + public void updateNeighbors(BlockPos pos, BlockState oldState, BlockState newState) { + World world = getWorld(); + oldState.updateDiagonalNeighbors(world, pos, NOTIFY); + newState.updateNeighbors(world, pos, NOTIFY); + newState.updateDiagonalNeighbors(world, pos, NOTIFY); + } + + @Override + public void onBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) { + getWorld().onBlockStateChange(pos, oldState, newState); + } +} diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/TileEntityUtils.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/TileEntityUtils.java index 29d2b187e..dc3297c7b 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/TileEntityUtils.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/TileEntityUtils.java @@ -24,13 +24,10 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.worldedit.math.BlockVector3; import net.minecraft.nbt.CompoundNBT; -import net.minecraft.nbt.IntNBT; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -import javax.annotation.Nullable; - /** * Utility methods for setting tile entities in the world. */ @@ -39,39 +36,21 @@ public final class TileEntityUtils { private TileEntityUtils() { } - /** - * Update the given tag compound with position information. - * - * @param tag the tag - * @param position the position - */ - private static void updateForSet(CompoundNBT tag, BlockVector3 position) { - checkNotNull(tag); - checkNotNull(position); - - tag.put("x", new IntNBT(position.getBlockX())); - tag.put("y", new IntNBT(position.getBlockY())); - tag.put("z", new IntNBT(position.getBlockZ())); - } - /** * Set a tile entity at the given location using the tile entity ID from * the tag. * * @param world the world * @param position the position - * @param tag the tag for the tile entity (may be null to do nothing) + * @param tag the tag for the tile entity */ - static boolean setTileEntity(World world, BlockVector3 position, @Nullable CompoundNBT tag) { - if (tag != null) { - updateForSet(tag, position); - TileEntity tileEntity = TileEntity.create(tag); - if (tileEntity == null) { - return false; - } - world.setTileEntity(new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ()), tileEntity); - return true; + static boolean setTileEntity(World world, BlockPos position, CompoundNBT tag) { + TileEntity tileEntity = TileEntity.create(tag); + if (tileEntity == null) { + return false; } + world.setTileEntity(new BlockPos(position.getX(), position.getY(), position.getZ()), tileEntity); + return true; } public static CompoundNBT copyNbtData(TileEntity tile) { diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java index 33849e915..ddfaad17b 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java @@ -20,6 +20,8 @@ package com.sk89q.worldedit.sponge; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.CommandEvent; @@ -31,6 +33,7 @@ import com.sk89q.worldedit.extension.platform.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; import com.sk89q.worldedit.internal.command.CommandUtil; import com.sk89q.worldedit.sponge.config.SpongeConfiguration; +import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.registry.Registries; import org.enginehub.piston.Command; @@ -50,6 +53,7 @@ import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import static java.util.stream.Collectors.toList; @@ -192,6 +196,11 @@ class SpongePlatform extends AbstractPlatform implements MultiUserPlatform { return capabilities; } + @Override + public Set getSupportedSideEffects() { + return ImmutableSet.of(); + } + @Override public Collection getConnectedUsers() { List users = new ArrayList<>();