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/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_16_1/BlockMaterial_1_16_1.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BlockMaterial_1_16_1.java index ef492dbdd..dfa348a78 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BlockMaterial_1_16_1.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BlockMaterial_1_16_1.java @@ -23,7 +23,7 @@ public class BlockMaterial_1_16_1 implements BlockMaterial { this.material = defaultState.getMaterial(); this.craftBlockData = CraftBlockData.fromData(defaultState); this.craftMaterial = craftBlockData.getMaterial(); - this.isTranslucent = !(boolean) ReflectionUtil.getField(Block.class, block, "v"); //TODO Update Mapping for 1.16.1 + this.isTranslucent = !(boolean) ReflectionUtil.getField(Block.class, block, "at"); //TODO Update Mapping for 1.16.1 } public Block getBlock() { 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/wrapper/AsyncWorld.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java index 4252d3f32..60e0f8037 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 @@ -884,6 +884,16 @@ public class AsyncWorld extends PassthroughExtent implements World { parent.setWaterAnimalSpawnLimit(limit); } + @Override + public int getWaterAmbientSpawnLimit() { + return 0; + } + + @Override + public void setWaterAmbientSpawnLimit(int limit) { + + } + @Override public int getAmbientSpawnLimit() { return parent.getAmbientSpawnLimit(); @@ -1285,18 +1295,28 @@ public class AsyncWorld extends PassthroughExtent implements World { } public long getTicksPerWaterSpawns() { - throw new UnsupportedOperationException(); + return parent.getTicksPerWaterSpawns(); } 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); } public long getTicksPerAmbientSpawns() { - throw new UnsupportedOperationException(); + return parent.getTicksPerAmbientSpawns(); } 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..e64db1124 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,15 @@ 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.Map; -import java.util.Objects; + +import java.util.*; + 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 +69,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/BukkitWorld.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index f11fc13d3..588071c34 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,8 +24,9 @@ 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.sk89q.jnbt.CompoundTag; +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; import com.sk89q.worldedit.WorldEditException; @@ -34,6 +35,7 @@ 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; @@ -77,6 +79,7 @@ public class BukkitWorld extends AbstractWorld { } private final WeakReference worldRef; + private final WorldNativeAccess worldNativeAccess; /** * Construct the object. @@ -85,6 +88,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 @@ -464,26 +473,22 @@ public class BukkitWorld extends AbstractWorld { } @Override - public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) 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, sideEffects); + 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), sideEffects.doesApplyAny()); - return true; } - } else { - Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()); - bukkitBlock.setBlockData(BukkitAdapter.adapt(block), sideEffects.doesApplyAny()); - return true; } + Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()); + bukkitBlock.setBlockData(BukkitAdapter.adapt(block), sideEffects.doesApplyAny()); + return true; } @Override @@ -497,20 +502,17 @@ public class BukkitWorld extends AbstractWorld { } @Override - public Set applySideEffects(BlockVector3 position, com.sk89q.worldedit.world.block.BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException { - BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - if (adapter != null) { - adapter.applySideEffects(BukkitAdapter.adapt(getWorld(), position), previousType, sideEffectSet); + 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( - adapter.getSupportedSideEffects(), + WorldEditPlugin.getInstance().getInternalPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply() ); } - return Sets.intersection( - WorldEditPlugin.getInstance().getInternalPlatform().getSupportedSideEffects(), - sideEffectSet.getSideEffectsToApply() - ); + return ImmutableSet.of(); } @Override 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 416b4b621..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,17 +30,16 @@ 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.util.SideEffectSet; 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; @@ -98,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 sideEffectSet side effects to apply - * @return true if a block was likely changed + * @param world the world reference + * @return the native access object */ - boolean setBlock(Location location, BlockStateHolder state, SideEffectSet sideEffectSet); - - /** - * Applies 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 side effects to apply - */ - void applySideEffects(Location position, BlockState previousType, SideEffectSet sideEffectSet); + WorldNativeAccess createWorldNativeAccess(World world); /** * Get the state for the given entity. 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 9f5499dcd..5bf4d88af 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 @@ -66,10 +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, SideEffectSet.none()); - } - @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 21bcf1718..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,13 +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; @@ -71,6 +68,7 @@ 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; @@ -151,26 +149,11 @@ public final class FAWE_Spigot_v1_14_R4 extends CachedBukkitAdapter implements I return state.toBaseBlock(); } - @Override - public boolean setBlock(Location location, BlockStateHolder state, SideEffectSet sideEffectSet) { - return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, sideEffectSet.shouldApply(SideEffect.LIGHTING)); - } - - @Override - public void applySideEffects(Location position, BlockState previousType, SideEffectSet sideEffectSet) { - return; //TODO: properly implement SideEffects into FAWE - } - @Override public Set getSupportedSideEffects() { return SideEffectSet.defaults().getSideEffectsToApply(); } - @Override - public > boolean setBlock(Location location, B state, boolean notifyAndLight) { - return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, notifyAndLight); - } - 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(); @@ -222,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()); 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 ffbe28f1d..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,6 +41,7 @@ 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; @@ -73,6 +71,7 @@ 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; @@ -160,26 +159,11 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I return state.toBaseBlock(); } - @Override - public boolean setBlock(Location location, BlockStateHolder state, SideEffectSet sideEffectSet) { - return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, sideEffectSet.shouldApply(SideEffect.LIGHTING)); - } - - @Override - public void applySideEffects(Location position, BlockState previousType, SideEffectSet sideEffectSet) { - return; //TODO: properly implement SideEffects into FAWE - } - @Override public Set getSupportedSideEffects() { return SideEffectSet.defaults().getSideEffectsToApply(); } - @Override - public > boolean setBlock(Location location, B state, boolean notifyAndLight) { - return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, notifyAndLight); - } - 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(); @@ -231,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()); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java index a97372095..3a5f2af11 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.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_16_1.BlockMaterial_1_16_1; -import com.boydti.fawe.bukkit.adapter.mc1_16_1.BukkitAdapter_1_16_1; -import com.boydti.fawe.bukkit.adapter.mc1_16_1.BukkitGetBlocks_1_16_1; -import com.boydti.fawe.bukkit.adapter.mc1_16_1.MapChunkUtil_1_16_1; +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; @@ -44,6 +41,7 @@ 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; @@ -72,6 +70,7 @@ 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; @@ -159,26 +158,11 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I return state.toBaseBlock(); } - @Override - public boolean setBlock(Location location, BlockStateHolder state, SideEffectSet sideEffectSet) { - return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, sideEffectSet.shouldApply(SideEffect.LIGHTING)); - } - - @Override - public void applySideEffects(Location position, BlockState previousType, SideEffectSet sideEffectSet) { - return; //TODO: properly implement SideEffects into FAWE - } - @Override public Set getSupportedSideEffects() { return SideEffectSet.defaults().getSideEffectsToApply(); } - @Override - public > boolean setBlock(Location location, B state, boolean notifyAndLight) { - return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, notifyAndLight); - } - 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(); @@ -230,6 +214,12 @@ public final class FAWE_Spigot_v1_16_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()); 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 index c4c814fcb..5b142a364 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.java @@ -24,9 +24,9 @@ import java.util.Locale; public enum SideEffect { LIGHTING(State.ON), NEIGHBORS(State.ON), - CONNECTIONS(State.ON), + VALIDATION(State.ON), ENTITY_AI(State.OFF), - PLUGIN_EVENTS(State.OFF); + EVENTS(State.OFF); private final String displayName; private final String description; diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index 4708d4596..016a3d60d 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -502,12 +502,12 @@ "worldedit.sideeffect.lighting.description": "Updates block lighting", "worldedit.sideeffect.neighbors": "Neighbors", "worldedit.sideeffect.neighbors.description": "Notifies nearby blocks of changes", - "worldedit.sideeffect.connections": "Connections", - "worldedit.sideeffect.connections.description": "Updates connections for blocks like fences", + "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.plugin_events": "Plugin Events", - "worldedit.sideeffect.plugin_events.description": "Tells other plugins/mods about these changes when applicable", + "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", diff --git a/worldedit-fabric/build.gradle.kts b/worldedit-fabric/build.gradle.kts index 1cf6df8dd..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 { 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 7fb18b0e7..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 @@ -202,7 +202,7 @@ class FabricPlatform extends AbstractPlatform implements MultiUserPlatform { } private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( - SideEffect.CONNECTIONS, + SideEffect.VALIDATION, SideEffect.ENTITY_AI, SideEffect.LIGHTING, SideEffect.NEIGHBORS 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 e085d2326..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 @@ -34,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; @@ -64,7 +66,6 @@ import net.minecraft.item.ItemStack; import net.minecraft.item.ItemUsageContext; import net.minecraft.server.MinecraftServer; import net.minecraft.server.WorldGenerationProgressListener; -import net.minecraft.server.world.ChunkHolder; import net.minecraft.server.world.ServerChunkManager; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.ActionResult; @@ -129,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. @@ -138,6 +140,7 @@ public class FabricWorld extends AbstractWorld { FabricWorld(World world) { checkNotNull(world); this.worldRef = new WeakReference<>(world); + this.worldNativeAccess = new FabricWorldNativeAccess(worldRef); } /** @@ -191,98 +194,14 @@ public class FabricWorld extends AbstractWorld { return null; } - public void markAndNotifyBlock(World world, BlockPos pos, @Nullable WorldChunk worldChunk, net.minecraft.block.BlockState blockState, - net.minecraft.block.BlockState state, SideEffectSet sideEffectSet) { - Block block = state.getBlock(); - net.minecraft.block.BlockState blockState2 = world.getBlockState(pos); - if (blockState2 == state) { - if (blockState != blockState2) { - world.checkBlockRerender(pos, blockState, blockState2); - } - - if (world.isClient || worldChunk.getLevelType() != null && worldChunk.getLevelType().isAfter(ChunkHolder.LevelType.TICKING)) { - if (sideEffectSet.shouldApply(SideEffect.ENTITY_AI)) { - world.updateListeners(pos, blockState, state, UPDATE | NOTIFY); - } else { - // If we want to skip entity AI, just call the chunk dirty flag. - ((ServerChunkManager) world.getChunkManager()).markForUpdate(pos); - } - } - - if (!world.isClient && sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) { - world.updateNeighbors(pos, blockState.getBlock()); - if (state.hasComparatorOutput()) { - world.updateHorizontalAdjacent(pos, block); - } - } - - if (sideEffectSet.shouldApply(SideEffect.CONNECTIONS)) { - blockState.method_11637(world, pos, 2); - state.updateNeighborStates(world, pos, 2); - state.method_11637(world, pos, 2); - } - - // This is disabled for other platforms, but keep it for mods. - world.onBlockChanged(pos, blockState, blockState2); - } - } - @Override public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) 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 - WorldChunk 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) { - if (sideEffects.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { - world.getChunkManager().getLightingProvider().checkBlock(pos); - } - markAndNotifyBlock(world, pos, chunk, old, newState, sideEffects); - } - - return successful; + return worldNativeAccess.setBlock(position, block, sideEffects); } @Override public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException { - BlockPos pos = new BlockPos(position.getX(), position.getY(), position.getZ()); - net.minecraft.block.BlockState oldData = FabricAdapter.adapt(previousType); - net.minecraft.block.BlockState newData = getWorld().getBlockState(pos); - - if (sideEffectSet.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { - getWorld().getChunkManager().getLightingProvider().checkBlock(pos); - } - markAndNotifyBlock(getWorld(), pos, null, oldData, newData, sideEffectSet); // Update + worldNativeAccess.applySideEffects(position, previousType, sideEffectSet); return Sets.intersection(FabricWorldEdit.inst.getPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply()); } 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/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 b844425eb..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 @@ -208,7 +208,7 @@ class ForgePlatform extends AbstractPlatform implements MultiUserPlatform { } private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( - SideEffect.CONNECTIONS, + SideEffect.VALIDATION, SideEffect.ENTITY_AI, SideEffect.LIGHTING, SideEffect.NEIGHBORS 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 5d69754a6..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 @@ -19,8 +19,6 @@ package com.sk89q.worldedit.forge; -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; @@ -34,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; @@ -105,6 +106,7 @@ import net.minecraft.world.storage.SaveHandler; import net.minecraft.world.storage.WorldInfo; import net.minecraftforge.common.DimensionManager; +import javax.annotation.Nullable; import java.io.File; import java.io.IOException; import java.lang.ref.WeakReference; @@ -119,7 +121,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; -import javax.annotation.Nullable; +import static com.google.common.base.Preconditions.checkNotNull; /** * An adapter to Minecraft worlds for WorldEdit. @@ -127,13 +129,13 @@ import javax.annotation.Nullable; 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. @@ -143,6 +145,7 @@ public class ForgeWorld extends AbstractWorld { ForgeWorld(World world) { checkNotNull(world); this.worldRef = new WeakReference<>(world); + this.nativeAccess = new ForgeWorldNativeAccess(worldRef); } /** @@ -194,101 +197,14 @@ public class ForgeWorld extends AbstractWorld { return null; } - /** - * This is a heavily modified function stripped from MC to apply worldedit-modifications. - * - * @see World#markAndNotifyBlock - */ - public void markAndNotifyBlock(World world, BlockPos pos, @Nullable Chunk chunk, net.minecraft.block.BlockState blockstate, - net.minecraft.block.BlockState newState, SideEffectSet sideEffectSet) { - Block block = newState.getBlock(); - net.minecraft.block.BlockState blockstate1 = world.getBlockState(pos); - if (blockstate1 == newState) { - if (blockstate != blockstate1) { - world.markBlockRangeForRenderUpdate(pos, blockstate, blockstate1); - } - - // Remove redundant branches - if (world.isRemote || chunk == null || chunk.getLocationType().isAtLeast(ChunkHolder.LocationType.TICKING)) { - if (sideEffectSet.shouldApply(SideEffect.ENTITY_AI)) { - world.notifyBlockUpdate(pos, blockstate, newState, UPDATE | NOTIFY); - } else { - // If we want to skip entity AI, just call the chunk dirty flag. - ((ServerChunkProvider) world.getChunkProvider()).markBlockChanged(pos); - } - } - - if (!world.isRemote && sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) { - world.notifyNeighbors(pos, blockstate.getBlock()); - if (newState.hasComparatorInputOverride()) { - world.updateComparatorOutputLevel(pos, block); - } - } - - // Make connection updates optional - if (sideEffectSet.shouldApply(SideEffect.CONNECTIONS)) { - blockstate.updateDiagonalNeighbors(world, pos, 2); - newState.updateNeighbors(world, pos, 2); - newState.updateDiagonalNeighbors(world, pos, 2); - } - - // This is disabled for other platforms, but keep it for mods. - world.onBlockStateChange(pos, blockstate, blockstate1); - } - } - @Override public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) 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) { - if (sideEffects.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { - world.getChunkProvider().getLightManager().checkBlock(pos); - } - markAndNotifyBlock(world, pos, chunk, old, newState, sideEffects); - } - - return successful; + return nativeAccess.setBlock(position, block, sideEffects); } @Override public Set applySideEffects(BlockVector3 position, BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException { - BlockPos pos = new BlockPos(position.getX(), position.getY(), position.getZ()); - net.minecraft.block.BlockState oldData = ForgeAdapter.adapt(previousType); - net.minecraft.block.BlockState newData = getWorld().getBlockState(pos); - - if (sideEffectSet.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { - getWorld().getChunkProvider().getLightManager().checkBlock(pos); - } - markAndNotifyBlock(getWorld(), pos, null, oldData, newData, sideEffectSet); // Update + nativeAccess.applySideEffects(position, previousType, sideEffectSet); return Sets.intersection(ForgeWorldEdit.inst.getPlatform().getSupportedSideEffects(), sideEffectSet.getSideEffectsToApply()); } 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) {