diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index a8d6d3c4f..2fdabbcca 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -20,6 +20,7 @@ repositories { maven { url = uri("https://repo.destroystokyo.com/repository/maven-public//") } maven { url = uri("http://repo.dmulloy2.net/content/groups/public/") } maven { url = uri("http://ci.ender.zone/plugin/repository/everything/") } + maven { url = uri("https://repo.inventivetalent.org/content/groups/public/")} } configurations.all { @@ -49,6 +50,9 @@ dependencies { exclude("com.sk89q.worldedit.worldedit-libs", "bukkit") exclude("com.sk89q.worldedit.worldedit-libs", "core") } + "implementation"("org.inventivetalent:mapmanager:1.7.3-SNAPSHOT") { isTransitive = false } + "implementation"("org.inventivetalent:mapmanager:1.7.3-SNAPSHOT") { isTransitive = false } + "implementation"("com.massivecraft:factions:2.8.0") { isTransitive = false } "implementation"("com.drtshock:factions:1.6.9.5") { isTransitive = false } "implementation"("com.github.TechFortress:GriefPrevention:16.12.0") { isTransitive = false } 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 02bfa1a61..9100da809 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 @@ -2,7 +2,9 @@ package com.boydti.fawe.bukkit; import com.boydti.fawe.Fawe; import com.boydti.fawe.IFawe; -import com.boydti.fawe.beta.implementation.QueueHandler; +import com.boydti.fawe.beta.implementation.queue.QueueHandler; +import com.boydti.fawe.beta.implementation.cache.preloader.AsyncPreloader; +import com.boydti.fawe.beta.implementation.cache.preloader.Preloader; import com.boydti.fawe.bukkit.adapter.BukkitQueueHandler; import com.boydti.fawe.bukkit.listener.BrushListener; import com.boydti.fawe.bukkit.listener.BukkitImageListener; @@ -54,14 +56,14 @@ import org.bukkit.plugin.java.JavaPlugin; public class FaweBukkit implements IFawe, Listener { -// private final WorldEditPlugin plugin; private final Plugin plugin; private VaultUtil vault; private ItemUtil itemUtil; private boolean listeningImages; private BukkitImageListener imageListener; - //private CFIPacketListener packetListener; + + public static boolean PAPER; public VaultUtil getVault() { return this.vault; @@ -69,6 +71,13 @@ public class FaweBukkit implements IFawe, Listener { public FaweBukkit(Plugin plugin) { this.plugin = plugin; + try { + Class.forName("com.destroystokyo.paper.Namespaced"); + PAPER = true; + } catch (Throwable e) { + e.printStackTrace(); + // TODO no paper + } try { Settings.IMP.TICK_LIMITER.ENABLED = !Bukkit.hasWhitelist(); Fawe.set(this); @@ -103,7 +112,6 @@ public class FaweBukkit implements IFawe, Listener { } catch (ClassNotFoundException e) { new ChunkListener_9(); } - }); } @@ -129,15 +137,15 @@ public class FaweBukkit implements IFawe, Listener { PluginManager manager = Bukkit.getPluginManager(); if (manager.getPlugin("PacketListenerApi") == null) { - File output = new File(plugin.getDataFolder().getParentFile(), "PacketListenerAPI_v3.6.0-SNAPSHOT.jar"); - byte[] jarData = Jars.PL_v3_6_0.download(); + File output = new File(plugin.getDataFolder().getParentFile(), "PacketListenerAPI_v3.7.3-SNAPSHOT.jar"); + byte[] jarData = Jars.PL_v3_7_3.download(); try (FileOutputStream fos = new FileOutputStream(output)) { fos.write(jarData); } } if (manager.getPlugin("MapManager") == null) { - File output = new File(plugin.getDataFolder().getParentFile(), "MapManager_v1.4.0-SNAPSHOT.jar"); - byte[] jarData = Jars.MM_v1_4_0.download(); + File output = new File(plugin.getDataFolder().getParentFile(), "MapManager_v1.7.3-SNAPSHOT.jar"); + byte[] jarData = Jars.MM_v1_7_3.download(); try (FileOutputStream fos = new FileOutputStream(output)) { fos.write(jarData); } @@ -397,4 +405,12 @@ public class FaweBukkit implements IFawe, Listener { return null; // return ((BlocksHubBukkit) blocksHubPlugin).getApi(); } + + @Override + public Preloader getPreloader() { + if (PAPER) { + return new AsyncPreloader(); + } + return null; + } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/BukkitQueueHandler.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/BukkitQueueHandler.java index 0822b5385..f7ca9fd9c 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/BukkitQueueHandler.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/BukkitQueueHandler.java @@ -1,7 +1,7 @@ package com.boydti.fawe.bukkit.adapter; import com.boydti.fawe.Fawe; -import com.boydti.fawe.beta.implementation.QueueHandler; +import com.boydti.fawe.beta.implementation.queue.QueueHandler; import com.boydti.fawe.bukkit.listener.ChunkListener; import java.lang.reflect.Field; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java index 8e80c56f1..b076bcd74 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java @@ -2,6 +2,7 @@ package com.boydti.fawe.bukkit.adapter.mc1_14; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.bukkit.FaweBukkit; import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.BitArray4096; @@ -10,6 +11,7 @@ import com.boydti.fawe.util.TaskManager; import com.sk89q.worldedit.world.block.BlockID; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import net.jpountz.util.UnsafeUtils; import net.minecraft.server.v1_14_R1.Block; import net.minecraft.server.v1_14_R1.Chunk; @@ -21,6 +23,7 @@ import net.minecraft.server.v1_14_R1.DataPaletteBlock; import net.minecraft.server.v1_14_R1.DataPaletteLinear; import net.minecraft.server.v1_14_R1.GameProfileSerializer; import net.minecraft.server.v1_14_R1.IBlockData; +import net.minecraft.server.v1_14_R1.PacketPlayOutMapChunk; import net.minecraft.server.v1_14_R1.PlayerChunk; import net.minecraft.server.v1_14_R1.PlayerChunkMap; import org.bukkit.craftbukkit.v1_14_R1.CraftChunk; @@ -78,7 +81,7 @@ public class BukkitAdapter_1_14 { fieldDirtyBits.setAccessible(true); { - Field tmp = null; + Field tmp; try { tmp = DataPaletteBlock.class.getDeclaredField("writeLock"); } catch (NoSuchFieldException paper) { @@ -133,8 +136,6 @@ public class BukkitAdapter_1_14 { } } - private static boolean PAPER = true; - public static Chunk ensureLoaded(net.minecraft.server.v1_14_R1.World nmsWorld, int X, int Z) { Chunk nmsChunk = nmsWorld.getChunkIfLoaded(X, Z); if (nmsChunk != null) { @@ -143,7 +144,7 @@ public class BukkitAdapter_1_14 { if (Fawe.isMainThread()) { return nmsWorld.getChunkAt(X, Z); } - if (PAPER) { + if (FaweBukkit.PAPER) { CraftWorld craftWorld = nmsWorld.getWorld(); CompletableFuture future = craftWorld.getChunkAtAsync(X, Z, true); try { @@ -154,15 +155,14 @@ public class BukkitAdapter_1_14 { } catch (ExecutionException e) { e.printStackTrace(); } catch (Throwable e) { - System.out.println("Error, cannot load chunk async (paper not installed?)"); - PAPER = false; + e.printStackTrace(); } } // TODO optimize return TaskManager.IMP.sync(() -> nmsWorld.getChunkAt(X, Z)); } - private static PlayerChunk getPlayerChunk(net.minecraft.server.v1_14_R1.WorldServer nmsWorld, final int cx, final int cz) { + public static PlayerChunk getPlayerChunk(net.minecraft.server.v1_14_R1.WorldServer nmsWorld, final int cx, final int cz) { PlayerChunkMap chunkMap = nmsWorld.getChunkProvider().playerChunkMap; PlayerChunk playerChunk = chunkMap.visibleChunks.get(ChunkCoordIntPair.pair(cx, cz)); if (playerChunk == null) { @@ -274,7 +274,7 @@ public class BukkitAdapter_1_14 { for (int i = 0; i < num_palette; i++) { final int ordinal = paletteToBlock[i]; blockToPalette[ordinal] = Integer.MAX_VALUE; - final BlockState state = BlockTypes.states[ordinal]; + final BlockState state = BlockTypesCache.states[ordinal]; final IBlockData ibd = ((BlockMaterial_1_14) state.getMaterial()).getState(); palette.a(ibd); } 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 5cede4c10..8bff62644 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 @@ -3,11 +3,10 @@ package com.boydti.fawe.bukkit.adapter.mc1_14; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IChunkSet; -import com.boydti.fawe.beta.implementation.QueueHandler; +import com.boydti.fawe.beta.implementation.queue.QueueHandler; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.object.collection.AdaptedMap; -import com.boydti.fawe.object.collection.AdaptedSetCollection; import com.boydti.fawe.object.collection.BitArray4096; import com.boydti.fawe.util.ReflectionUtils; import com.google.common.collect.Iterables; @@ -58,7 +57,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { public Chunk nmsChunk; public CraftWorld world; public int X, Z; - private boolean forceLoad; +// private boolean forceLoad; public BukkitGetBlocks_1_14(World world, int X, int Z, boolean forceLoad) { this.world = (CraftWorld) world; @@ -76,6 +75,15 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { // } // } + + public int getX() { + return X; + } + + public int getZ() { + return Z; + } + @Override public BiomeType getBiomeType(int x, int z) { BiomeBase base = getChunk().getBiomeIndex()[(z << 4) + x]; @@ -304,7 +312,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); continue; } else { - updateGet(this, nmsChunk, sections, newSection, setArr, layer); + updateGet(this, nmsChunk, sections, newSection, getArr, layer); } } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/MapChunkUtil_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/MapChunkUtil_1_14.java new file mode 100644 index 000000000..ce5559f6c --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/MapChunkUtil_1_14.java @@ -0,0 +1,79 @@ +package com.boydti.fawe.bukkit.adapter.mc1_14; + +import com.boydti.fawe.beta.implementation.packet.ChunkPacket; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.math.BlockVector3; +import net.minecraft.server.v1_14_R1.NBTBase; +import net.minecraft.server.v1_14_R1.NBTTagCompound; +import net.minecraft.server.v1_14_R1.PacketPlayOutMapChunk; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Map; + +public class MapChunkUtil_1_14 { + private static final Field fieldX; + + private static final Field fieldZ; + + private static final Field fieldHeightMap; + + private static final Field fieldBitMask; + + private static final Field fieldChunkData; + + private static final Field fieldBlockEntities; + + private static final Field fieldFull; + + + static { + try { + 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"); + + fieldX.setAccessible(true); + fieldZ.setAccessible(true); + fieldBitMask.setAccessible(true); + fieldHeightMap.setAccessible(true); + fieldBlockEntities.setAccessible(true); + fieldFull.setAccessible(true); + } catch (Throwable e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + public static PacketPlayOutMapChunk create(BukkitImplAdapter adapter, ChunkPacket packet) { + PacketPlayOutMapChunk nmsPacket = new PacketPlayOutMapChunk(); + + try { + fieldX.setInt(nmsPacket, packet.getChunkX()); + fieldZ.setInt(nmsPacket, packet.getChunkZ()); + + fieldBitMask.set(nmsPacket, packet.getChunk().getBitMask()); + NBTBase heightMap = adapter.fromNative(packet.getHeightMap()); + fieldHeightMap.set(nmsPacket, heightMap); + fieldChunkData.set(nmsPacket, packet.get()); + + Map tiles = packet.getChunk().getTiles(); + ArrayList nmsTiles = new ArrayList<>(tiles.size()); + for (Map.Entry entry : tiles.entrySet()) { + NBTBase nmsTag = adapter.fromNative(entry.getValue()); + nmsTiles.add((NBTTagCompound) nmsTag); + } + fieldBlockEntities.set(nmsPacket, nmsTiles); + fieldFull.set(nmsPacket, packet.isFull()); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + return nmsPacket; + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/Spigot_v1_14_R4.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/Spigot_v1_14_R4.java index b6bf9693b..5efd3d5d2 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/Spigot_v1_14_R4.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/Spigot_v1_14_R4.java @@ -20,6 +20,8 @@ package com.boydti.fawe.bukkit.adapter.mc1_14; import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; @@ -62,6 +64,7 @@ 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.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.registry.BlockMaterial; import net.minecraft.server.v1_14_R1.Block; @@ -75,6 +78,7 @@ import net.minecraft.server.v1_14_R1.Chunk; import net.minecraft.server.v1_14_R1.ChunkCoordIntPair; import net.minecraft.server.v1_14_R1.ChunkSection; import net.minecraft.server.v1_14_R1.Entity; +import net.minecraft.server.v1_14_R1.EntityPlayer; import net.minecraft.server.v1_14_R1.EntityTypes; import net.minecraft.server.v1_14_R1.EnumDirection; import net.minecraft.server.v1_14_R1.EnumHand; @@ -102,7 +106,9 @@ import net.minecraft.server.v1_14_R1.NBTTagLongArray; import net.minecraft.server.v1_14_R1.NBTTagShort; import net.minecraft.server.v1_14_R1.NBTTagString; import net.minecraft.server.v1_14_R1.PacketPlayOutEntityStatus; +import net.minecraft.server.v1_14_R1.PacketPlayOutMapChunk; import net.minecraft.server.v1_14_R1.PacketPlayOutTileEntityData; +import net.minecraft.server.v1_14_R1.PlayerChunk; import net.minecraft.server.v1_14_R1.PlayerChunkMap; import net.minecraft.server.v1_14_R1.TileEntity; import net.minecraft.server.v1_14_R1.Vec3D; @@ -132,10 +138,12 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.OptionalInt; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.stream.Stream; import static com.google.common.base.Preconditions.checkNotNull; @@ -171,7 +179,7 @@ public final class Spigot_v1_14_R4 extends CachedBukkitAdapter implements Bukkit if (idbToStateOrdinal != null) return false; idbToStateOrdinal = new char[Block.REGISTRY_ID.a()]; // size for (int i = 0; i < idbToStateOrdinal.length; i++) { - BlockState state = BlockTypes.states[i]; + BlockState state = BlockTypesCache.states[i]; BlockMaterial_1_14 material = (BlockMaterial_1_14) state.getMaterial(); int id = Block.REGISTRY_ID.getId(material.getState()); idbToStateOrdinal[id] = state.getOrdinalChar(); @@ -590,6 +598,13 @@ public final class Spigot_v1_14_R4 extends CachedBukkitAdapter implements Bukkit } } + @Override + public OptionalInt getInternalBlockStateId(BlockState state) { + BlockMaterial_1_14 material = (BlockMaterial_1_14) state.getMaterial(); + IBlockData mcState = material.getCraftBlockData().getState(); + return OptionalInt.of(Block.REGISTRY_ID.getId(mcState)); + } + @Override public BlockState adapt(BlockData blockData) { CraftBlockData cbd = ((CraftBlockData) blockData); @@ -598,7 +613,7 @@ public final class Spigot_v1_14_R4 extends CachedBukkitAdapter implements Bukkit } public BlockState adapt(IBlockData ibd) { - return BlockTypes.states[adaptToInt(ibd)]; + return BlockTypesCache.states[adaptToInt(ibd)]; } public int adaptToInt(IBlockData ibd) { @@ -648,6 +663,35 @@ public final class Spigot_v1_14_R4 extends CachedBukkitAdapter implements Bukkit )); } + @Override + public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket packet) { + WorldServer nmsWorld = ((CraftWorld) world).getHandle(); + PlayerChunk map = BukkitAdapter_1_14.getPlayerChunk(nmsWorld, packet.getChunkX(), packet.getChunkZ()); + if (map != null && map.hasBeenLoaded()) { + boolean flag = false; + PlayerChunk.d players = map.players; + Stream stream = players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag); + + EntityPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle(); + stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer) + .forEach(entityPlayer -> { + synchronized (packet) { + PacketPlayOutMapChunk nmsPacket = (PacketPlayOutMapChunk) packet.getNativePacket(); + if (nmsPacket == null) { + nmsPacket = MapChunkUtil_1_14.create(this, packet); + packet.setNativePacket(nmsPacket); + } + try { + FaweCache.IMP.CHUNK_FLAG.get().set(true); + entityPlayer.playerConnection.sendPacket(nmsPacket); + } finally { + FaweCache.IMP.CHUNK_FLAG.get().set(false); + } + } + }); + } + } + private static EnumDirection adapt(Direction face) { switch (face) { case NORTH: return EnumDirection.NORTH; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/test/TestChunkPacketSend.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/test/TestChunkPacketSend.java new file mode 100644 index 000000000..7d9be59dc --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/test/TestChunkPacketSend.java @@ -0,0 +1,34 @@ +package com.boydti.fawe.bukkit.adapter.mc1_14.test; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.ListenerPriority; +import com.comphenix.protocol.events.PacketAdapter; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.reflect.StructureModifier; +import com.comphenix.protocol.wrappers.WrappedBlockData; +import org.bukkit.plugin.Plugin; + +import java.util.List; + +public class TestChunkPacketSend { + public TestChunkPacketSend(Plugin plugin) { + // Disable all sound effects + ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + protocolManager.addPacketListener( + new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.MAP_CHUNK) { + @Override + public void onPacketSending(PacketEvent event) { + if (event.getPacketType() != PacketType.Play.Server.MAP_CHUNK) { + return; + } + PacketContainer packet = event.getPacket(); + StructureModifier bytes = packet.getBytes(); + StructureModifier blockData = packet.getBlockData(); + List values = blockData.getValues(); + } + }); + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/ArrayWrapper.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/ArrayWrapper.java deleted file mode 100644 index d24b7cc19..000000000 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/ArrayWrapper.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.boydti.fawe.bukkit.chat; - -import java.lang.reflect.Array; -import java.util.Arrays; -import java.util.Collection; -import org.apache.commons.lang.Validate; - -/** - * Represents a wrapper around an array class of an arbitrary reference type, - * which properly implements "value" hash code and equality functions. - *

- * This class is intended for use as a key to a map. - *

- * - * @param The type of elements in the array. - * @author Glen Husman - * @see Arrays - */ -public final class ArrayWrapper { - - /** - * Creates an array wrapper with some elements. - * - * @param elements The elements of the array. - */ - @SafeVarargs - public ArrayWrapper(E... elements) { - setArray(elements); - } - - private E[] _array; - - /** - * Retrieves a reference to the wrapped array instance. - * - * @return The array wrapped by this instance. - */ - public E[] getArray() { - return _array; - } - - /** - * Set this wrapper to wrap a new array instance. - * - * @param array The new wrapped array. - */ - public void setArray(E[] array) { - Validate.notNull(array, "The array must not be null."); - _array = array; - } - - /** - * Determines if this object has a value equivalent to another object. - * - * @see Arrays#equals(Object[], Object[]) - */ - @SuppressWarnings("rawtypes") - @Override - public boolean equals(Object other) { - if (!(other instanceof ArrayWrapper)) { - return false; - } - return Arrays.equals(_array, ((ArrayWrapper) other)._array); - } - - /** - * Gets the hash code represented by this objects value. - * - * @return This object's hash code. - * @see Arrays#hashCode(Object[]) - */ - @Override - public int hashCode() { - return Arrays.hashCode(_array); - } - - /** - * Converts an iterable element collection to an array of elements. - * The iteration order of the specified object will be used as the array element order. - * - * @param list The iterable of objects which will be converted to an array. - * @param c The type of the elements of the array. - * @return An array of elements in the specified iterable. - */ - @SuppressWarnings("unchecked") - public static T[] toArray(Iterable list, Class c) { - int size = -1; - if (list instanceof Collection) { - @SuppressWarnings("rawtypes") - Collection coll = (Collection) list; - size = coll.size(); - } - - - if (size < 0) { - size = 0; - // Ugly hack: Count it ourselves - for (@SuppressWarnings("unused") T element : list) { - size++; - } - } - - T[] result = (T[]) Array.newInstance(c, size); - int i = 0; - for (T element : list) { // Assumes iteration order is consistent - result[i++] = element; // Assign array element at index THEN increment counter - } - return result; - } - -} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/JsonRepresentedObject.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/JsonRepresentedObject.java deleted file mode 100644 index 0996a1aaf..000000000 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/JsonRepresentedObject.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.boydti.fawe.bukkit.chat; - -import com.google.gson.stream.JsonWriter; -import java.io.IOException; - -/** - * Represents an object that can be serialized to a JSON writer instance. - */ -interface JsonRepresentedObject { - - /** - * Writes the JSON representation of this object to the specified writer. - * @param writer The JSON writer which will receive the object. - * @throws IOException If an error occurs writing to the stream. - */ - public void writeJson(JsonWriter writer) throws IOException; - -} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/JsonString.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/JsonString.java deleted file mode 100644 index 115bdf66e..000000000 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/JsonString.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.boydti.fawe.bukkit.chat; - -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import org.bukkit.configuration.serialization.ConfigurationSerializable; - -/** - * Represents a JSON string value. - * Writes by this object will not write name values nor begin/end objects in the JSON stream. - * All writes merely write the represented string value. - */ -final class JsonString implements JsonRepresentedObject, ConfigurationSerializable { - - private String _value; - - public JsonString(CharSequence value) { - _value = value == null ? null : value.toString(); - } - - @Override - public void writeJson(JsonWriter writer) throws IOException { - writer.value(getValue()); - } - - public String getValue() { - return _value; - } - - public Map serialize() { - HashMap theSingleValue = new HashMap<>(); - theSingleValue.put("stringValue", _value); - return theSingleValue; - } - - public static JsonString deserialize(Map map) { - return new JsonString(map.get("stringValue").toString()); - } - - @Override - public String toString() { - return _value; - } - -} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/Reflection.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/Reflection.java deleted file mode 100644 index a60d4cdda..000000000 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/Reflection.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.boydti.fawe.bukkit.chat; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import org.bukkit.Bukkit; - -/** - * A class containing static utility methods and caches which are intended as reflective conveniences. - * Unless otherwise noted, upon failure methods will return {@code null}. - */ -public final class Reflection { - - /** - * Stores loaded classes from the {@code net.minecraft.server} package. - */ - private static final Map> _loadedNMSClasses = new HashMap<>(); - /** - * Stores loaded classes from the {@code org.bukkit.craftbukkit} package (and subpackages). - */ - private static final Map> _loadedOBCClasses = new HashMap<>(); - private static final Map, Map> _loadedFields = new HashMap<>(); - /** - * Contains loaded methods in a cache. - * The map maps [types to maps of [method names to maps of [parameter types to method instances]]]. - */ - private static final Map, Map>, Method>>> _loadedMethods = new HashMap<>(); - private static String _versionString; - - private Reflection() { } - - /** - * Gets the version string from the package name of the CraftBukkit server implementation. - * This is needed to bypass the JAR package name changing on each update. - * - * @return The version string of the OBC and NMS packages, including the trailing dot. - */ - public synchronized static String getVersion() { - if (_versionString == null) { - String name = Bukkit.getServer().getClass().getPackage().getName(); - _versionString = name.substring(name.lastIndexOf('.') + 1) + "."; - } - - return _versionString; - } - - /** - * Gets a {@link Class} object representing a type contained within the {@code net.minecraft.server} versioned package. - * The class instances returned by this method are cached, such that no lookup will be done twice (unless multiple threads are accessing this method simultaneously). - * - * @param className The name of the class, excluding the package, within NMS. - * @return The class instance representing the specified NMS class, or {@code null} if it could not be loaded. - */ - public synchronized static Class getNMSClass(String className) { - if (_loadedNMSClasses.containsKey(className)) { - return _loadedNMSClasses.get(className); - } - - String fullName = "net.minecraft.server." + getVersion() + className; - Class clazz; - try { - clazz = Class.forName(fullName); - } catch (ClassNotFoundException e) { - _loadedNMSClasses.put(className, null); - throw new RuntimeException(e); - } - _loadedNMSClasses.put(className, clazz); - return clazz; - } - - /** - * Gets a {@link Class} object representing a type contained within the {@code org.bukkit.craftbukkit} versioned package. - * The class instances returned by this method are cached, such that no lookup will be done twice (unless multiple threads are accessing this method simultaneously). - * - * @param className The name of the class, excluding the package, within OBC. This name may contain a subpackage name, such as {@code inventory.CraftItemStack}. - * @return The class instance representing the specified OBC class, or {@code null} if it could not be loaded. - */ - public synchronized static Class getOBCClass(String className) { - if (_loadedOBCClasses.containsKey(className)) { - return _loadedOBCClasses.get(className); - } - - String fullName = "org.bukkit.craftbukkit." + getVersion() + className; - Class clazz; - try { - clazz = Class.forName(fullName); - } catch (ClassNotFoundException e) { - _loadedOBCClasses.put(className, null); - throw new RuntimeException(e); - } - _loadedOBCClasses.put(className, clazz); - return clazz; - } - - /** - * Attempts to get the NMS handle of a CraftBukkit object. - *

- * The only match currently attempted by this method is a retrieval by using a parameterless {@code getHandle()} method implemented by the runtime type of the specified object. - *

- * - * @param obj The object for which to retrieve an NMS handle. - * @return The NMS handle of the specified object, or {@code null} if it could not be retrieved using {@code getHandle()}. - */ - public synchronized static Object getHandle(Object obj) throws InvocationTargetException, IllegalAccessException, IllegalArgumentException { - return getMethod(obj.getClass(), "getHandle").invoke(obj); - } - - /** - * Retrieves a {@link Field} instance declared by the specified class with the specified name. - * Java access modifiers are ignored during this retrieval. No guarantee is made as to whether the field - * returned will be an instance or static field. - *

- * A global caching mechanism within this class is used to store fields. Combined with synchronization, this guarantees that - * no field will be reflectively looked up twice. - *

- *

- * If a field is deemed suitable for return, {@link Field#setAccessible(boolean) setAccessible} will be invoked with an argument of {@code true} before it is returned. - * This ensures that callers do not have to check or worry about Java access modifiers when dealing with the returned instance. - *

- * - * @param clazz The class which contains the field to retrieve. - * @param name The declared name of the field in the class. - * @return A field object with the specified name declared by the specified class. - * @see Class#getDeclaredField(String) - */ - public synchronized static Field getField(Class clazz, String name) { - Map loaded; - if (!_loadedFields.containsKey(clazz)) { - loaded = new HashMap<>(); - _loadedFields.put(clazz, loaded); - } else { - loaded = _loadedFields.get(clazz); - } - if (loaded.containsKey(name)) { - // If the field is loaded (or cached as not existing), return the relevant value, which might be null - return loaded.get(name); - } - try { - Field field = clazz.getDeclaredField(name); - field.setAccessible(true); - loaded.put(name, field); - return field; - } catch (NoSuchFieldException | SecurityException e) { - // Error loading - e.printStackTrace(); - // Cache field as not existing - loaded.put(name, null); - return null; - } - } - - /** - * Retrieves a {@link Method} instance declared by the specified class with the specified name and argument types. - * Java access modifiers are ignored during this retrieval. No guarantee is made as to whether the field - * returned will be an instance or static field. - *

- * A global caching mechanism within this class is used to store method. Combined with synchronization, this guarantees that - * no method will be reflectively looked up twice. - *

- * If a method is deemed suitable for return, {@link Method#setAccessible(boolean) setAccessible} will be invoked with an argument of {@code true} before it is returned. - * This ensures that callers do not have to check or worry about Java access modifiers when dealing with the returned instance. - *

- * This method does not search superclasses of the specified type for methods with the specified signature. - * Callers wishing this behavior should use {@link Class#getDeclaredMethod(String, Class...)}. - * - * @param clazz The class which contains the method to retrieve. - * @param name The declared name of the method in the class. - * @param args The formal argument types of the method. - * @return A method object with the specified name declared by the specified class. - */ - public synchronized static Method getMethod(Class clazz, String name, Class... args) { - if (!_loadedMethods.containsKey(clazz)) { - _loadedMethods.put(clazz, new HashMap<>()); - } - - Map>, Method>> loadedMethodNames = _loadedMethods.get(clazz); - if (!loadedMethodNames.containsKey(name)) { - loadedMethodNames.put(name, new HashMap<>()); - } - - Map>, Method> loadedSignatures = loadedMethodNames.get(name); - ArrayWrapper> wrappedArg = new ArrayWrapper<>(args); - if (loadedSignatures.containsKey(wrappedArg)) { - return loadedSignatures.get(wrappedArg); - } - - for (Method m : clazz.getMethods()) { - if (m.getName().equals(name) && Arrays.equals(args, m.getParameterTypes())) { - m.setAccessible(true); - loadedSignatures.put(wrappedArg, m); - return m; - } - } - loadedSignatures.put(wrappedArg, null); - return null; - } - -} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/CFIPacketListener.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/CFIPacketListener.java index 8574de6f4..b7be10671 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/CFIPacketListener.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/CFIPacketListener.java @@ -1,5 +1,6 @@ package com.boydti.fawe.bukkit.listener; +import com.boydti.fawe.FaweCache; import com.boydti.fawe.command.CFICommands; import com.boydti.fawe.object.RunnableVal3; import com.boydti.fawe.object.brush.visualization.VirtualWorld; @@ -15,20 +16,32 @@ import com.comphenix.protocol.wrappers.BlockPosition; import com.comphenix.protocol.wrappers.ChunkCoordIntPair; import com.comphenix.protocol.wrappers.EnumWrappers; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitPlayer; import com.sk89q.worldedit.event.platform.BlockInteractEvent; import com.sk89q.worldedit.event.platform.Interaction; import com.sk89q.worldedit.extension.platform.PlatformManager; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.world.block.BlockState; import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import java.util.List; + +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockTypes; import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; /** * The CFIPacketListener handles packets for editing the VirtualWorld @@ -45,99 +58,90 @@ public class CFIPacketListener implements Listener { this.plugin = plugin; this.protocolmanager = ProtocolLibrary.getProtocolManager(); - // TODO NOT IMPLEMENTED -// // Direct digging to the virtual world -// registerBlockEvent(PacketType.Play.Client.BLOCK_DIG, false, new RunnableVal3() { -// @Override -// public void run(Builder event, URI gen, String pt) { -// try { -// Player plr = event.getPlayer(); -// BlockVector3 realPos = pt.add(gen.getOrigin().toBlockPoint()); -// if (!sendBlockChange(plr, gen, pt, Interaction.HIT)) { -// gen.setBlock(pt, BlockTypes.AIR.getDefaultState()); -// } -// } catch (WorldEditException e) { -// e.printStackTrace(); -// } -// } -// }); -// -// // Direct placing to the virtual world -// RunnableVal3 placeTask = new RunnableVal3() { -// @Override -// public void run(Builder event, URI gen, String pt) { -// try { -// Player plr = event.getPlayer(); -// List hands = event.getPacket().getHands().getValues(); -// -// EnumWrappers.Hand enumHand = hands.isEmpty() ? EnumWrappers.Hand.MAIN_HAND : hands.get(0); -// PlayerInventory inv = plr.getInventory(); -// ItemStack hand = enumHand == EnumWrappers.Hand.MAIN_HAND ? inv.getItemInMainHand() : inv.getItemInOffHand(); -// if (hand.getType().isBlock()) { -// Material type = hand.getType(); -// switch (type) { -// case AIR: -// case CAVE_AIR: -// case VOID_AIR: -// break; -// default: { -// BlockStateHolder block = BukkitAdapter.asBlockState(hand); -// if (block != null) { -// gen.setBlock(pt, block); -// return; -// } -// } -// } -// } -// pt = getRelPos(event, gen); -// sendBlockChange(plr, gen, pt, Interaction.OPEN); -// } catch (WorldEditException e) { -// e.printStackTrace(); -// } -// } -// }; -// registerBlockEvent(PacketType.Play.Client.BLOCK_PLACE, true, placeTask); -// registerBlockEvent(PacketType.Play.Client.USE_ITEM, true, placeTask); -// -// // Cancel block change packets where the real world overlaps with the virtual one -// registerBlockEvent(PacketType.Play.Server.BLOCK_CHANGE, false, new RunnableVal3() { -// @Override -// public void run(Builder event, URI gen, String pt) { -// // Do nothing -// } -// }); -// -// // Modify chunk packets where the real world overlaps with the virtual one -// protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.MAP_CHUNK) { -// @Override -// public void onPacketSending(PacketEvent event) { -// if (!event.isServerPacket()) return; -// -// VirtualWorld gen = getGenerator(event); -// if (gen != null) { -// BlockVector3 origin = gen.getOrigin().toBlockPoint(); -// PacketContainer packet = event.getPacket(); -// StructureModifier ints = packet.getIntegers(); -// int cx = ints.read(0); -// int cz = ints.read(1); -// -// int ocx = origin.getBlockX() >> 4; -// int ocz = origin.getBlockZ() >> 4; -// -// if (gen.contains(BlockVector3.at((cx - ocx) << 4, 0, (cz - ocz) << 4))) { -// event.setCancelled(true); -// -// Player plr = event.getPlayer(); -// -// FaweQueue queue = SetQueue.IMP.getNewQueue(plr.getWorld().getName(), true, false); -// -// FaweChunk toSend = gen.getSnapshot(cx - ocx, cz - ocz); -// toSend.setLoc(gen, cx, cz); -// queue.sendChunkUpdate(toSend, FawePlayer.wrap(plr)); -// } -// } -// } -// }); + // Direct digging to the virtual world + registerBlockEvent(PacketType.Play.Client.BLOCK_DIG, false, new RunnableVal3() { + @Override + public void run(PacketEvent event, VirtualWorld gen, BlockVector3 pt) { + try { + Player plr = event.getPlayer(); + BlockVector3 realPos = pt.add(gen.getOrigin().toBlockPoint()); + if (!sendBlockChange(plr, gen, pt, Interaction.HIT)) { + gen.setBlock(pt, BlockTypes.AIR.getDefaultState()); + } + } catch (WorldEditException e) { + e.printStackTrace(); + } + } + }); + + // Direct placing to the virtual world + RunnableVal3 placeTask = new RunnableVal3() { + @Override + public void run(PacketEvent event, VirtualWorld gen, BlockVector3 pt) { + try { + Player plr = event.getPlayer(); + List hands = event.getPacket().getHands().getValues(); + + EnumWrappers.Hand enumHand = hands.isEmpty() ? EnumWrappers.Hand.MAIN_HAND : hands.get(0); + PlayerInventory inv = plr.getInventory(); + ItemStack hand = enumHand == EnumWrappers.Hand.MAIN_HAND ? inv.getItemInMainHand() : inv.getItemInOffHand(); + if (hand.getType().isBlock()) { + Material type = hand.getType(); + switch (type) { + case AIR: + case CAVE_AIR: + case VOID_AIR: + break; + default: { + BlockStateHolder block = BukkitAdapter.asBlockState(hand); + if (block != null) { + gen.setBlock(pt, block); + return; + } + } + } + } + pt = getRelPos(event, gen); + sendBlockChange(plr, gen, pt, Interaction.OPEN); + } catch (WorldEditException e) { + e.printStackTrace(); + } + } + }; + registerBlockEvent(PacketType.Play.Client.BLOCK_PLACE, true, placeTask); + registerBlockEvent(PacketType.Play.Client.USE_ITEM, true, placeTask); + + // Cancel block change packets where the real world overlaps with the virtual one + registerBlockEvent(PacketType.Play.Server.BLOCK_CHANGE, false, new RunnableVal3() { + @Override + public void run(PacketEvent event, VirtualWorld gen, BlockVector3 pt) { + // Do nothing + } + }); + + // Modify chunk packets where the real world overlaps with the virtual one + protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.MAP_CHUNK) { + @Override + public void onPacketSending(PacketEvent event) { + if (!event.isServerPacket() || FaweCache.IMP.CHUNK_FLAG.get().get()) return; + VirtualWorld gen = getGenerator(event); + if (gen != null) { + BlockVector3 origin = gen.getOrigin().toBlockPoint(); + PacketContainer packet = event.getPacket(); + StructureModifier ints = packet.getIntegers(); + int cx = ints.read(0); + int cz = ints.read(1); + + int ocx = origin.getBlockX() >> 4; + int ocz = origin.getBlockZ() >> 4; + + if (gen.contains(BlockVector3.at((cx - ocx) << 4, 0, (cz - ocz) << 4))) { + event.setCancelled(true); + gen.refreshChunk(cx - ocx, cz - ocz); + } + } + } + }); // The following few listeners are to ignore block collisions where the virtual and real world overlap diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/ChunkCache.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/ChunkCache.java deleted file mode 100644 index 115740f29..000000000 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/ChunkCache.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.boydti.fawe.bukkit.listener; - -public class ChunkCache { -} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/preloader/PluginPreloader.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/preloader/PluginPreloader.java new file mode 100644 index 000000000..dcc98dc26 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/preloader/PluginPreloader.java @@ -0,0 +1,196 @@ +package com.boydti.fawe.bukkit.preloader; + +import com.boydti.fawe.Fawe; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.PluginBase; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginLoader; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.InputStream; +import java.lang.ref.PhantomReference; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Logger; + +public class PluginPreloader extends PluginBase { + private World world; + private Set loaded; + private int index; + private AtomicBoolean invalidator; + private final Object invalidatorLock; + + public PluginPreloader() { + invalidator = new AtomicBoolean(); + invalidatorLock = new Object(); + } + + public AtomicBoolean invalidate() { + synchronized (invalidatorLock) { + invalidator.set(false); + return invalidator = new AtomicBoolean(true); + } + } + + private synchronized void unload() { + World oldWorld = world; + if (oldWorld != null) { + Set toUnload = loaded; + if (loaded != null && index > 0) { + Iterator iter = toUnload.iterator(); + Fawe.get().getQueueHandler().sync(() -> { + for (int i = 0; i < index && iter.hasNext(); i++) { + BlockVector2 chunk = iter.next(); + world.removePluginChunkTicket(chunk.getX(), chunk.getZ(), this); + } + }); + } + } + this.world = null; + this.loaded = null; + this.index = 0; + } + + public void update(Region region) { + AtomicBoolean invalidator = invalidate(); + synchronized (this) { + com.sk89q.worldedit.world.World weWorld = region.getWorld(); + if (weWorld == null) { + return; + } + unload(); + index = 0; + world = BukkitAdapter.adapt(weWorld); + loaded = region.getChunks(); + Iterator iter = loaded.iterator(); + + if (!invalidator.get()) return; + Fawe.get().getQueueHandler().syncWhenFree(() -> { + for (; iter.hasNext() && invalidator.get();index++) { + BlockVector2 chunk = iter.next(); + if (!world.isChunkLoaded(chunk.getX(), chunk.getZ())) { + world.addPluginChunkTicket(chunk.getX(), chunk.getZ(), this); + } + } + }); + } + } + + public void clear() { + invalidate(); + unload(); + } + + @Override + public @NotNull File getDataFolder() { + return null; + } + + @Override + public @NotNull PluginDescriptionFile getDescription() { + return null; + } + + @Override + public @NotNull FileConfiguration getConfig() { + return null; + } + + @Override + public @Nullable InputStream getResource(@NotNull String filename) { + return null; + } + + @Override + public void saveConfig() { + + } + + @Override + public void saveDefaultConfig() { + + } + + @Override + public void saveResource(@NotNull String resourcePath, boolean replace) { + + } + + @Override + public void reloadConfig() { + + } + + @Override + public @NotNull PluginLoader getPluginLoader() { + return null; + } + + @Override + public @NotNull Server getServer() { + return null; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public void onDisable() { + + } + + @Override + public void onLoad() { + + } + + @Override + public void onEnable() { + + } + + @Override + public boolean isNaggable() { + return false; + } + + @Override + public void setNaggable(boolean canNag) { + + } + + @Override + public @Nullable ChunkGenerator getDefaultWorldGenerator(@NotNull String worldName, @Nullable String id) { + return null; + } + + @Override + public @NotNull Logger getLogger() { + return null; + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + return false; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) { + return null; + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageViewer.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageViewer.java index 73a0877ba..b49a35b3f 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageViewer.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageViewer.java @@ -19,173 +19,173 @@ import org.bukkit.entity.ItemFrame; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; -//import org.inventivetalent.mapmanager.MapManagerPlugin; -//import org.inventivetalent.mapmanager.controller.MapController; -//import org.inventivetalent.mapmanager.controller.MultiMapController; -//import org.inventivetalent.mapmanager.manager.MapManager; -//import org.inventivetalent.mapmanager.wrapper.MapWrapper; +import org.inventivetalent.mapmanager.MapManagerPlugin; +import org.inventivetalent.mapmanager.controller.MapController; +import org.inventivetalent.mapmanager.controller.MultiMapController; +import org.inventivetalent.mapmanager.manager.MapManager; +import org.inventivetalent.mapmanager.wrapper.MapWrapper; public class BukkitImageViewer implements ImageViewer { -// private final MapManager mapManager; -// private final Player player; -// private BufferedImage last; + private final MapManager mapManager; + private final Player player; + private BufferedImage last; private ItemFrame[][] frames; -// private boolean reverse; + private boolean reverse; public BukkitImageViewer(Player player) { -// mapManager = ((MapManagerPlugin) Bukkit.getPluginManager().getPlugin("MapManager")).getMapManager(); -// this.player = player; + mapManager = ((MapManagerPlugin) Bukkit.getPluginManager().getPlugin("MapManager")).getMapManager(); + this.player = player; } -// + public void selectFrame(ItemFrame start) { -// Location pos1 = start.getLocation().clone(); -// Location pos2 = start.getLocation().clone(); -// -// BlockFace facing = start.getFacing(); -// int planeX = facing.getModX() == 0 ? 1 : 0; -// int planeY = facing.getModY() == 0 ? 1 : 0; -// int planeZ = facing.getModZ() == 0 ? 1 : 0; -// -// ItemFrame[][] res = find(pos1, pos2, facing); -// Location tmp; -// while (true) { -// if (res != null) { -// frames = res; -// } -// tmp = pos1.clone().subtract(planeX, planeY, planeZ); -// if ((res = find(tmp, pos2, facing)) != null) { -// pos1 = tmp; -// continue; -// } -// tmp = pos2.clone().add(planeX, planeY, planeZ); -// if ((res = find(pos1, tmp, facing)) != null) { -// pos2 = tmp; -// continue; -// } -// tmp = pos1.clone().subtract(planeX, 0, planeZ); -// if ((res = find(tmp, pos2, facing)) != null) { -// pos1 = tmp; -// continue; -// } -// tmp = pos2.clone().add(planeX, 0, planeZ); -// if ((res = find(pos1, tmp, facing)) != null) { -// pos2 = tmp; -// continue; -// } -// tmp = pos1.clone().subtract(0, 1, 0); -// if ((res = find(tmp, pos2, facing)) != null) { -// pos1 = tmp; -// continue; -// } -// tmp = pos2.clone().add(0, 1, 0); -// if ((res = find(pos1, tmp, facing)) != null) { -// pos2 = tmp; -// continue; -// } -// break; -// } + Location pos1 = start.getLocation().clone(); + Location pos2 = start.getLocation().clone(); + + BlockFace facing = start.getFacing(); + int planeX = facing.getModX() == 0 ? 1 : 0; + int planeY = facing.getModY() == 0 ? 1 : 0; + int planeZ = facing.getModZ() == 0 ? 1 : 0; + + ItemFrame[][] res = find(pos1, pos2, facing); + Location tmp; + while (true) { + if (res != null) { + frames = res; + } + tmp = pos1.clone().subtract(planeX, planeY, planeZ); + if ((res = find(tmp, pos2, facing)) != null) { + pos1 = tmp; + continue; + } + tmp = pos2.clone().add(planeX, planeY, planeZ); + if ((res = find(pos1, tmp, facing)) != null) { + pos2 = tmp; + continue; + } + tmp = pos1.clone().subtract(planeX, 0, planeZ); + if ((res = find(tmp, pos2, facing)) != null) { + pos1 = tmp; + continue; + } + tmp = pos2.clone().add(planeX, 0, planeZ); + if ((res = find(pos1, tmp, facing)) != null) { + pos2 = tmp; + continue; + } + tmp = pos1.clone().subtract(0, 1, 0); + if ((res = find(tmp, pos2, facing)) != null) { + pos1 = tmp; + continue; + } + tmp = pos2.clone().add(0, 1, 0); + if ((res = find(pos1, tmp, facing)) != null) { + pos2 = tmp; + continue; + } + break; + } } -// + public ItemFrame[][] getItemFrames() { return frames; } -// -// private ItemFrame[][] find(Location pos1, Location pos2, BlockFace facing) { -// try { -// Location distance = pos2.clone().subtract(pos1).add(1, 1, 1); -// int width = Math.max(distance.getBlockX(), distance.getBlockZ()); -// ItemFrame[][] frames = new ItemFrame[width][distance.getBlockY()]; -// -// World world = pos1.getWorld(); -// -// this.reverse = (facing == BlockFace.NORTH || facing == BlockFace.EAST); -// int v = 0; -// for (double y = pos1.getY(); y <= pos2.getY(); y++, v++) { -// int h = 0; -// for (double z = pos1.getZ(); z <= pos2.getZ(); z++) { -// for (double x = pos1.getX(); x <= pos2.getX(); x++, h++) { -// Location pos = new Location(world, x, y, z); -// Collection entities = world.getNearbyEntities(pos, 0.1, 0.1, 0.1); -// boolean contains = false; -// for (Entity ent : entities) { -// if (ent instanceof ItemFrame && ((ItemFrame) ent).getFacing() == facing) { -// ItemFrame itemFrame = (ItemFrame) ent; -// itemFrame.setRotation(Rotation.NONE); -// contains = true; -// frames[reverse ? width - 1 - h : h][v] = (ItemFrame) ent; -// break; -// } -// } -// if (!contains) return null; -// } -// } -// } -// return frames; -// } catch (Throwable e) { -// e.printStackTrace(); -// } -// return null; -// } + + private ItemFrame[][] find(Location pos1, Location pos2, BlockFace facing) { + try { + Location distance = pos2.clone().subtract(pos1).add(1, 1, 1); + int width = Math.max(distance.getBlockX(), distance.getBlockZ()); + ItemFrame[][] frames = new ItemFrame[width][distance.getBlockY()]; + + World world = pos1.getWorld(); + + this.reverse = (facing == BlockFace.NORTH || facing == BlockFace.EAST); + int v = 0; + for (double y = pos1.getY(); y <= pos2.getY(); y++, v++) { + int h = 0; + for (double z = pos1.getZ(); z <= pos2.getZ(); z++) { + for (double x = pos1.getX(); x <= pos2.getX(); x++, h++) { + Location pos = new Location(world, x, y, z); + Collection entities = world.getNearbyEntities(pos, 0.1, 0.1, 0.1); + boolean contains = false; + for (Entity ent : entities) { + if (ent instanceof ItemFrame && ((ItemFrame) ent).getFacing() == facing) { + ItemFrame itemFrame = (ItemFrame) ent; + itemFrame.setRotation(Rotation.NONE); + contains = true; + frames[reverse ? width - 1 - h : h][v] = (ItemFrame) ent; + break; + } + } + if (!contains) return null; + } + } + } + return frames; + } catch (Throwable e) { + e.printStackTrace(); + } + return null; + } @Override public void view(Drawable drawable) { -// view(null, drawable); + view(null, drawable); } -// -// private void view(@Nullable BufferedImage image, @Nullable Drawable drawable) { -// if (image == null && drawable == null) throw new IllegalArgumentException("An image or drawable must be provided. Both cannot be null"); -// boolean initializing = last == null; -// -// if (this.frames != null) { -// if (image == null && drawable != null) image = drawable.draw(); -// last = image; -// int width = frames.length; -// int height = frames[0].length; -// BufferedImage scaled = ImageUtil.getScaledInstance(image, 128 * width, 128 * height, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); -// MapWrapper mapWrapper = mapManager.wrapMultiImage(scaled, width, height); -// MultiMapController controller = (MultiMapController) mapWrapper.getController(); -// controller.addViewer(player); -// controller.sendContent(player); -// controller.showInFrames(player, frames, true); -// } else { -// int slot = getMapSlot(player); -// if (slot == -1) { -// if (initializing) { -// player.getInventory().setItemInMainHand(new ItemStack(Material.MAP)); -// } else { -// return; -// } -// } else if (player.getInventory().getHeldItemSlot() != slot) { -// player.getInventory().setHeldItemSlot(slot); -// } -// if (image == null && drawable != null) image = drawable.draw(); -// last = image; -// BufferedImage scaled = ImageUtil.getScaledInstance(image, 128, 128, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); -// MapWrapper mapWrapper = mapManager.wrapImage(scaled); -// MapController controller = mapWrapper.getController(); -// controller.addViewer(player); -// controller.sendContent(player); -// controller.showInHand(player, true); -// } -// } -// -// private int getMapSlot(Player player) { -// PlayerInventory inventory = player.getInventory(); -// for (int i = 0; i < 9; i++) { -// ItemStack item = inventory.getItem(i); -// if (item != null && item.getType() == Material.MAP) { -// return i; -// } -// } -// return -1; -// } -// + + private void view(@Nullable BufferedImage image, @Nullable Drawable drawable) { + if (image == null && drawable == null) throw new IllegalArgumentException("An image or drawable must be provided. Both cannot be null"); + boolean initializing = last == null; + + if (this.frames != null) { + if (image == null && drawable != null) image = drawable.draw(); + last = image; + int width = frames.length; + int height = frames[0].length; + BufferedImage scaled = ImageUtil.getScaledInstance(image, 128 * width, 128 * height, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); + MapWrapper mapWrapper = mapManager.wrapMultiImage(scaled, width, height); + MultiMapController controller = (MultiMapController) mapWrapper.getController(); + controller.addViewer(player); + controller.sendContent(player); + controller.showInFrames(player, frames, true); + } else { + int slot = getMapSlot(player); + if (slot == -1) { + if (initializing) { + player.getInventory().setItemInMainHand(new ItemStack(Material.MAP)); + } else { + return; + } + } else if (player.getInventory().getHeldItemSlot() != slot) { + player.getInventory().setHeldItemSlot(slot); + } + if (image == null && drawable != null) image = drawable.draw(); + last = image; + BufferedImage scaled = ImageUtil.getScaledInstance(image, 128, 128, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); + MapWrapper mapWrapper = mapManager.wrapImage(scaled); + MapController controller = mapWrapper.getController(); + controller.addViewer(player); + controller.sendContent(player); + controller.showInHand(player, true); + } + } + + private int getMapSlot(Player player) { + PlayerInventory inventory = player.getInventory(); + for (int i = 0; i < 9; i++) { + ItemStack item = inventory.getItem(i); + if (item != null && item.getType() == Material.MAP) { + return i; + } + } + return -1; + } + public void refresh() { -// if (last != null) view(last, null); + if (last != null) view(last, null); } @Override public void close() throws IOException { -// last = null; + last = null; } } \ No newline at end of file diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlock.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlock.java index ab8802f98..c492296f2 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlock.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlock.java @@ -11,6 +11,7 @@ import com.sk89q.worldedit.world.block.BlockID; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import org.bukkit.FluidCollisionMode; import org.bukkit.Location; import org.bukkit.Material; @@ -57,7 +58,7 @@ public class AsyncBlock implements Block { } public int getPropertyId() { - return world.getBlock(x, y, z).getInternalId() >> BlockTypes.BIT_OFFSET; + return world.getBlock(x, y, z).getInternalId() >> BlockTypesCache.BIT_OFFSET; } public int getCombinedId() { @@ -105,7 +106,7 @@ public class AsyncBlock implements Block { @Deprecated public boolean setTypeIdAndPropertyId(int id, int propertyId) { - return setCombinedId(id + (propertyId << BlockTypes.BIT_OFFSET)); + return setCombinedId(id + (propertyId << BlockTypesCache.BIT_OFFSET)); } @Deprecated diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlockState.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlockState.java index 24a8cc696..8b171a9a6 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlockState.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlockState.java @@ -9,6 +9,7 @@ import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; @@ -42,7 +43,7 @@ public class AsyncBlockState implements BlockState { } public int getPropertyId() { - return state.getInternalId() >> BlockTypes.BIT_OFFSET; + return state.getInternalId() >> BlockTypesCache.BIT_OFFSET; } @Override @@ -158,12 +159,12 @@ public class AsyncBlockState implements BlockState { @Override public byte getRawData() { - return (byte) (state.getInternalId() >> BlockTypes.BIT_OFFSET); + return (byte) (state.getInternalId() >> BlockTypesCache.BIT_OFFSET); } @Override public void setRawData(byte data) { - int combinedId = getTypeId() + (data << BlockTypes.BIT_OFFSET); + int combinedId = getTypeId() + (data << BlockTypesCache.BIT_OFFSET); state = com.sk89q.worldedit.world.block.BlockState.getFromInternalId(combinedId).toBaseBlock(state.getNbtData()); this.blockData = BukkitAdapter.adapt(state); } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java index 28dd35557..96cbf1e33 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java @@ -139,7 +139,7 @@ public enum BukkitAdapter { */ public static Player adapt(com.sk89q.worldedit.entity.Player player) { player = PlayerProxy.unwrap(player); - return ((BukkitPlayer) player).getPlayer(); + return player == null ? null : ((BukkitPlayer) player).getPlayer(); } /** diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java index c1427985b..a8463ce59 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java @@ -34,6 +34,7 @@ import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.Map; +import java.util.OptionalInt; public class BukkitBlockRegistry extends BundledBlockRegistry { @@ -93,6 +94,10 @@ public class BukkitBlockRegistry extends BundledBlockRegistry { this.material = bukkitMaterial; } + public int getId() { + return material.getId(); + } + @Override public boolean isAir() { switch (material) { @@ -132,4 +137,9 @@ public class BukkitBlockRegistry extends BundledBlockRegistry { } return blocks; } + + @Override + public OptionalInt getInternalBlockStateId(BlockState state) { + return WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBlockStateId(state); + } } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java index 8eec6d307..b93afda5d 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java @@ -47,11 +47,6 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.gamemode.GameMode; import com.sk89q.worldedit.world.gamemode.GameModes; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.UUID; -import javax.annotation.Nullable; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; @@ -62,23 +57,44 @@ import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + public class BukkitPlayer extends AbstractPlayerActor { private Player player; private WorldEditPlugin plugin; public BukkitPlayer(Player player) { - this(WorldEditPlugin.getInstance(), player); + super(getExistingMap(WorldEditPlugin.getInstance(), player)); + this.plugin = WorldEditPlugin.getInstance(); + this.player = player; } public BukkitPlayer(WorldEditPlugin plugin, Player player) { this.plugin = plugin; this.player = player; + init(); + } + + private void init() { if (Settings.IMP.CLIPBOARD.USE_DISK) { loadClipboardFromDisk(); } } + private static Map getExistingMap(WorldEditPlugin plugin, Player player) { + BukkitPlayer cached = plugin.getCachedPlayer(player); + if (cached != null) { + return cached.getRawMeta(); + } + return new ConcurrentHashMap<>(); + } + @Override public UUID getUniqueId() { return player.getUniqueId(); @@ -87,16 +103,16 @@ public class BukkitPlayer extends AbstractPlayerActor { @Override public BaseItemStack getItemInHand(HandSide handSide) { ItemStack itemStack = handSide == HandSide.MAIN_HAND - ? player.getInventory().getItemInMainHand() - : player.getInventory().getItemInOffHand(); + ? getPlayer().getInventory().getItemInMainHand() + : getPlayer().getInventory().getItemInOffHand(); return BukkitAdapter.adapt(itemStack); } @Override public BaseBlock getBlockInHand(HandSide handSide) throws WorldEditException { ItemStack itemStack = handSide == HandSide.MAIN_HAND - ? player.getInventory().getItemInMainHand() - : player.getInventory().getItemInOffHand(); + ? getPlayer().getInventory().getItemInMainHand() + : getPlayer().getInventory().getItemInOffHand(); return BukkitAdapter.asBlockState(itemStack).toBaseBlock(); } @@ -107,18 +123,18 @@ public class BukkitPlayer extends AbstractPlayerActor { @Override public String getDisplayName() { - return player.getDisplayName(); + return getPlayer().getDisplayName(); } @Override public void giveItem(BaseItemStack itemStack) { - final PlayerInventory inv = player.getInventory(); + final PlayerInventory inv = getPlayer().getInventory(); ItemStack newItem = BukkitAdapter.adapt(itemStack); if (itemStack.getType().getId().equalsIgnoreCase(WorldEdit.getInstance().getConfiguration().wandItem)) { inv.remove(newItem); } - final ItemStack item = player.getInventory().getItemInMainHand(); - player.getInventory().setItemInMainHand(newItem); + final ItemStack item = getPlayer().getInventory().getItemInMainHand(); + getPlayer().getInventory().setItemInMainHand(newItem); HashMap overflow = inv.addItem(item); if (!overflow.isEmpty()) { TaskManager.IMP.sync(new RunnableVal() { @@ -128,7 +144,7 @@ public class BukkitPlayer extends AbstractPlayerActor { ItemStack stack = entry.getValue(); if (stack.getType() != Material.AIR && stack.getAmount() > 0) { Item - dropped = player.getWorld().dropItem(player.getLocation(), stack); + dropped = getPlayer().getWorld().dropItem(getPlayer().getLocation(), stack); PlayerDropItemEvent event = new PlayerDropItemEvent(player, dropped); if (event.isCancelled()) { dropped.remove(); @@ -138,45 +154,45 @@ public class BukkitPlayer extends AbstractPlayerActor { } }); } - player.updateInventory(); + getPlayer().updateInventory(); } @Override public void printRaw(String msg) { for (String part : msg.split("\n")) { - player.sendMessage(part); + getPlayer().sendMessage(part); } } @Override public void print(String msg) { for (String part : msg.split("\n")) { - player.sendMessage("\u00A7d" + part); + getPlayer().sendMessage("\u00A7d" + part); } } @Override public void printDebug(String msg) { for (String part : msg.split("\n")) { - player.sendMessage("\u00A77" + part); + getPlayer().sendMessage("\u00A77" + part); } } @Override public void printError(String msg) { for (String part : msg.split("\n")) { - player.sendMessage("§c" + part); + getPlayer().sendMessage("§c" + part); } } @Override public void print(Component component) { - TextAdapter.sendComponent(player, WorldEditText.format(component)); + TextAdapter.sendComponent(getPlayer(), WorldEditText.format(component)); } @Override public void setPosition(Vector3 pos, float pitch, float yaw) { - org.bukkit.World world = player.getWorld(); + org.bukkit.World world = getPlayer().getWorld(); if (pos instanceof com.sk89q.worldedit.util.Location) { com.sk89q.worldedit.util.Location loc = (com.sk89q.worldedit.util.Location) pos; Extent extent = loc.getExtent(); @@ -184,42 +200,41 @@ public class BukkitPlayer extends AbstractPlayerActor { world = Bukkit.getWorld(((World) extent).getName()); } } - player.teleport(new Location(world, pos.getX(), pos.getY(), pos.getZ(), yaw, pitch)); + getPlayer().teleport(new Location(world, pos.getX(), pos.getY(), pos.getZ(), yaw, pitch)); } @Override public String[] getGroups() { - return plugin.getPermissionsResolver().getGroups(player); + return plugin.getPermissionsResolver().getGroups(getPlayer()); } @Override public BlockBag getInventoryBlockBag() { - return new BukkitPlayerBlockBag(player); + return new BukkitPlayerBlockBag(getPlayer()); } @Override public GameMode getGameMode() { - return GameModes.get(player.getGameMode().name().toLowerCase(Locale.ROOT)); + return GameModes.get(getPlayer().getGameMode().name().toLowerCase(Locale.ROOT)); } @Override public void setGameMode(GameMode gameMode) { - player.setGameMode(org.bukkit.GameMode.valueOf(gameMode.getId().toUpperCase(Locale.ROOT))); + getPlayer().setGameMode(org.bukkit.GameMode.valueOf(gameMode.getId().toUpperCase(Locale.ROOT))); } @Override public boolean hasPermission(String perm) { return (!plugin.getLocalConfiguration().noOpPermissions && player.isOp()) - || plugin.getPermissionsResolver().hasPermission( - player.getWorld().getName(), player, perm); + || plugin.getPermissionsResolver().hasPermission(player.getWorld().getName(), player, perm); } @Override public boolean togglePermission(String permission) { if (this.hasPermission(permission)) { - player.addAttachment(plugin).setPermission(permission, false); + getPlayer().addAttachment(plugin).setPermission(permission, false); return false; } else { - player.addAttachment(plugin).setPermission(permission, true); + getPlayer().addAttachment(plugin).setPermission(permission, true); return true; } } @@ -231,19 +246,19 @@ public class BukkitPlayer extends AbstractPlayerActor { * - The `/wea` command will give/remove the required bypass permission */ if (Fawe.imp().getVault() == null || Fawe. imp().getVault().permission == null) { - player.addAttachment(Fawe. imp().getPlugin()).setPermission(permission, value); + getPlayer().addAttachment(Fawe. imp().getPlugin()).setPermission(permission, value); } else if (value) { if (!Fawe. imp().getVault().permission.playerAdd(player, permission)) { - player.addAttachment(Fawe. imp().getPlugin()).setPermission(permission, value); + getPlayer().addAttachment(Fawe. imp().getPlugin()).setPermission(permission, value); } } else if (!Fawe.imp().getVault().permission.playerRemove(player, permission)) { - player.addAttachment(Fawe.imp().getPlugin()).setPermission(permission, value); + getPlayer().addAttachment(Fawe.imp().getPlugin()).setPermission(permission, value); } } @Override public World getWorld() { - return BukkitAdapter.adapt(player.getWorld()); + return BukkitAdapter.adapt(getPlayer().getWorld()); } @Override @@ -253,21 +268,30 @@ public class BukkitPlayer extends AbstractPlayerActor { if (params.length > 0) { send = send + "|" + StringUtil.joinString(params, "|"); } - player.sendPluginMessage(plugin, WorldEditPlugin.CUI_PLUGIN_CHANNEL, send.getBytes(CUIChannelListener.UTF_8_CHARSET)); + getPlayer().sendPluginMessage(plugin, WorldEditPlugin.CUI_PLUGIN_CHANNEL, send.getBytes(CUIChannelListener.UTF_8_CHARSET)); } public Player getPlayer() { + if (!player.isValid()) { + Player tmp = Bukkit.getPlayer(getUniqueId()); + if (tmp != null) { + player = tmp; + } else { + System.out.println("Invalid player " + player.getName()); + new Exception().printStackTrace(); + } + } return player; } @Override public boolean isAllowedToFly() { - return player.getAllowFlight(); + return getPlayer().getAllowFlight(); } @Override public void setFlying(boolean flying) { - player.setFlying(flying); + getPlayer().setFlying(flying); } @Override @@ -277,7 +301,7 @@ public class BukkitPlayer extends AbstractPlayerActor { @Override public com.sk89q.worldedit.util.Location getLocation() { - Location nativeLocation = player.getLocation(); + Location nativeLocation = getPlayer().getLocation(); Vector3 position = BukkitAdapter.asVector(nativeLocation); return new com.sk89q.worldedit.util.Location( getWorld(), @@ -288,7 +312,7 @@ public class BukkitPlayer extends AbstractPlayerActor { @Override public boolean setLocation(com.sk89q.worldedit.util.Location location) { - return player.teleport(BukkitAdapter.adapt(location)); + return getPlayer().teleport(BukkitAdapter.adapt(location)); } @Nullable @@ -299,7 +323,7 @@ public class BukkitPlayer extends AbstractPlayerActor { @Override public SessionKey getSessionKey() { - return new SessionKeyImpl(this.player.getUniqueId(), player.getName()); + return new SessionKeyImpl(getUniqueId(), getName()); } private static class SessionKeyImpl implements SessionKey { @@ -342,11 +366,11 @@ public class BukkitPlayer extends AbstractPlayerActor { @Override public > void sendFakeBlock(BlockVector3 pos, B block) { - Location loc = new Location(player.getWorld(), pos.getX(), pos.getY(), pos.getZ()); + Location loc = new Location(getPlayer().getWorld(), pos.getX(), pos.getY(), pos.getZ()); if (block == null) { - player.sendBlockChange(loc, player.getWorld().getBlockAt(loc).getBlockData()); + getPlayer().sendBlockChange(loc, getPlayer().getWorld().getBlockAt(loc).getBlockData()); } else { - player.sendBlockChange(loc, BukkitAdapter.adapt(block)); + getPlayer().sendBlockChange(loc, BukkitAdapter.adapt(block)); if (block instanceof BaseBlock && ((BaseBlock) block).hasNbtData()) { BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); if (adapter != null) { @@ -361,8 +385,13 @@ public class BukkitPlayer extends AbstractPlayerActor { @Override public void sendTitle(String title, String sub) { - player.sendTitle(ChatColor.GOLD + title, ChatColor.GOLD + sub, 0, 70, 20); + getPlayer().sendTitle(ChatColor.GOLD + title, ChatColor.GOLD + sub, 0, 70, 20); Bukkit.getServer().dispatchCommand(player, "title " + getName() + " subtitle [{\"text\":\"" + sub + "\",\"color\":\"gold\"}]"); Bukkit.getServer().dispatchCommand(player, "title " + getName() + " title [{\"text\":\"" + title + "\",\"color\":\"gold\"}]"); } + + @Override + public void unregister() { + getPlayer().removeMetadata("WE", WorldEditPlugin.getInstance()); + } } 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 c67ef1c27..33786c3ce 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 @@ -21,7 +21,10 @@ package com.sk89q.worldedit.bukkit; 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.boydti.fawe.bukkit.FaweBukkit; import com.boydti.fawe.bukkit.adapter.mc1_14.BukkitGetBlocks_1_14; import com.boydti.fawe.config.Settings; import com.sk89q.jnbt.CompoundTag; @@ -31,6 +34,7 @@ import com.sk89q.worldedit.WorldEditException; 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.history.change.BlockChange; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; @@ -52,6 +56,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import javax.annotation.Nullable; + import org.bukkit.Effect; import org.bukkit.TreeType; import org.bukkit.World; @@ -317,8 +322,19 @@ public class BukkitWorld extends AbstractWorld { @Override public void checkLoadedChunk(BlockVector3 pt) { World world = getWorld(); - - world.getChunkAt(pt.getBlockX() >> 4, pt.getBlockZ() >> 4); + int X = pt.getBlockX() >> 4; + int Z = pt.getBlockZ() >> 4; + if (Fawe.isMainThread()) { + world.getChunkAt(X, Z); + } else if (!world.isChunkLoaded(X, Z)) { + if (FaweBukkit.PAPER) { + world.getChunkAtAsync(X, Z, true); + } else { + Fawe.get().getQueueHandler().sync(() -> { + world.getChunkAt(X, Z); + }); + } + } } @Override @@ -516,11 +532,18 @@ public class BukkitWorld extends AbstractWorld { } @Override - public void sendChunk(int chunkX, int chunkZ, int bitMask) { + public void refreshChunk(int X, int Z) { + getWorld().refreshChunk(X, Z); } @Override public IChunkGet get(int chunkX, int chunkZ) { return new BukkitGetBlocks_1_14(getWorldChecked(), chunkX, chunkZ, Settings.IMP.QUEUE.POOL); } + + @Override + public void sendFakeChunk(Player player, ChunkPacket packet) { + org.bukkit.entity.Player bukkitPlayer = BukkitAdapter.adapt(player); + WorldEditPlugin.getInstance().getBukkitImplAdapter().sendFakeChunk(getWorld(), bukkitPlayer, packet); + } } 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 4e782bf29..efdce1069 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 @@ -84,6 +84,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.WorldInitEvent; import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.metadata.MetadataValue; @@ -559,15 +560,26 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter * @return a wrapped player */ public BukkitPlayer wrapPlayer(Player player) { - synchronized (player) { - @NotNull List meta = player.getMetadata("WE"); - if (meta == null || meta.isEmpty()) { - BukkitPlayer wePlayer = new BukkitPlayer(this, player); - player.setMetadata("WE", new FixedMetadataValue(this, wePlayer)); - return wePlayer; + BukkitPlayer wePlayer = getCachedPlayer(player); + if (wePlayer == null) { + synchronized (player) { + wePlayer = getCachedPlayer(player); + if (wePlayer == null) { + wePlayer = new BukkitPlayer(this, player); + player.setMetadata("WE", new FixedMetadataValue(this, wePlayer)); + return wePlayer; + } } - return (BukkitPlayer) meta.get(0).value(); } + return wePlayer; + } + + public BukkitPlayer getCachedPlayer(Player player) { + List meta = player.getMetadata("WE"); + if (meta == null || meta.isEmpty()) { + return null; + } + return (BukkitPlayer) meta.get(0).value(); } public Actor wrapCommandSender(CommandSender sender) { 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 26423c18b..079866df3 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 @@ -20,6 +20,7 @@ package com.sk89q.worldedit.bukkit.adapter; import com.boydti.fawe.Fawe; +import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.bukkit.FaweBukkit; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; @@ -141,6 +142,13 @@ public interface BukkitImplAdapter extends IBukkitAdapter { */ void sendFakeOP(Player player); + /** + * Send a fake chunk packet to a player + * @param player + * @param packet + */ + void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket packet); + /** * Simulates a player using an item. * diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/CachedBukkitAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/CachedBukkitAdapter.java index 76e0392d7..c196fcd2c 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/CachedBukkitAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/CachedBukkitAdapter.java @@ -6,6 +6,7 @@ import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemTypes; import java.util.List; @@ -58,7 +59,7 @@ public abstract class CachedBukkitAdapter implements IBukkitAdapter { @Override public BlockType adapt(Material material) { try { - return BlockTypes.values[blockTypes[material.ordinal()]]; + return BlockTypesCache.values[blockTypes[material.ordinal()]]; } catch (NullPointerException e) { if (init()) return adapt(material); throw e; diff --git a/worldedit-bukkit/src/main/resources/plugin.yml b/worldedit-bukkit/src/main/resources/plugin.yml index d9dbe048d..2a8614813 100644 --- a/worldedit-bukkit/src/main/resources/plugin.yml +++ b/worldedit-bukkit/src/main/resources/plugin.yml @@ -154,3 +154,76 @@ permissions: worldedit.extinguish: true worldedit.calc: true worldedit.fill: true + fawe.voxelbrush: + default: op + children: + voxelsniper.brush.ball: true + voxelsniper.brush.biome: true + voxelsniper.brush.blendball: true + voxelsniper.brush.blenddisc: true + voxelsniper.brush.blendvoxel: true + voxelsniper.brush.blendvoxeldisc: true + voxelsniper.brush.blob: true + voxelsniper.brush.blockreset: true + voxelsniper.brush.blockresetsurface: true + voxelsniper.brush.canyon: true + voxelsniper.brush.canyonselection: true + voxelsniper.brush.checkervoxeldisc: true + voxelsniper.brush.cleansnow: true + voxelsniper.brush.clonestamp: true + voxelsniper.brush.copypasta: true + voxelsniper.brush.cylinder: true + voxelsniper.brush.disc: true + voxelsniper.brush.discface: true + voxelsniper.brush.dome: true + voxelsniper.brush.drain: true + voxelsniper.brush.ellipse: true + voxelsniper.brush.ellipsoid: true + voxelsniper.brush.eraser: true + voxelsniper.brush.erode: true + voxelsniper.brush.extrude: true + voxelsniper.brush.filldown: true + voxelsniper.brush.flatocean: true + voxelsniper.brush.heatray: true + voxelsniper.brush.jaggedline: true + voxelsniper.brush.line: true + voxelsniper.brush.move: true + voxelsniper.brush.ocean: true + voxelsniper.brush.overlay: true + voxelsniper.brush.pull: true + voxelsniper.brush.randomerode: true + voxelsniper.brush.ring: true + voxelsniper.brush.rot2d: true + voxelsniper.brush.rot2dvert: true + voxelsniper.brush.rot3d: true + voxelsniper.brush.ruler: true + voxelsniper.brush.scanner: true + voxelsniper.brush.set: true + voxelsniper.brush.setredstoneflip: true + voxelsniper.brush.setredstonerotate: true + voxelsniper.brush.shellball: true + voxelsniper.brush.shellset: true + voxelsniper.brush.shellvoxel: true + voxelsniper.brush.signoverwrite: true + voxelsniper.brush.snipe: true + voxelsniper.brush.snowcone: true + voxelsniper.brush.spiralstaircase: true + voxelsniper.brush.splatterball: true + voxelsniper.brush.splatterdisc: true + voxelsniper.brush.splatteroverlay: true + voxelsniper.brush.splattervoxel: true + voxelsniper.brush.splattervoxeldisc: true + voxelsniper.brush.spline: true + voxelsniper.brush.stamp: true + voxelsniper.brush.stencil: true + voxelsniper.brush.stencillist: true + voxelsniper.brush.threepointcircle: true + voxelsniper.brush.triangle: true + voxelsniper.brush.underlay: true + voxelsniper.brush.voltmeter: true + voxelsniper.brush.voxel: true + voxelsniper.brush.voxeldisc: true + voxelsniper.brush.voxeldiscface: true + voxelsniper.brush.warp: true + voxelsniper.goto: true + voxelsniper.sniper: true 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 ea1d641f9..465e94a92 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java @@ -1,6 +1,6 @@ package com.boydti.fawe; -import com.boydti.fawe.beta.implementation.QueueHandler; +import com.boydti.fawe.beta.implementation.queue.QueueHandler; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.brush.visualization.VisualQueue; @@ -16,7 +16,6 @@ import com.boydti.fawe.util.TextureUtil; import com.boydti.fawe.util.WEManager; import com.github.luben.zstd.util.Native; import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.factory.DefaultTransformParser; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.session.request.Request; @@ -30,12 +29,9 @@ import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryUsage; -import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; import javax.management.InstanceAlreadyExistsException; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java b/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java index 4904f6e88..62bce6fea 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java @@ -7,7 +7,6 @@ import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.extent.LightingExtent; -import com.boydti.fawe.object.schematic.Schematic; import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.MainUtil; @@ -192,7 +191,7 @@ public class FaweAPI { * @see ClipboardFormat * @see Schematic */ - public static Schematic load(File file) throws IOException { + public static Clipboard load(File file) throws IOException { return ClipboardFormats.findByFile(file).load(file); } 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 9fe5ff141..f59f22d1b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java @@ -28,6 +28,7 @@ import com.sk89q.worldedit.math.MutableBlockVector3; import com.sk89q.worldedit.math.MutableVector3; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import java.lang.reflect.Field; import java.util.ArrayList; @@ -38,10 +39,14 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; import static com.google.common.base.Preconditions.checkNotNull; @@ -101,19 +106,25 @@ public enum FaweCache implements Trimable { @Override public synchronized boolean trim(boolean aggressive) { - BLOCK_TO_PALETTE.clean(); - PALETTE_TO_BLOCK.clean(); - BLOCK_STATES.clean(); - SECTION_BLOCKS.clean(); - PALETTE_CACHE.clean(); - PALETTE_TO_BLOCK_CHAR.clean(); - INDEX_STORE.clean(); + if (aggressive) { + CleanableThreadLocal.cleanAll(); + } else { + CHUNK_FLAG.clean(); + BYTE_BUFFER_8192.clean(); + BLOCK_TO_PALETTE.clean(); + PALETTE_TO_BLOCK.clean(); + BLOCK_STATES.clean(); + SECTION_BLOCKS.clean(); + PALETTE_CACHE.clean(); + PALETTE_TO_BLOCK_CHAR.clean(); + INDEX_STORE.clean(); - MUTABLE_VECTOR3.clean(); - MUTABLE_BLOCKVECTOR3.clean(); - SECTION_BITS_TO_CHAR.clean(); - for (Map.Entry entry : REGISTERED_SINGLETONS.entrySet()) { - entry.getValue().clean(); + MUTABLE_VECTOR3.clean(); + MUTABLE_BLOCKVECTOR3.clean(); + SECTION_BITS_TO_CHAR.clean(); + for (Map.Entry entry : REGISTERED_SINGLETONS.entrySet()) { + entry.getValue().clean(); + } } for (Map.Entry entry : REGISTERED_POOLS.entrySet()) { Pool pool = entry.getValue(); @@ -197,13 +208,19 @@ public enum FaweCache implements Trimable { public static final FaweException LOW_MEMORY = new FaweException(BBC.WORLDEDIT_CANCEL_REASON_LOW_MEMORY); public static final FaweException MAX_ENTITIES = new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_ENTITIES); public static final FaweException MAX_TILES = new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_TILES); + public static final FaweException MAX_ITERATIONS = new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_ITERATIONS); /* thread cache */ + public final CleanableThreadLocal CHUNK_FLAG = new CleanableThreadLocal<>(AtomicBoolean::new); // resets to false + + public final CleanableThreadLocal LONG_BUFFER_1024 = new CleanableThreadLocal<>(() -> new long[1024]); + + public final CleanableThreadLocal BYTE_BUFFER_8192 = new CleanableThreadLocal<>(() -> new byte[8192]); public final CleanableThreadLocal BLOCK_TO_PALETTE = new CleanableThreadLocal<>(() -> { - int[] result = new int[BlockTypes.states.length]; + int[] result = new int[BlockTypesCache.states.length]; Arrays.fill(result, Integer.MAX_VALUE); return result; }); @@ -228,6 +245,8 @@ public enum FaweCache implements Trimable { * Holds data for a palette used in a chunk section */ public final class Palette { + public int bitsPerEntry; + public int paletteToBlockLength; /** * Reusable buffer array, MUST check paletteToBlockLength for actual length @@ -278,7 +297,7 @@ public enum FaweCache implements Trimable { int ordinal = blocksChars[i]; int palette = blockToPalette[ordinal]; if (palette == Integer.MAX_VALUE) { -// BlockState state = BlockTypes.states[ordinal]; +// BlockState state = BlockTypesCache.states[ordinal]; blockToPalette[ordinal] = palette = num_palette; paletteToBlock[num_palette] = ordinal; num_palette++; @@ -290,7 +309,7 @@ public enum FaweCache implements Trimable { int ordinal = blocksInts[i]; int palette = blockToPalette[ordinal]; if (palette == Integer.MAX_VALUE) { - BlockState state = BlockTypes.states[ordinal]; + BlockState state = BlockTypesCache.states[ordinal]; blockToPalette[ordinal] = palette = num_palette; paletteToBlock[num_palette] = ordinal; num_palette++; @@ -319,6 +338,7 @@ public enum FaweCache implements Trimable { // Construct palette Palette palette = PALETTE_CACHE.get(); + palette.bitsPerEntry = bitsPerEntry; palette.paletteToBlockLength = num_palette; palette.paletteToBlock = paletteToBlock; @@ -499,6 +519,31 @@ public enum FaweCache implements Trimable { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, queue , Executors.defaultThreadFactory(), - new ThreadPoolExecutor.CallerRunsPolicy()); + new ThreadPoolExecutor.CallerRunsPolicy()) { + protected void afterExecute(Runnable r, Throwable t) { + try { + super.afterExecute(r, t); + if (t == null && r instanceof Future) { + try { + Future future = (Future) r; + if (future.isDone()) { + future.get(); + } + } catch (CancellationException ce) { + t = ce; + } catch (ExecutionException ee) { + t = ee.getCause(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + if (t != null) { + t.printStackTrace(); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + }; } } 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 c9f5ae2c6..2883a874d 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java @@ -1,11 +1,11 @@ package com.boydti.fawe; -import com.boydti.fawe.beta.implementation.QueueHandler; +import com.boydti.fawe.beta.implementation.queue.QueueHandler; +import com.boydti.fawe.beta.implementation.cache.preloader.Preloader; import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.image.ImageViewer; -import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.world.World; import java.io.File; @@ -54,4 +54,6 @@ public interface IFawe { QueueHandler getQueueHandler(); + Preloader getPreloader(); + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFuture.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFuture.java deleted file mode 100644 index 38fecb9c9..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFuture.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.boydti.fawe.beta; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -public class ChunkFuture implements Future { - - private final IChunk chunk; - private volatile boolean cancelled; - private volatile boolean done; - - public ChunkFuture(IChunk chunk) { - this.chunk = chunk; - } - - public IChunk getChunk() { - return chunk; - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - cancelled = true; - return !done; - } - - @Override - public boolean isCancelled() { - return cancelled; - } - - @Override - public boolean isDone() { - return done; - } - - @Override - public Void get() throws InterruptedException, ExecutionException { - synchronized (chunk) { - if (!done) { - this.wait(); - } - } - return null; - } - - @Override - public Void get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - synchronized (chunk) { - if (!done) { - this.wait(unit.toMillis(timeout)); - if (!done) { - throw new TimeoutException(); - } - } - } - return null; - } -} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/Filter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/Filter.java index a459a751c..a4aaf9b3f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/Filter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/Filter.java @@ -1,5 +1,6 @@ package com.boydti.fawe.beta; +import com.boydti.fawe.beta.implementation.filter.block.FilterBlock; import com.sk89q.worldedit.regions.Region; import javax.annotation.Nullable; import org.jetbrains.annotations.Range; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlockMask.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlockMask.java index 7ad53d554..7db8c83b8 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlockMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlockMask.java @@ -1,5 +1,7 @@ package com.boydti.fawe.beta; +import com.boydti.fawe.beta.implementation.filter.block.FilterBlock; + public interface FilterBlockMask { boolean applyBlock(FilterBlock block); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/Flood.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/Flood.java index 961dfba08..9143a86a2 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/Flood.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/Flood.java @@ -1,7 +1,7 @@ package com.boydti.fawe.beta; import com.boydti.fawe.Fawe; -import com.boydti.fawe.beta.implementation.QueueHandler; +import com.boydti.fawe.beta.implementation.queue.QueueHandler; import com.boydti.fawe.util.MathMan; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.world.World; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBatchProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBatchProcessor.java index 97898fb85..bbb522840 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBatchProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBatchProcessor.java @@ -1,8 +1,8 @@ package com.boydti.fawe.beta; import com.boydti.fawe.FaweCache; -import com.boydti.fawe.beta.implementation.EmptyBatchProcessor; -import com.boydti.fawe.beta.implementation.MultiBatchProcessor; +import com.boydti.fawe.beta.implementation.processors.EmptyBatchProcessor; +import com.boydti.fawe.beta.implementation.processors.MultiBatchProcessor; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; @@ -46,7 +46,7 @@ public interface IBatchProcessor { if (set.hasSection(layer)) { if (layer == minLayer) { char[] arr = set.getArray(layer); - int index = (minY & 15) << 12; + int index = (minY & 15) << 8; for (int i = 0; i < index; i++) arr[i] = 0; set.setBlocks(layer, arr); } else { @@ -59,7 +59,7 @@ public interface IBatchProcessor { if (set.hasSection(layer)) { if (layer == minLayer) { char[] arr = set.getArray(layer); - int index = ((maxY + 1) & 15) << 12; + int index = ((maxY + 1) & 15) << 8; for (int i = index; i < arr.length; i++) arr[i] = 0; set.setBlocks(layer, arr); } else { 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 e8150b1fa..1edbdf2a1 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 @@ -1,10 +1,20 @@ package com.boydti.fawe.beta; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.object.collection.BitArray4096; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.block.BlockID; import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.registry.BlockRegistry; +import java.io.IOException; import java.util.Map; import java.util.Set; @@ -19,5 +29,115 @@ public interface IBlocks extends Trimable { BlockState getBlock(int x, int y, int z); + Map getTiles(); + + Set getEntities(); + + BiomeType getBiomeType(int x, int z); + + default int getBitMask() { + int mask = 0; + for (int layer = 0; layer < FaweCache.IMP.CHUNK_LAYERS; layer++) { + if (hasSection(layer)) { + mask += (1 << layer); + } + } + return mask; + } + IBlocks reset(); + + default byte[] toByteArray(boolean writeBiomes) { + return toByteArray(null, writeBiomes); + } + + default byte[] toByteArray(byte[] buffer, boolean writeBiomes) { + if (buffer == null) { + buffer = new byte[1024]; + } + + BlockRegistry registry = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry(); + FastByteArrayOutputStream sectionByteArray = new FastByteArrayOutputStream(buffer); + FaweOutputStream sectionWriter = new FaweOutputStream(sectionByteArray); + try { + for (int layer = 0; layer < FaweCache.IMP.CHUNK_LAYERS; layer++) { + if (!this.hasSection(layer)) continue; + + char[] ids = this.getArray(layer); + + int nonEmpty = 0; // TODO optimize into same loop as toPalette + for (char id : ids) { + if (id != 0) nonEmpty++; + } + + sectionWriter.writeShort(nonEmpty); // non empty + + sectionWriter.writeByte(14); // globalPaletteBitsPerBlock + + if (true) { + BitArray4096 bits = new BitArray4096(14); // globalPaletteBitsPerBlock + bits.setAt(0, 0); + for (int i = 0; i < 4096; i++) { + int ordinal = ids[i]; + BlockState state = BlockState.getFromOrdinal(ordinal); + if (!state.getMaterial().isAir()) { + int mcId = registry.getInternalBlockStateId(state).getAsInt(); + bits.setAt(i, mcId); + } + } + sectionWriter.write(bits.getData()); + } else { + + FaweCache.Palette palette = FaweCache.IMP.toPalette(0, ids); + + sectionWriter.writeByte(palette.bitsPerEntry); // bits per block + sectionWriter.writeVarInt(palette.paletteToBlockLength); + for (int i = 0; i < palette.paletteToBlockLength; i++) { + int ordinal = palette.paletteToBlock[i]; + switch (ordinal) { + case BlockID.CAVE_AIR: + case BlockID.VOID_AIR: + case BlockID.AIR: + case BlockID.__RESERVED__: + sectionWriter.writeVarInt(0); + break; + default: + BlockState state = BlockState.getFromOrdinal(ordinal); + int mcId = registry.getInternalBlockStateId(state).getAsInt(); + sectionWriter.writeVarInt(mcId); + break; + } + } + sectionWriter.writeVarInt(palette.blockStatesLength); + for (int i = 0; i < palette.blockStatesLength; i++) { + sectionWriter.writeLong(palette.blockStates[i]); + } + } + } + +// if (writeBiomes) { +// for (int x = 0; x < 16; x++) { +// for (int z = 0; z < 16; z++) { +// BiomeType biome = this.getBiomeType(x, z); +// if (biome == null) { +// if (writeBiomes) { +// break; +// } else { +// biome = BiomeTypes.FOREST; +// } +// } +// } +// } +// } + if (writeBiomes) { + for (int i = 0; i < 256; i++) { + // TODO biomes + sectionWriter.writeInt(0); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return sectionByteArray.toByteArray(); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunk.java index 3894a0c9f..74298b190 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunk.java @@ -1,5 +1,6 @@ package com.boydti.fawe.beta; +import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.biome.BiomeType; @@ -27,12 +28,6 @@ public interface IChunk> extends Trimable, Callable, IChu */ void init(IQueueExtent extent, int x, int z); - /** - * Get the queue - * @return - */ - IQueueExtent getQueue(); - /** * Get chunkX * @return diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkCache.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkCache.java similarity index 89% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkCache.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkCache.java index 7724e74ef..480db7063 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkCache.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkCache.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.implementation; +package com.boydti.fawe.beta; import com.boydti.fawe.beta.Trimable; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java index 8b83cdc12..e41d5686a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java @@ -28,8 +28,10 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent { CompoundTag getTag(int x, int y, int z); + @Override Map getTiles(); + @Override Set getEntities(); @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java index 698bfaee1..f3d49af3f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java @@ -38,8 +38,13 @@ public interface IChunkSet extends IBlocks, OutputExtent { BiomeType[] getBiomes(); + @Override + BiomeType getBiomeType(int x, int z); + + @Override Map getTiles(); + @Override Set getEntities(); @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateChunk.java index eb0b7b742..74c36d532 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateChunk.java @@ -1,5 +1,6 @@ package com.boydti.fawe.beta; +import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; @@ -38,11 +39,6 @@ public interface IDelegateChunk extends IChunk { return getParent().call(set, finalize); } - @Override - default IQueueExtent getQueue() { - return getParent().getQueue(); - } - @Override default CompoundTag getTag(int x, int y, int z) { return getParent().getTag(x, y, z); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateFilter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateFilter.java index 89736bb90..6149266df 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateFilter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateFilter.java @@ -1,5 +1,6 @@ package com.boydti.fawe.beta; +import com.boydti.fawe.beta.implementation.filter.block.FilterBlock; import com.sk89q.worldedit.regions.Region; import javax.annotation.Nullable; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java index 1c64d3f30..2b87445b0 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java @@ -1,13 +1,11 @@ package com.boydti.fawe.beta; -import com.boydti.fawe.beta.implementation.IChunkCache; -import com.boydti.fawe.beta.implementation.MultiBatchProcessor; +import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; -import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.function.generator.GenBase; @@ -45,16 +43,6 @@ public interface IDelegateQueueExtent extends IQueueExtent { return getParent().isQueueEnabled(); } - @Override - default void clearBlockUpdates(Player... players) { - getParent().clearBlockUpdates(players); - } - - @Override - default void sendBlockUpdates(Player... players) { - getParent().sendBlockUpdates(players); - } - @Override default void enableQueue() { getParent().enableQueue(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java index 3248641ae..219b37ff2 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java @@ -1,15 +1,15 @@ package com.boydti.fawe.beta; import com.boydti.fawe.FaweCache; -import com.boydti.fawe.beta.implementation.IBatchProcessorHolder; -import com.boydti.fawe.beta.implementation.IChunkCache; -import com.boydti.fawe.beta.implementation.MultiBatchProcessor; +import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; +import com.boydti.fawe.beta.implementation.processors.IBatchProcessorHolder; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; @@ -17,7 +17,7 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import javax.annotation.Nullable; import java.io.Flushable; -import java.util.List; +import java.util.Set; import java.util.concurrent.Future; /** @@ -31,22 +31,6 @@ public interface IQueueExtent extends Flushable, Trimable, Extent, IBatchProcess return true; } - /** - * Clear any block updates - * @param players - */ - default void clearBlockUpdates(Player... players) { - throw new UnsupportedOperationException("TODO NOT IMPLEMENTED"); - } - - /** - * Send all the chunks as block updates - * @param players - */ - default void sendBlockUpdates(Player... players) { - throw new UnsupportedOperationException("TODO NOT IMPLEMENTED"); - } - /** * Must ensure that it is enqueued with QueueHandler */ @@ -204,4 +188,35 @@ public interface IQueueExtent extends Flushable, Trimable, Extent, IBatchProcess * @return true if this queue contains no elements */ boolean isEmpty(); + + default ChunkFilterBlock apply(ChunkFilterBlock block, Filter filter, Region region, int chunkX, int chunkZ) { + if (!filter.appliesChunk(chunkX, chunkZ)) { + return block; + } + IChunk chunk = this.getOrCreateChunk(chunkX, chunkZ); + // Initialize + chunk.init(this, chunkX, chunkZ); + + IChunk newChunk = filter.applyChunk(chunk, region); + if (newChunk != null) { + chunk = newChunk; + if (block == null) { + block = this.initFilterBlock(); + } + chunk.filterBlocks(filter, block, region); + } + this.submit(chunk); + return block; + } + + @Override + default T apply(Region region, T filter) { + final Set chunks = region.getChunks(); + ChunkFilterBlock block = null; + for (BlockVector2 chunk : chunks) { + block = apply(block, filter, region, chunk.getX(), chunk.getZ()); + } + flush(); + return filter; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IQueueWrapper.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueWrapper.java similarity index 78% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IQueueWrapper.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueWrapper.java index 8c4957ce2..fe9ab65a2 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IQueueWrapper.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueWrapper.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.implementation; +package com.boydti.fawe.beta; import com.boydti.fawe.beta.IQueueExtent; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/NorthVector.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/NorthVector.java deleted file mode 100644 index a3ab572a1..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/NorthVector.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.boydti.fawe.beta; - -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; - -public class NorthVector extends BlockVector3 { - - private final BlockVector3 parent; - - public NorthVector(BlockVector3 parent) { - this.parent = parent; - } - -// @Override -// public BlockVector3 south(BlockVector3 orDefault) { -// return parent; -// } - - @Override - public int getX() { - return parent.getX(); - } - - @Override - public int getY() { - return parent.getY(); - } - - @Override - public int getZ() { - return parent.getZ(); - } - - @Override - public boolean setOrdinal(Extent orDefault, int ordinal) { - return orDefault.setBlock(this, BlockState.getFromOrdinal(ordinal)); - } - - @Override - public boolean setBlock(Extent orDefault, BlockState state) { - return orDefault.setBlock(this, state); - } - - @Override - public boolean setFullBlock(Extent orDefault, BaseBlock block) { - return orDefault.setBlock(this, block); - } - - @Override - public boolean setBiome(Extent orDefault, BiomeType biome) { - return orDefault.setBiome(getX(), getY(), getZ(), biome); - } - - @Override - public int getOrdinal(Extent orDefault) { - return getBlock(orDefault).getOrdinal(); - } - - @Override - public char getOrdinalChar(Extent orDefault) { - return (char) getOrdinal(orDefault); - } - - @Override - public BlockState getBlock(Extent orDefault) { - return orDefault.getBlock(this); - } - - @Override - public BaseBlock getFullBlock(Extent orDefault) { - return orDefault.getFullBlock(this); - } - - @Override - public CompoundTag getNbtData(Extent orDefault) { - return orDefault.getFullBlock(getX(), getY(), getZ()).getNbtData(); - } - - @Override - public BlockState getOrdinalBelow(Extent orDefault) { - return getStateRelative(orDefault, 0, -1, 0); - } - - @Override - public BlockState getStateAbove(Extent orDefault) { - return getStateRelative(orDefault, 0, 1, 0); - } - - @Override - public BlockState getStateRelativeY(Extent orDefault, int y) { - return getStateRelative(orDefault, 0, y, 0); - } - - public BlockState getStateRelative(Extent orDefault, int x, int y, int z) { - return getFullBlockRelative(orDefault, x, y, z).toBlockState(); - } - - public BaseBlock getFullBlockRelative(Extent orDefault, int x, int y, int z) { - return orDefault.getFullBlock(x + getX(), y + getY(), z + getZ()); - } -} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/BatchProcessorHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/BatchProcessorHolder.java deleted file mode 100644 index 7d0089be9..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/BatchProcessorHolder.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.boydti.fawe.beta.implementation; - -import com.boydti.fawe.beta.IBatchProcessor; - -public class BatchProcessorHolder implements IBatchProcessorHolder { - private IBatchProcessor processor = EmptyBatchProcessor.INSTANCE; - - @Override - public IBatchProcessor getProcessor() { - return processor; - } - - @Override - public void setProcessor(IBatchProcessor set) { - this.processor = set; - } - - @Override - public String toString() { - return super.toString() + "{" + getProcessor() + "}"; - } -} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java index fae95bc04..ed068a773 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java @@ -115,6 +115,11 @@ public class BitSetBlocks implements IChunkSet { return null; } + @Override + public BiomeType getBiomeType(int x, int z) { + return null; + } + @Override public Map getTiles() { return null; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java index 483625740..9f12d31f8 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java @@ -7,11 +7,12 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import java.util.Map; import java.util.Set; -public class CharBlocks implements IBlocks { +public abstract class CharBlocks implements IBlocks { public static final Section FULL = new Section() { @Override @@ -96,7 +97,7 @@ public class CharBlocks implements IBlocks { @Override public BlockState getBlock(int x, int y, int z) { - return BlockTypes.states[get(x, y, z)]; + return BlockTypesCache.states[get(x, y, z)]; } public char get(int x, int y, int z) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharGetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharGetBlocks.java index 84b857470..9304969f3 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharGetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharGetBlocks.java @@ -5,17 +5,18 @@ import com.boydti.fawe.beta.IChunkSet; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; public abstract class CharGetBlocks extends CharBlocks implements IChunkGet { @Override public BaseBlock getFullBlock(int x, int y, int z) { - return BlockTypes.states[get(x, y, z)].toBaseBlock(); + return BlockTypesCache.states[get(x, y, z)].toBaseBlock(); } @Override public BlockState getBlock(int x, int y, int z) { - return BlockTypes.states[get(x, y, z)]; + return BlockTypesCache.states[get(x, y, z)]; } @Override 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 2c470eef6..d33cee71e 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 @@ -13,6 +13,7 @@ import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import java.util.Collections; import java.util.HashSet; @@ -43,6 +44,11 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { return biomes; } + @Override + public BiomeType getBiomeType(int x, int z) { + return biomes[(z << 4) | x]; + } + @Override public Map getTiles() { return tiles == null ? Collections.emptyMap() : tiles; @@ -69,7 +75,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { @Override public BlockState getBlock(int x, int y, int z) { - return BlockTypes.states[get(x, y, z)]; + return BlockTypesCache.states[get(x, y, z)]; } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/DelegateChunkSet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/DelegateChunkSet.java similarity index 98% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/DelegateChunkSet.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/DelegateChunkSet.java index 94e202072..e288ebd9c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/DelegateChunkSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/DelegateChunkSet.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.implementation; +package com.boydti.fawe.beta.implementation.blocks; import com.boydti.fawe.beta.IChunkSet; import com.sk89q.jnbt.CompoundTag; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/FallbackChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java similarity index 99% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/FallbackChunkGet.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java index 7579bb5de..8cc1b5b9c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/FallbackChunkGet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.implementation; +package com.boydti.fawe.beta.implementation.blocks; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IBlocks; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/NullChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java similarity index 97% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/NullChunkGet.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java index 9bf8bc6a3..b6996623a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/NullChunkGet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.implementation; +package com.boydti.fawe.beta.implementation.blocks; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IBlocks; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkCache.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/cache/ChunkCache.java similarity index 84% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkCache.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/cache/ChunkCache.java index 69e306948..36c0fbd2b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkCache.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/cache/ChunkCache.java @@ -1,10 +1,12 @@ -package com.boydti.fawe.beta.implementation; +package com.boydti.fawe.beta.implementation.cache; +import com.boydti.fawe.beta.IChunkCache; import com.boydti.fawe.beta.Trimable; import com.boydti.fawe.util.MathMan; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.objects.ObjectIterator; + import java.lang.ref.WeakReference; public class ChunkCache implements IChunkCache { @@ -12,11 +14,18 @@ public class ChunkCache implements IChunkCache { protected final Long2ObjectLinkedOpenHashMap> getCache; private final IChunkCache delegate; - protected ChunkCache(IChunkCache delegate) { + public ChunkCache(IChunkCache delegate) { this.getCache = new Long2ObjectLinkedOpenHashMap<>(); this.delegate = delegate; } + /** + * Get or create the IGetBlocks + * + * @param index chunk index {@link com.boydti.fawe.util.MathMan#pairInt(int, int)} + * @param provider used to create if it isn't already cached + * @return cached IGetBlocks + */ @Override public synchronized T get(int x, int z) { long pair = MathMan.pairInt(x, z); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/cache/preloader/AsyncPreloader.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/cache/preloader/AsyncPreloader.java new file mode 100644 index 000000000..14933971a --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/cache/preloader/AsyncPreloader.java @@ -0,0 +1,110 @@ +package com.boydti.fawe.beta.implementation.cache.preloader; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.object.collection.MutablePair; +import com.boydti.fawe.util.FaweTimer; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.World; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + + +public class AsyncPreloader implements Preloader, Runnable { + private final ConcurrentHashMap>> update; + + public AsyncPreloader() { + this.update = new ConcurrentHashMap<>(); + Fawe.get().getQueueHandler().async(this); + } + + @Override + public void cancel(Player player) { + cancelAndGet(player); + } + + private MutablePair> cancelAndGet(Player player) { + MutablePair> existing = update.get(player.getUniqueId()); + if (existing != null) { + existing.setValue(null); + } + return existing; + } + + @Override + public void update(Player player) { + LocalSession session = WorldEdit.getInstance().getSessionManager().getIfPresent(player); + if (session == null) return; + World world = player.getWorld(); + MutablePair> existing = cancelAndGet(player); + try { + Region region = session.getSelection(world); + if (!(region instanceof CuboidRegion) || region.getArea() > 50466816) { + // TOO LARGE or NOT CUBOID + return; + } + if (existing == null) { + MutablePair> previous = update.putIfAbsent(player.getUniqueId(), existing = new MutablePair<>()); + if (previous != null) { + existing = previous; + } + synchronized (existing) { // Ensure key & value are mutated together + existing.setKey(world); + existing.setValue(region.getChunks()); + } + synchronized (update) { + update.notify(); + } + } + } catch (IncompleteRegionException ignore){} + } + + @Override + public void run() { + FaweTimer timer = Fawe.get().getTimer(); + try { + while (true) { + if (!update.isEmpty()) { + if (timer.getTPS() > 19) { + Iterator>>> plrIter = update.entrySet().iterator(); + Map.Entry>> entry = plrIter.next(); + MutablePair> pair = entry.getValue(); + World world = pair.getKey(); + Set chunks = pair.getValue(); + if (chunks != null) { + Iterator chunksIter = chunks.iterator(); + while (chunksIter.hasNext() && pair.getValue() == chunks) { // Ensure the queued load is still valid + BlockVector2 chunk = chunksIter.next(); + queueLoad(world, chunk); + } + } + plrIter.remove(); + } else { + Thread.sleep(1000); + } + } else { + synchronized (update) { + update.wait(); + } + } + } + } catch (InterruptedException e) { + e.printStackTrace(); + return; + } + } + + public void queueLoad(World world, BlockVector2 chunk) { + world.checkLoadedChunk(BlockVector3.at(chunk.getX() << 4, 0, chunk.getZ() << 4)); + } +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/cache/preloader/Preloader.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/cache/preloader/Preloader.java new file mode 100644 index 000000000..db32604be --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/cache/preloader/Preloader.java @@ -0,0 +1,9 @@ +package com.boydti.fawe.beta.implementation.cache.preloader; + +import com.sk89q.worldedit.entity.Player; + +public interface Preloader { + void cancel(Player player); + + void update(Player player); +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/ChunkHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java similarity index 98% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/ChunkHolder.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java index d17cff999..91db98cc0 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java @@ -1,7 +1,7 @@ -package com.boydti.fawe.beta.implementation.holder; +package com.boydti.fawe.beta.implementation.chunk; import com.boydti.fawe.FaweCache; -import com.boydti.fawe.beta.ChunkFilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.FilterBlockMask; import com.boydti.fawe.beta.Flood; @@ -59,11 +59,6 @@ public class ChunkHolder> implements IChunk { return delegate; } - @Override - public IQueueExtent getQueue() { - return extent; - } - @Override public boolean setTile(int x, int y, int z, CompoundTag tag) { return false; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/DelegateChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/DelegateChunk.java similarity index 90% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/DelegateChunk.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/DelegateChunk.java index c5f17ba16..7cc7bfb01 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/DelegateChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/DelegateChunk.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.implementation.holder; +package com.boydti.fawe.beta.implementation.chunk; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IDelegateChunk; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/FinalizedChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/FinalizedChunk.java similarity index 93% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/FinalizedChunk.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/FinalizedChunk.java index cc0d59fdb..539923000 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/FinalizedChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/FinalizedChunk.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.implementation.holder; +package com.boydti.fawe.beta.implementation.chunk; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IQueueExtent; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java new file mode 100644 index 000000000..c93d0b43b --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java @@ -0,0 +1,137 @@ +package com.boydti.fawe.beta.implementation.chunk; + +import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; +import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.FilterBlockMask; +import com.boydti.fawe.beta.Flood; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.beta.IQueueExtent; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +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.BlockTypes; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Future; + +public enum NullChunk implements IChunk { + INSTANCE; + + @Override + public void init(IQueueExtent extent, int x, int z) {} + + @Override + public int getX() { + return 0; + } + + @Override + public int getZ() { + return 0; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public Future call() { + return null; + } + + @Override + public void filterBlocks(Filter filter, ChunkFilterBlock block, @Nullable Region region) { + + } + + @Override + public void flood(Flood flood, FilterBlockMask mask, ChunkFilterBlock block) { + + } + + @Override + public boolean setBiome(int x, int y, int z, BiomeType biome) { + return false; + } + + @Override + public boolean setTile(int x, int y, int z, CompoundTag tag) { + return false; + } + + @Override + public boolean setBlock(int x, int y, int z, BlockStateHolder block) { + return false; + } + + @Override + public BiomeType getBiomeType(int x, int z) { + return null; + } + + @Override + public boolean hasSection(int layer) { + return false; + } + + @Override + public char[] getArray(int layer) { + return null; + } + + @Override + public BlockState getBlock(int x, int y, int z) { + return BlockTypes.__RESERVED__.getDefaultState(); + } + + @Override + public BaseBlock getFullBlock(int x, int y, int z) { + return BlockTypes.__RESERVED__.getDefaultState().toBaseBlock(); + } + + @Override + public CompoundTag getTag(int x, int y, int z) { + return null; + } + + @Override + public Map getTiles() { + return Collections.emptyMap(); + } + + @Override + public Set getEntities() { + return Collections.emptySet(); + } + + @Override + public char[] load(int layer) { + return null; + } + + @Override + public CompoundTag getEntity(UUID uuid) { + return null; + } + + @Override + public Future call(IChunkSet set, Runnable finalize) { + return null; + } + + @Override + public boolean trim(boolean aggressive) { + return true; + } +} + diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/ReferenceChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ReferenceChunk.java similarity index 94% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/ReferenceChunk.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ReferenceChunk.java index 4e5c799c8..4c8190066 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/ReferenceChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ReferenceChunk.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.implementation.holder; +package com.boydti.fawe.beta.implementation.chunk; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IDelegateChunk; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/SoftChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/SoftChunk.java similarity index 90% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/SoftChunk.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/SoftChunk.java index 29ea78386..ee69d6452 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/SoftChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/SoftChunk.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.implementation.holder; +package com.boydti.fawe.beta.implementation.chunk; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IQueueExtent; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/WeakChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/WeakChunk.java similarity index 67% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/WeakChunk.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/WeakChunk.java index a2c43733d..6b7d24561 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/WeakChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/WeakChunk.java @@ -1,16 +1,10 @@ -package com.boydti.fawe.beta.implementation.holder; +package com.boydti.fawe.beta.implementation.chunk; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IQueueExtent; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.world.biome.BiomeType; import java.lang.ref.Reference; import java.lang.ref.WeakReference; -import java.util.Map; -import java.util.Set; -import java.util.UUID; /** * A {@link ReferenceChunk} using {@link WeakReference} to hold the chunk. diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/filters/ArrayImageMask.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/ArrayImageMask.java similarity index 85% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/filters/ArrayImageMask.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/ArrayImageMask.java index bc8bb0f46..e47b25a27 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/filters/ArrayImageMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/ArrayImageMask.java @@ -1,6 +1,6 @@ -package com.boydti.fawe.beta.filters; +package com.boydti.fawe.beta.implementation.filter; -import com.boydti.fawe.beta.FilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.FilterBlock; import com.boydti.fawe.beta.FilterBlockMask; import java.awt.image.BufferedImage; import java.util.concurrent.ThreadLocalRandom; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/filters/CountFilter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/CountFilter.java similarity index 82% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/filters/CountFilter.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/CountFilter.java index 111502781..fa16fee03 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/filters/CountFilter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/CountFilter.java @@ -1,6 +1,6 @@ -package com.boydti.fawe.beta.filters; +package com.boydti.fawe.beta.implementation.filter; -import com.boydti.fawe.beta.FilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.FilterBlock; public class CountFilter extends ForkedFilter { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/filters/DistrFilter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/DistrFilter.java similarity index 78% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/filters/DistrFilter.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/DistrFilter.java index 78a1f654b..581a3554a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/filters/DistrFilter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/DistrFilter.java @@ -1,12 +1,14 @@ -package com.boydti.fawe.beta.filters; +package com.boydti.fawe.beta.implementation.filter; -import com.boydti.fawe.beta.FilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.FilterBlock; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.function.mask.ABlockMask; import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -14,7 +16,7 @@ import java.util.List; public class DistrFilter extends ForkedFilter { - private final int[] counter = new int[BlockTypes.states.length]; + private final int[] counter = new int[BlockTypesCache.states.length]; public DistrFilter() { super(null); @@ -45,7 +47,7 @@ public class DistrFilter extends ForkedFilter { int total = 0; for (int i = 0; i < counter.length; i++) { int value = counter[i]; - if (value != 0 && mask.test(BlockTypes.states[i])) { + if (value != 0 && mask.test(BlockTypesCache.states[i])) { total += value; } } @@ -61,7 +63,7 @@ public class DistrFilter extends ForkedFilter { for (int i = 0; i < counter.length; i++) { final int count = counter[i]; if (count != 0) { - distribution.add(new Countable<>(BlockTypes.states[i], count)); + distribution.add(new Countable<>(BlockTypesCache.states[i], count)); } } Collections.sort(distribution); @@ -70,18 +72,18 @@ public class DistrFilter extends ForkedFilter { public List> getTypeDistribution() { final List> distribution = new ArrayList<>(); - int[] typeCounter = new int[BlockTypes.values.length]; + int[] typeCounter = new int[BlockTypesCache.values.length]; for (int i = 0; i < counter.length; i++) { final int count = counter[i]; if (count != 0) { - BlockState state = BlockTypes.states[i]; + BlockState state = BlockTypesCache.states[i]; typeCounter[state.getBlockType().getInternalId()] += count; } } for (int i = 0; i < typeCounter.length; i++) { final int count = typeCounter[i]; if (count != 0) { - distribution.add(new Countable<>(BlockTypes.values[i], count)); + distribution.add(new Countable<>(BlockTypesCache.values[i], count)); } } Collections.sort(distribution); @@ -89,10 +91,10 @@ public class DistrFilter extends ForkedFilter { } public void print(Actor actor, long size) { - for (Countable c : getDistribution()) { + for (Countable c : getDistribution()) { final String name = c.getID().toString(); final String str = String.format("%-7s (%.3f%%) %s", - c.getAmount(), + String.valueOf(c.getAmount()), c.getAmount() / (double) size * 100, name); actor.print(str); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/filters/ForkedFilter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/ForkedFilter.java similarity index 94% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/filters/ForkedFilter.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/ForkedFilter.java index 64827ffd0..dc2e33f7c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/filters/ForkedFilter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/ForkedFilter.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.filters; +package com.boydti.fawe.beta.implementation.filter; import com.boydti.fawe.beta.Filter; import java.util.Map; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/filters/SetFilter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/SetFilter.java similarity index 74% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/filters/SetFilter.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/SetFilter.java index 0ba970ea5..153313469 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/filters/SetFilter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/SetFilter.java @@ -1,7 +1,7 @@ -package com.boydti.fawe.beta.filters; +package com.boydti.fawe.beta.implementation.filter; import com.boydti.fawe.beta.Filter; -import com.boydti.fawe.beta.FilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.FilterBlock; import com.sk89q.worldedit.world.block.BlockState; public class SetFilter implements Filter { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/AbstractFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/AbstractFilterBlock.java new file mode 100644 index 000000000..0a40471b6 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/AbstractFilterBlock.java @@ -0,0 +1,94 @@ +package com.boydti.fawe.beta.implementation.filter.block; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector3; +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 javax.annotation.Nullable; + +public abstract class AbstractFilterBlock extends FilterBlock { + + public abstract BaseBlock getFullBlock(); + public abstract void setFullBlock(BaseBlock block); + public abstract BlockVector3 getPosition(); + + @Override + public Extent getExtent() { + return this; + } + + @Override + public int getX() { + return getPosition().getX(); + } + + @Override + public int getY() { + return getPosition().getY(); + } + + @Override + public int getZ() { + return getPosition().getZ(); + } + + @Override + public int getOrdinal() { + return getBlock().getOrdinal(); + } + + @Override + public void setOrdinal(int ordinal) { + setBlock(BlockState.getFromOrdinal(ordinal)); + } + + @Override + public BlockState getBlock() { + return getFullBlock().toBlockState(); + } + + @Override + public void setBlock(BlockState state) { + setFullBlock(state.toBaseBlock(getBlock().getNbtData())); + } + + @Override + public CompoundTag getNbtData() { + return getFullBlock().getNbtData(); + } + + @Override + public void setNbtData(@Nullable CompoundTag nbtData) { + setFullBlock(getFullBlock().toBaseBlock(nbtData)); + } + + @Override + public BlockVector3 getMinimumPoint() { + return BlockVector3.at(getX(), getY(), getZ()); + } + + @Override + public BlockVector3 getMaximumPoint() { + return BlockVector3.at(getX(), getY(), getZ()); + } + + @Override + public > boolean setBlock(int x, int y, int z, T block) + throws WorldEditException { + if (x == this.getX() && y == this.getY() && z == this.getZ()) { + setFullBlock(block.toBaseBlock()); + return true; + } + return getExtent().setBlock(x,y, z, block); + } + + @Override + public boolean setBiome(int x, int y, int z, BiomeType biome) { + return getExtent().setBiome(x, y, z,biome); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/SingleFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/AbstractSingleFilterBlock.java similarity index 74% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/SingleFilterBlock.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/AbstractSingleFilterBlock.java index 631e14357..45c972b6b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/SingleFilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/AbstractSingleFilterBlock.java @@ -1,25 +1,21 @@ -package com.boydti.fawe.beta; +package com.boydti.fawe.beta.implementation.filter.block; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; 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 javax.annotation.Nullable; -public class SingleFilterBlock extends FilterBlock { +public abstract class AbstractSingleFilterBlock extends FilterBlock { private BaseBlock block; - private int x, y, z; - public SingleFilterBlock init(int x, int y, int z, BaseBlock block) { - this.x = x; - this.y = y; - this.z = z; + public AbstractSingleFilterBlock init(BaseBlock block) { this.block = block; return this; } @@ -69,35 +65,20 @@ public class SingleFilterBlock extends FilterBlock { block = block.toBaseBlock(nbtData); } - @Override - public int getX() { - return x; - } - - @Override - public int getY() { - return y; - } - - @Override - public int getZ() { - return z; - } - @Override public BlockVector3 getMinimumPoint() { - return BlockVector3.at(x, y, z); + return BlockVector3.at(getX(), getY(), getZ()); } @Override public BlockVector3 getMaximumPoint() { - return BlockVector3.at(x, y, z); + return BlockVector3.at(getX(), getY(), getZ()); } @Override public > boolean setBlock(int x, int y, int z, T block) throws WorldEditException { - if (x == this.x && y == this.y && z == this.z) { + if (x == this.getX() && y == this.getY() && z == this.getZ()) { setFullBlock(block.toBaseBlock()); return true; } @@ -106,6 +87,6 @@ public class SingleFilterBlock extends FilterBlock { @Override public boolean setBiome(int x, int y, int z, BiomeType biome) { - return getExtent().setBiome(x,y, z,biome); + return getExtent().setBiome(x, y, z,biome); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/ArrayFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/ArrayFilterBlock.java similarity index 92% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/ArrayFilterBlock.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/ArrayFilterBlock.java index 3fd3c0a07..851a4ec92 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/ArrayFilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/ArrayFilterBlock.java @@ -1,15 +1,16 @@ -package com.boydti.fawe.beta; +package com.boydti.fawe.beta.implementation.filter.block; +import com.boydti.fawe.beta.Filter; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.math.BlockVector2; -import com.sk89q.worldedit.math.BlockVector3; 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.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; + import javax.annotation.Nullable; public class ArrayFilterBlock extends SimpleFilterBlock { @@ -52,7 +53,7 @@ public class ArrayFilterBlock extends SimpleFilterBlock { @Override public BlockState getBlock() { - return BlockTypes.states[ordinal]; + return BlockTypesCache.states[ordinal]; } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/CharFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/CharFilterBlock.java similarity index 96% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/CharFilterBlock.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/CharFilterBlock.java index eeabb1713..2dbd271c5 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/CharFilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/CharFilterBlock.java @@ -1,7 +1,13 @@ -package com.boydti.fawe.beta; +package com.boydti.fawe.beta.implementation.filter.block; -import static com.sk89q.worldedit.world.block.BlockTypes.states; +import static com.sk89q.worldedit.world.block.BlockTypesCache.states; +import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.FilterBlockMask; +import com.boydti.fawe.beta.Flood; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; @@ -13,6 +19,7 @@ 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.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.registry.BlockMaterial; import javax.annotation.Nullable; @@ -225,7 +232,7 @@ public class CharFilterBlock extends ChunkFilterBlock { @Override public final BlockState getBlock() { final int ordinal = getArr[index]; - return BlockTypes.states[ordinal]; + return BlockTypesCache.states[ordinal]; } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/ChunkFilterBlock.java similarity index 72% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFilterBlock.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/ChunkFilterBlock.java index d87a536f5..a254b7f2f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/ChunkFilterBlock.java @@ -1,5 +1,10 @@ -package com.boydti.fawe.beta; +package com.boydti.fawe.beta.implementation.filter.block; +import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.FilterBlockMask; +import com.boydti.fawe.beta.Flood; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.regions.Region; @@ -15,7 +20,7 @@ public abstract class ChunkFilterBlock extends SimpleFilterBlock { int layer); public abstract void flood(IChunkGet iget, IChunkSet iset, int layer, - Flood flood, FilterBlockMask mask); + Flood flood, FilterBlockMask mask); public abstract void filter(Filter filter, int x, int y, int z); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/DelegateFilter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/DelegateFilter.java similarity index 66% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/DelegateFilter.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/DelegateFilter.java index 21dbd8291..f51d85b3f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/DelegateFilter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/DelegateFilter.java @@ -1,4 +1,7 @@ -package com.boydti.fawe.beta; +package com.boydti.fawe.beta.implementation.filter.block; + +import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.IDelegateFilter; public abstract class DelegateFilter implements IDelegateFilter { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/DelegateFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/DelegateFilterBlock.java similarity index 99% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/DelegateFilterBlock.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/DelegateFilterBlock.java index 61a0d6b4b..1e5985aa8 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/DelegateFilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/DelegateFilterBlock.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta; +package com.boydti.fawe.beta.implementation.filter.block; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.MaxChangedBlocksException; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/ExtentFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/ExtentFilterBlock.java new file mode 100644 index 000000000..4397f94dd --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/ExtentFilterBlock.java @@ -0,0 +1,35 @@ +package com.boydti.fawe.beta.implementation.filter.block; + +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.block.BaseBlock; + +public class ExtentFilterBlock extends AbstractFilterBlock { + private final Extent extent; + private BlockVector3 pos; + + public ExtentFilterBlock(Extent extent) { + this.extent = extent; + this.pos = BlockVector3.ZERO; + } + + public ExtentFilterBlock init(BlockVector3 pos) { + this.pos = pos; + return this; + } + + @Override + public BaseBlock getFullBlock() { + return pos.getFullBlock(extent); + } + + @Override + public void setFullBlock(BaseBlock block) { + pos.setFullBlock(extent, block); + } + + @Override + public BlockVector3 getPosition() { + return pos; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/FilterBlock.java similarity index 98% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlock.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/FilterBlock.java index 36a4a04d9..c432a233c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/FilterBlock.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta; +package com.boydti.fawe.beta.implementation.filter.block; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.StringTag; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/SimpleFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/SimpleFilterBlock.java similarity index 84% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/SimpleFilterBlock.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/SimpleFilterBlock.java index 27cc60afd..a709f337a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/SimpleFilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/SimpleFilterBlock.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta; +package com.boydti.fawe.beta.implementation.filter.block; import com.sk89q.worldedit.extent.Extent; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/SingleFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/SingleFilterBlock.java new file mode 100644 index 000000000..4c2e41244 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/SingleFilterBlock.java @@ -0,0 +1,31 @@ +package com.boydti.fawe.beta.implementation.filter.block; + +import com.sk89q.worldedit.world.block.BaseBlock; + +public class SingleFilterBlock extends AbstractSingleFilterBlock { + + private int x, y, z; + + public SingleFilterBlock init(int x, int y, int z, BaseBlock block) { + this.x = x; + this.y = y; + this.z = z; + super.init(block); + return this; + } + + @Override + public int getX() { + return x; + } + + @Override + public int getY() { + return y; + } + + @Override + public int getZ() { + return z; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/VectorSingleFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/VectorSingleFilterBlock.java new file mode 100644 index 000000000..c96d714cc --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/VectorSingleFilterBlock.java @@ -0,0 +1,32 @@ +package com.boydti.fawe.beta.implementation.filter.block; + +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.block.BaseBlock; + +public class VectorSingleFilterBlock extends AbstractSingleFilterBlock { + private final BlockVector3 mutable; + + public VectorSingleFilterBlock(BlockVector3 mutable) { + this.mutable = mutable; + } + + public VectorSingleFilterBlock init(BaseBlock block) { + super.init(block); + return this; + } + + @Override + public int getX() { + return mutable.getX(); + } + + @Override + public int getY() { + return mutable.getY(); + } + + @Override + public int getZ() { + return mutable.getZ(); + } +} 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 new file mode 100644 index 000000000..822b18a84 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/packet/ChunkPacket.java @@ -0,0 +1,125 @@ +package com.boydti.fawe.beta.implementation.packet; + +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 java.util.HashMap; +import java.util.function.Function; +import java.util.function.Supplier; + +public class ChunkPacket implements Function, Supplier { + + private final boolean full; + private final Supplier chunkSupplier; + private IBlocks chunk; + + private int chunkX; + private int chunkZ; + private byte[] sectionBytes; + private Object nativePacket; + + public ChunkPacket(int chunkX, int chunkZ, Supplier chunkSupplier, boolean replaceAllSections) { + this.chunkX = chunkX; + this.chunkZ = chunkZ; + this.chunkSupplier = chunkSupplier; + this.full = replaceAllSections; + } + + public int getChunkX() { + return chunkX; + } + + public int getChunkZ() { + return chunkZ; + } + + public synchronized void setPosition(int chunkX, int chunkZ) { + this.chunkX = chunkX; + this.chunkZ = chunkZ; + nativePacket = null; + } + + public boolean isFull() { + return full; + } + + public IBlocks getChunk() { + if (this.chunk == null) { + synchronized (this) { + if (this.chunk == null) { + this.chunk = chunkSupplier.get(); + } + } + } + return chunk; + } + + private byte[] getSectionBytes() { + byte[] tmp = this.sectionBytes; + if (tmp == null) { + synchronized (this) { + if (sectionBytes == null) { + sectionBytes = getChunk().toByteArray(FaweCache.IMP.BYTE_BUFFER_8192.get(), this.full); + } + tmp = sectionBytes; + } + } + return tmp; + } + + public Object getNativePacket() { + return nativePacket; + } + + public void setNativePacket(Object nativePacket) { + this.nativePacket = nativePacket; + } + + @Override + @Deprecated + public byte[] get() { + return apply(FaweCache.IMP.BYTE_BUFFER_8192.get()); + } + + public CompoundTag getHeightMap() { + HashMap map = new HashMap<>(); + map.put("MOTION_BLOCKING", new long[36]); + CompoundTag tag = FaweCache.IMP.asTag(map); + // TODO + return tag; + } + + @Override + public byte[] apply(byte[] buffer) { + try { + byte[] sectionBytes = getSectionBytes(); + + FastByteArrayOutputStream baos = new FastByteArrayOutputStream(buffer); + FaweOutputStream fos = new FaweOutputStream(baos); + + fos.writeInt(this.chunkX); + fos.writeInt(this.chunkZ); + + fos.writeBoolean(this.full); + + fos.writeVarInt(getChunk().getBitMask()); + + + fos.writeNBT("", getHeightMap()); + + fos.writeVarInt(sectionBytes.length); + fos.write(sectionBytes); + // TODO entities / NBT +// Set entities = chunk.getEntities(); +// Map tiles = chunk.getTiles(); + fos.writeVarInt(0); // (Entities / NBT) + return baos.toByteArray(); + } catch (Throwable e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java new file mode 100644 index 000000000..07488154b --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java @@ -0,0 +1,31 @@ +package com.boydti.fawe.beta.implementation.processors; + +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; + +public class BatchProcessorHolder implements IBatchProcessorHolder { + private IBatchProcessor processor = EmptyBatchProcessor.INSTANCE; + + @Override + public IBatchProcessor getProcessor() { + return processor; + } + + @Override + public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { + return getProcessor().processSet(chunk, get, set); + } + + @Override + public void setProcessor(IBatchProcessor set) { + this.processor = set; + } + + @Override + public String toString() { + IBatchProcessor tmp = getProcessor(); + return super.toString() + "{" + (tmp == this ? "" : getProcessor()) + "}"; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ChunkSendProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ChunkSendProcessor.java new file mode 100644 index 000000000..3aac986cb --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ChunkSendProcessor.java @@ -0,0 +1,44 @@ +package com.boydti.fawe.beta.implementation.processors; + +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.beta.implementation.packet.ChunkPacket; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.world.World; + +import java.util.function.Supplier; +import java.util.stream.Stream; + +public class ChunkSendProcessor implements IBatchProcessor { + private final Supplier> players; + private final World world; + + public ChunkSendProcessor(World world, Supplier> players) { + this.players = players; + this.world = world; + } + + @Override + public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { + int chunkX = chunk.getX(); + int chunkZ = chunk.getZ(); + boolean replaceAll = true; + ChunkPacket packet = new ChunkPacket(chunkX, chunkZ, () -> set, replaceAll); + Stream stream = this.players.get(); + if (stream == null) { + world.sendFakeChunk(null, packet); + } else { + stream.filter(player -> player.getWorld().equals(world)) + .forEach(player -> world.sendFakeChunk(player, packet)); + } + return set; + } + + @Override + public Extent construct(Extent child) { + return null; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/EmptyBatchProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/EmptyBatchProcessor.java similarity index 91% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/EmptyBatchProcessor.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/EmptyBatchProcessor.java index 297b50cfa..c68eadb5e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/EmptyBatchProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/EmptyBatchProcessor.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.implementation; +package com.boydti.fawe.beta.implementation.processors; import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.IChunk; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IBatchProcessorHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/IBatchProcessorHolder.java similarity index 95% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IBatchProcessorHolder.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/IBatchProcessorHolder.java index 6712c5ec4..1a2770cd4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IBatchProcessorHolder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/IBatchProcessorHolder.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.implementation; +package com.boydti.fawe.beta.implementation.processors; import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.IChunk; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/LimitExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/LimitExtent.java new file mode 100644 index 000000000..d7ec08ec5 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/LimitExtent.java @@ -0,0 +1,634 @@ +package com.boydti.fawe.beta.implementation.processors; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.beta.implementation.filter.block.ExtentFilterBlock; +import com.boydti.fawe.object.FaweLimit; +import com.boydti.fawe.object.changeset.FaweChangeSet; +import com.boydti.fawe.object.exception.FaweException; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.PassthroughExtent; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.function.generator.GenBase; +import com.sk89q.worldedit.function.generator.Resource; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.util.Countable; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; +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.block.BlockTypes; + +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +public class LimitExtent extends PassthroughExtent { + private final FaweLimit limit; + + /** + * Create a new instance. + * + * @param extent the extent + */ + public LimitExtent(Extent extent, FaweLimit limit) { + super(extent); + this.limit = limit; + } + + public List getEntities(Region region) { + limit.THROW_MAX_CHECKS(region.getArea()); + try { + return getExtent().getEntities(region); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return Collections.emptyList(); + } + } + + public List getEntities() { + limit.THROW_MAX_CHECKS(); + try { + return getExtent().getEntities(); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return Collections.emptyList(); + } + } + + @Nullable + public Entity createEntity(Location location, BaseEntity entity) { + limit.THROW_MAX_CHANGES(); + limit.THROW_MAX_ENTITIES(); + try { + return getExtent().createEntity(location, entity); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return null; + } + } + + @Override + @Nullable + public void removeEntity(int x, int y, int z, UUID uuid) { + limit.THROW_MAX_CHANGES(); + limit.THROW_MAX_ENTITIES(); + try { + getExtent().removeEntity(x, y, z, uuid); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + } + } + + @Override + public boolean regenerateChunk(int x, int z, @Nullable BiomeType type, @Nullable Long seed) { + limit.THROW_MAX_CHANGES(Character.MAX_VALUE); + try { + return getExtent().regenerateChunk(x, z, type, seed); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return false; + } + } + + @Override + public int getHighestTerrainBlock(int x, int z, int minY, int maxY) { + limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT); + try { + return getExtent().getHighestTerrainBlock(x, z, minY, maxY); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public int getHighestTerrainBlock(int x, int z, int minY, int maxY, Mask filter) { + limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT); + try { + return getExtent().getHighestTerrainBlock(x, z, minY, maxY, filter); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) { + limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT); + try { + return getExtent().getNearestSurfaceLayer(x, z, y, minY, maxY); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, boolean ignoreAir) { + limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT); + try { + return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, ignoreAir); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) { + limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT); + try { + return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax) { + limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT); + try { + return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, Mask mask) { + limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT); + try { + return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, mask); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, boolean ignoreAir) { + limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT); + try { + return getExtent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, ignoreAir); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public void addCaves(Region region) throws WorldEditException { + limit.THROW_MAX_CHECKS(region.getArea()); + limit.THROW_MAX_CHANGES(region.getArea()); + try { + getExtent().addCaves(region); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + } + } + + @Override + public void generate(Region region, GenBase gen) throws WorldEditException { + limit.THROW_MAX_CHECKS(region.getArea()); + limit.THROW_MAX_CHANGES(region.getArea()); + try { + getExtent().generate(region, gen); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + } + } + + @Override + public void addSchems(Region region, Mask mask, List clipboards, int rarity, boolean rotate) throws WorldEditException { + limit.THROW_MAX_CHECKS(region.getArea()); + limit.THROW_MAX_CHANGES(region.getArea()); + try { + getExtent().addSchems(region, mask, clipboards, rarity, rotate); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + } + } + + @Override + public void spawnResource(Region region, Resource gen, int rarity, int frequency) throws WorldEditException { + limit.THROW_MAX_CHECKS(region.getArea()); + limit.THROW_MAX_CHANGES(region.getArea()); + try { + getExtent().spawnResource(region, gen, rarity, frequency); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + } + } + + @Override + public void addOre(Region region, Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws WorldEditException { + limit.THROW_MAX_CHECKS(region.getArea()); + limit.THROW_MAX_CHANGES(region.getArea()); + try { + getExtent().addOre(region, mask, material, size, frequency, rarity, minY, maxY); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + } + } + + @Override + public void addOres(Region region, Mask mask) throws WorldEditException { + limit.THROW_MAX_CHECKS(region.getArea()); + limit.THROW_MAX_CHANGES(region.getArea()); + try { + getExtent().addOres(region, mask); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + } + } + + @Override + public List> getBlockDistribution(Region region) { + limit.THROW_MAX_CHECKS(region.getArea()); + try { + return getExtent().getBlockDistribution(region); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return Collections.emptyList(); + } + } + + @Override + public List> getBlockDistributionWithData(Region region) { + limit.THROW_MAX_CHECKS(region.getArea()); + try { + return getExtent().getBlockDistributionWithData(region); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return Collections.emptyList(); + } + } + + @Override + public int countBlocks(Region region, Set searchBlocks) { + limit.THROW_MAX_CHECKS(region.getArea()); + try { + return getExtent().countBlocks(region, searchBlocks); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public int countBlocks(Region region, Mask searchMask) { + limit.THROW_MAX_CHECKS(region.getArea()); + try { + return getExtent().countBlocks(region, searchMask); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public > int setBlocks(Region region, B block) throws MaxChangedBlocksException { + limit.THROW_MAX_CHANGES(region.getArea()); + try { + return getExtent().setBlocks(region, block); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException { + limit.THROW_MAX_CHANGES(region.getArea()); + try { + return getExtent().setBlocks(region, pattern); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public > int replaceBlocks(Region region, Set filter, B replacement) throws MaxChangedBlocksException { + limit.THROW_MAX_CHECKS(region.getArea()); + limit.THROW_MAX_CHANGES(region.getArea()); + try { + return getExtent().replaceBlocks(region, filter, replacement); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public int replaceBlocks(Region region, Set filter, Pattern pattern) throws MaxChangedBlocksException { + limit.THROW_MAX_CHECKS(region.getArea()); + limit.THROW_MAX_CHANGES(region.getArea()); + try { + return getExtent().replaceBlocks(region, filter, pattern); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public int replaceBlocks(Region region, Mask mask, Pattern pattern) throws MaxChangedBlocksException { + limit.THROW_MAX_CHECKS(region.getArea()); + limit.THROW_MAX_CHANGES(region.getArea()); + try { + return getExtent().replaceBlocks(region, mask, pattern); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public int center(Region region, Pattern pattern) throws MaxChangedBlocksException { + limit.THROW_MAX_CHECKS(region.getArea()); + limit.THROW_MAX_CHANGES(region.getArea()); + try { + return getExtent().center(region, pattern); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public int setBlocks(Set vset, Pattern pattern) { + limit.THROW_MAX_CHANGES(vset.size()); + try { + return getExtent().setBlocks(vset, pattern); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return 0; + } + } + + @Override + public T apply(Region region, T filter) { + limit.THROW_MAX_CHECKS(region.getArea()); + limit.THROW_MAX_CHANGES(region.getArea()); + try { + return getExtent().apply(region, filter); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return filter; + } + } + + @Override + public T apply(Iterable positions, T filter) { + int size; + if (positions instanceof Collection) { + size = ((Collection) positions).size(); + } else if (positions instanceof Region) { + BlockVector3 dim = ((Region) positions).getDimensions(); + size = dim.getX() * dim.getY() * dim.getZ(); + } else if (positions instanceof Extent) { + BlockVector3 min = ((Extent) positions).getMinimumPoint(); + BlockVector3 max = ((Extent) positions).getMinimumPoint(); + BlockVector3 dim = max.subtract(min).add(BlockVector3.ONE); + size = dim.getX() * dim.getY() * dim.getZ(); + } else { + ExtentFilterBlock block = new ExtentFilterBlock(this); + for (BlockVector3 pos : positions) { + limit.THROW_MAX_CHECKS(); + filter.applyBlock(block.init(pos)); + } + return filter; + } + limit.THROW_MAX_CHECKS(size); + limit.THROW_MAX_CHANGES(size); + try { + return getExtent().apply(positions, filter); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return filter; + } + } + + public BlockState getBlock(BlockVector3 position) { + limit.THROW_MAX_CHECKS(); + try { + return getExtent().getBlock(position); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return BlockTypes.AIR.getDefaultState(); + } + } + + public BlockState getBlock(int x, int y, int z) { + limit.THROW_MAX_CHECKS(); + try { + return getExtent().getBlock(x, y, z); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return BlockTypes.AIR.getDefaultState(); + } + } + + @Override + public BaseBlock getFullBlock(BlockVector3 position) { + limit.THROW_MAX_CHECKS(); + try { + return getExtent().getFullBlock(position); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return BlockTypes.AIR.getDefaultState().toBaseBlock(); + } + } + + public BaseBlock getFullBlock(int x, int y, int z) { + limit.THROW_MAX_CHECKS(); + try { + return getExtent().getFullBlock(x, y, z); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return BlockTypes.AIR.getDefaultState().toBaseBlock(); + } + } + + public BiomeType getBiome(BlockVector2 position) { + limit.THROW_MAX_CHECKS(); + try { + return getExtent().getBiome(position); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return BiomeTypes.FOREST; + } + } + + public BiomeType getBiomeType(int x, int z) { + limit.THROW_MAX_CHECKS(); + try { + return getExtent().getBiomeType(x, z); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return BiomeTypes.FOREST; + } + } + + @Deprecated + public > boolean setBlock(BlockVector3 position, T block) throws WorldEditException { + limit.THROW_MAX_CHANGES(); + if (block.hasNbtData()) limit.MAX_BLOCKSTATES(); + try { + return getExtent().setBlock(position, block); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return false; + } + } + + public > boolean setBlock(int x, int y, int z, T block) throws WorldEditException { + limit.THROW_MAX_CHANGES(); + if (block.hasNbtData()) limit.MAX_BLOCKSTATES(); + try { + return getExtent().setBlock(x, y, z, block); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return false; + } + } + + public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { + limit.THROW_MAX_CHANGES(); + limit.MAX_BLOCKSTATES(); + try { + return getExtent().setTile(x, y, z, tile); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return false; + } + } + + public boolean setBiome(BlockVector2 position, BiomeType biome) { + limit.THROW_MAX_CHANGES(); + try { + return getExtent().setBiome(position, biome); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return false; + } + } + + public boolean setBiome(int x, int y, int z, BiomeType biome) { + limit.THROW_MAX_CHANGES(); + try { + return getExtent().setBiome(x, y, z, biome); + } catch (FaweException e) { + if (!limit.MAX_FAILS()) { + throw e; + } + return false; + } + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/LimitProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/LimitProcessor.java new file mode 100644 index 000000000..2485ba5c2 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/LimitProcessor.java @@ -0,0 +1,47 @@ +package com.boydti.fawe.beta.implementation.processors; + +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.object.FaweLimit; +import com.boydti.fawe.object.exception.FaweException; +import com.sk89q.worldedit.extent.Extent; + +public class LimitProcessor implements IBatchProcessor { + private final FaweLimit limit; + private final IBatchProcessor parent; + public LimitProcessor(FaweLimit limit, IBatchProcessor parent) { + this.limit = limit; + this.parent = parent; + } + + @Override + public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { + try { + return parent.processSet(chunk, get, set); + } catch (FaweException e) { + if (!limit.MAX_CHANGES()) { + throw e; + } + return null; + } + } + + @Override + public boolean processGet(int chunkX, int chunkZ) { + try { + return parent.processGet(chunkX, chunkZ); + } catch (FaweException e) { + if (!limit.MAX_CHECKS()) { + throw e; + } + return false; + } + } + + @Override + public Extent construct(Extent child) { + return new LimitExtent(parent.construct(child), limit); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/MultiBatchProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java similarity index 89% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/MultiBatchProcessor.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java index 6cd975d30..5cb13fd8f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/MultiBatchProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.implementation; +package com.boydti.fawe.beta.implementation.processors; import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.IChunk; @@ -55,13 +55,18 @@ public class MultiBatchProcessor implements IBatchProcessor { @Override public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { - for (IBatchProcessor processor : this.processors) { - set = processor.processSet(chunk, get, set); - if (set == null) { - return null; + try { + for (IBatchProcessor processor : this.processors) { + set = processor.processSet(chunk, get, set); + if (set == null) { + return null; + } } + return set; + } catch (Throwable e) { + e.printStackTrace(); + throw e; } - return set; } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/NullProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/NullProcessor.java new file mode 100644 index 000000000..7b3f54b25 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/NullProcessor.java @@ -0,0 +1,21 @@ +package com.boydti.fawe.beta.implementation.processors; + +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.NullExtent; + +public enum NullProcessor implements IBatchProcessor { + INSTANCE; + @Override + public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { + return null; + } + + @Override + public Extent construct(Extent child) { + return new NullExtent(); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/ParallelQueueExtent.java similarity index 89% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ParallelQueueExtent.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/ParallelQueueExtent.java index 31f2aeec7..c890f3f40 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ParallelQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/ParallelQueueExtent.java @@ -1,11 +1,12 @@ -package com.boydti.fawe.beta.implementation; +package com.boydti.fawe.beta.implementation.queue; -import com.boydti.fawe.beta.ChunkFilterBlock; +import com.boydti.fawe.beta.IQueueWrapper; +import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; import com.boydti.fawe.beta.Filter; -import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IQueueExtent; -import com.boydti.fawe.beta.filters.CountFilter; -import com.boydti.fawe.beta.filters.DistrFilter; +import com.boydti.fawe.beta.implementation.filter.CountFilter; +import com.boydti.fawe.beta.implementation.filter.DistrFilter; +import com.boydti.fawe.beta.implementation.processors.BatchProcessorHolder; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.changeset.FaweChangeSet; import com.boydti.fawe.object.clipboard.WorldCopyClipboard; @@ -27,12 +28,15 @@ 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 java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.ForkJoinTask; import java.util.stream.IntStream; +import static com.google.common.base.Preconditions.checkNotNull; + public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrapper { private final World world; @@ -67,26 +71,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap return super.enableHistory(changeSet); } - private ChunkFilterBlock apply(ChunkFilterBlock block, Filter filter, IQueueExtent queue, Region region, int X, int Z) { - if (!filter.appliesChunk(X, Z)) { - return block; - } - IChunk chunk = queue.getOrCreateChunk(X, Z); - // Initialize - chunk.init(queue, X, Z); - - IChunk newChunk = filter.applyChunk(chunk, region); - if (newChunk != null) { - chunk = newChunk; - if (block == null) { - block = queue.initFilterBlock(); - } - chunk.filterBlocks(filter, block, region); - } - queue.submit(chunk); - return block; - } - + @Override public T apply(Region region, T filter) { // The chunks positions to iterate over final Set chunks = region.getChunks(); @@ -96,7 +81,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap final int size = Math.min(chunks.size(), Settings.IMP.QUEUE.PARALLEL_THREADS); if (size <= 1) { BlockVector2 pos = chunksIter.next(); - apply(null, filter, getExtent(), region, pos.getX(), pos.getZ()); + getExtent().apply(null, filter, region, pos.getX(), pos.getZ()); } else { final ForkJoinTask[] tasks = IntStream.range(0, size).mapToObj(i -> handler.submit(() -> { try { @@ -117,7 +102,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap X = pos.getX(); Z = pos.getZ(); } - block = apply(block, newFilter, queue, region, X, Z); + block = queue.apply(block, newFilter, region, X, Z); } queue.flush(); } @@ -167,6 +152,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap if (vset instanceof Region) { setBlocks((Region) vset, pattern); } + // TODO optimize parallel for (BlockVector3 blockVector3 : vset) { pattern.apply(this, blockVector3, blockVector3); } @@ -226,7 +212,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap * returned by a given pattern. * * @param region the region to replace the blocks within - * @param filter a list of block types to match, or null to use {@link ExistingBlockMask} + * @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.function.mask.ExistingBlockMask} * @param replacement the replacement block * @return number of blocks affected * @throws MaxChangedBlocksException thrown if too many blocks are changed @@ -241,7 +227,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap * returned by a given pattern. * * @param region the region to replace the blocks within - * @param filter a list of block types to match, or null to use {@link ExistingBlockMask} + * @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.function.mask.ExistingBlockMask} * @param pattern the pattern that provides the new blocks * @return number of blocks affected * @throws MaxChangedBlocksException thrown if too many blocks are changed diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/QueueHandler.java similarity index 68% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/QueueHandler.java index e3eaff4ca..9fb3d1815 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/QueueHandler.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.implementation; +package com.boydti.fawe.beta.implementation.queue; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; @@ -8,6 +8,8 @@ import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.Trimable; +import com.boydti.fawe.beta.implementation.cache.ChunkCache; +import com.boydti.fawe.beta.IChunkCache; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.CleanableThreadLocal; import com.boydti.fawe.util.MemUtil; @@ -19,6 +21,7 @@ import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Queue; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; @@ -39,6 +42,7 @@ public abstract class QueueHandler implements Trimable, Runnable { private ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool(); private ThreadPoolExecutor blockingExecutor = FaweCache.IMP.newBlockingExecutor(); private ConcurrentLinkedQueue syncTasks = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue syncWhenFree = new ConcurrentLinkedQueue<>(); private Map>> chunkGetCache = new HashMap<>(); private CleanableThreadLocal queuePool = new CleanableThreadLocal<>(QueueHandler.this::create); @@ -60,53 +64,61 @@ public abstract class QueueHandler implements Trimable, Runnable { throw new IllegalStateException("Not main thread"); } if (!syncTasks.isEmpty()) { - long now = System.currentTimeMillis(); - targetTPS = 18 - Math.max(Settings.IMP.QUEUE.EXTRA_TIME_MS * 0.05, 0); - long diff = 50 + this.last - (this.last = now); - long absDiff = Math.abs(diff); - if (diff == 0) { - allocate = Math.min(50, allocate + 1); - } else if (diff < 0) { - allocate = Math.max(5, allocate + diff); - } else if (!Fawe.get().getTimer().isAbove(targetTPS)) { - allocate = Math.max(5, allocate - 1); - } - long currentAllocate = allocate - absDiff; + long currentAllocate = getAllocate(); if (!MemUtil.isMemoryFree()) { // TODO reduce mem usage + // FaweCache trim + // Preloader trim } - boolean wait = false; - do { - Runnable task = syncTasks.poll(); - if (task == null) { - if (wait) { - synchronized (syncTasks) { - try { - syncTasks.wait(1); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - task = syncTasks.poll(); - wait = false; - } else { - break; - } - } - if (task != null) { - task.run(); - wait = true; - } - } while (System.currentTimeMillis() - now < currentAllocate); + operate(syncTasks, last, currentAllocate); + } else if (!syncWhenFree.isEmpty()) { + operate(syncWhenFree, last, getAllocate()); + } else { + // trim?? } - while (!syncTasks.isEmpty()) { - final FutureTask task = syncTasks.poll(); + } + + private long getAllocate() { + long now = System.currentTimeMillis(); + targetTPS = 18 - Math.max(Settings.IMP.QUEUE.EXTRA_TIME_MS * 0.05, 0); + long diff = 50 + this.last - (this.last = now); + long absDiff = Math.abs(diff); + if (diff == 0) { + allocate = Math.min(50, allocate + 1); + } else if (diff < 0) { + allocate = Math.max(5, allocate + diff); + } else if (!Fawe.get().getTimer().isAbove(targetTPS)) { + allocate = Math.max(5, allocate - 1); + } + return allocate - absDiff; + } + + private void operate(Queue queue, long start, long currentAllocate) { + boolean wait = false; + do { + Runnable task = queue.poll(); + if (task == null) { + if (wait) { + synchronized (syncTasks) { + try { + queue.wait(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + task = queue.poll(); + wait = false; + } else { + break; + } + } if (task != null) { task.run(); + wait = true; } - } + } while (System.currentTimeMillis() - start < currentAllocate); } public > void complete(Future task) { @@ -136,50 +148,83 @@ public abstract class QueueHandler implements Trimable, Runnable { } public Future sync(Runnable run, T value) { + return sync(run, value, syncTasks); + } + + public Future sync(Runnable run) { + return sync(run, syncTasks); + } + + public Future sync(Callable call) throws Exception { + return sync(call, syncTasks); + } + + public Future sync(Supplier call) { + return sync(call, syncTasks); + } + + // Lower priorty sync task (runs only when there are no other tasks) + public Future syncWhenFree(Runnable run, T value) { + return sync(run, value, syncWhenFree); + } + + public Future syncWhenFree(Runnable run) { + return sync(run, syncWhenFree); + } + + public Future syncWhenFree(Callable call) throws Exception { + return sync(call, syncWhenFree); + } + + public Future syncWhenFree(Supplier call) { + return sync(call, syncWhenFree); + } + + private Future sync(Runnable run, T value, Queue queue) { if (Fawe.isMainThread()) { run.run(); return Futures.immediateFuture(value); } final FutureTask result = new FutureTask<>(run, value); - syncTasks.add(result); - notifySync(); + queue.add(result); + notifySync(queue); return result; } - public Future sync(Runnable run) { + private Future sync(Runnable run, Queue queue) { if (Fawe.isMainThread()) { run.run(); return Futures.immediateCancelledFuture(); } final FutureTask result = new FutureTask<>(run, null); - syncTasks.add(result); - notifySync(); + queue.add(result); + notifySync(queue); return result; } - public Future sync(Callable call) throws Exception { + private Future sync(Callable call, Queue queue) throws Exception { if (Fawe.isMainThread()) { return Futures.immediateFuture(call.call()); } final FutureTask result = new FutureTask<>(call); - syncTasks.add(result); - notifySync(); + queue.add(result); + notifySync(queue); return result; } - public Future sync(Supplier call) { + private Future sync(Supplier call, Queue queue) { if (Fawe.isMainThread()) { return Futures.immediateFuture(call.get()); } final FutureTask result = new FutureTask<>(call::get); - syncTasks.add(result); - notifySync(); + queue.add(result); + notifySync(queue); return result; } - private void notifySync() { - synchronized (syncTasks) { - syncTasks.notifyAll(); + private void notifySync(Object object) { + synchronized (object) { + object.notifyAll(); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java similarity index 89% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/SingleThreadQueueExtent.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java index b22dc5694..6f06cb6d8 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/SingleThreadQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java @@ -1,29 +1,28 @@ -package com.boydti.fawe.beta.implementation; +package com.boydti.fawe.beta.implementation.queue; import com.boydti.fawe.Fawe; -import com.boydti.fawe.FaweCache; -import com.boydti.fawe.beta.CharFilterBlock; -import com.boydti.fawe.beta.ChunkFilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.CharFilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.IQueueExtent; +import com.boydti.fawe.beta.IChunkCache; +import com.boydti.fawe.beta.implementation.chunk.NullChunk; import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks; -import com.boydti.fawe.beta.implementation.holder.ChunkHolder; -import com.boydti.fawe.beta.implementation.holder.ReferenceChunk; +import com.boydti.fawe.beta.implementation.chunk.ChunkHolder; +import com.boydti.fawe.beta.implementation.chunk.ReferenceChunk; +import com.boydti.fawe.beta.implementation.processors.BatchProcessorHolder; +import com.boydti.fawe.beta.implementation.processors.EmptyBatchProcessor; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.changeset.FaweChangeSet; -import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MemUtil; import com.google.common.util.concurrent.Futures; import com.sk89q.worldedit.extent.Extent; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -221,13 +220,15 @@ public class SingleThreadQueueExtent extends BatchProcessorHolder implements IQu @Override public final IChunk getOrCreateChunk(int x, int z) { - if (!processGet(x, z)) { - throw FaweCache.CHUNK; - } final long pair = (long) x << 32 | z & 0xffffffffL; if (pair == lastPair) { return lastChunk; } + if (!processGet(x, z)) { + lastPair = pair; + lastChunk = NullChunk.INSTANCE; + return NullChunk.INSTANCE; + } IChunk chunk = chunks.get(pair); if (chunk instanceof ReferenceChunk) { @@ -276,11 +277,20 @@ public class SingleThreadQueueExtent extends BatchProcessorHolder implements IQu private void pollSubmissions(int targetSize, boolean aggressive) { final int overflow = submissions.size() - targetSize; if (aggressive) { + if (targetSize == 0) { + while (!submissions.isEmpty()) { + Future future = submissions.poll(); + try { + while (future != null) future = (Future) future.get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } + } for (int i = 0; i < overflow; i++) { Future first = submissions.poll(); try { - while ((first = (Future) first.get()) != null) { - } + while (first != null) first = (Future) first.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } @@ -327,7 +337,6 @@ public class SingleThreadQueueExtent extends BatchProcessorHolder implements IQu chunks.clear(); } pollSubmissions(0, true); - reset(); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommands.java b/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommands.java index d34912e2d..28ba43c1a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommands.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommands.java @@ -6,7 +6,7 @@ import static com.sk89q.worldedit.util.formatting.text.TextComponent.newline; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweAPI; -import com.boydti.fawe.beta.SingleFilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.SingleFilterBlock; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.brush.visualization.cfi.HeightMapMCAGenerator; @@ -75,6 +75,7 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.IntStream; import javax.imageio.ImageIO; + import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.param.Arg; @@ -431,7 +432,7 @@ public class CFICommands { Clipboard clipboard = holder.getClipboard(); boolean[] ids = new boolean[BlockTypes.size()]; for (BlockVector3 pt : clipboard.getRegion()) { - ids[clipboard.getBlock(pt).getInternalBlockTypeId()] = true; + ids[clipboard.getBlock(pt).getBlockType().getInternalId()] = true; } blocks = new HashSet<>(); for (int combined = 0; combined < ids.length; combined++) { @@ -605,7 +606,7 @@ public class CFICommands { @CommandPermissions("worldedit.anvil.cfi") public void waterId(Player player, BlockStateHolder block) throws WorldEditException { CFISettings settings = assertSettings(player); - settings.getGenerator().setWaterId(block.getBlockType().getInternalId()); + settings.getGenerator().setWater(block.toImmutableState()); player.print("Set water id!"); settings.resetComponent(); @@ -620,7 +621,7 @@ public class CFICommands { @CommandPermissions("worldedit.anvil.cfi") public void baseId(Player player, BlockStateHolder block) throws WorldEditException { CFISettings settings = assertSettings(player); - settings.getGenerator().setBedrockId(block.getBlockType().getInternalId()); + settings.getGenerator().setBedrock(block.toImmutableState()); player.print(TextComponent.of("Set base id!")); settings.resetComponent(); component(player); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java b/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java index e8b53b2c5..ed7d4ee11 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java @@ -14,6 +14,8 @@ public class Settings extends Config { @Ignore public boolean PROTOCOL_SUPPORT_FIX = false; + @Ignore + public boolean PLOTSQUARED_HOOK = false; @Comment("These first 6 aren't configurable") // This is a comment @Final // Indicates that this value isn't configurable diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/CorruptSchematicStreamer.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/CorruptSchematicStreamer.java index 6d46542f3..f20d9e8a8 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/CorruptSchematicStreamer.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/CorruptSchematicStreamer.java @@ -4,17 +4,11 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.clipboard.CPUOptimizedClipboard; import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; -import com.boydti.fawe.object.clipboard.FaweClipboard; +import com.boydti.fawe.object.clipboard.LinearClipboard; import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.NBTInputStream; -import com.sk89q.worldedit.entity.BaseEntity; -import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.math.Vector3; -import com.sk89q.worldedit.regions.CuboidRegion; + import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.IOException; @@ -29,7 +23,7 @@ public class CorruptSchematicStreamer { private final InputStream stream; private final UUID uuid; - private FaweClipboard fc; + private LinearClipboard fc; final AtomicInteger volume = new AtomicInteger(); final AtomicInteger width = new AtomicInteger(); final AtomicInteger height = new AtomicInteger(); @@ -78,7 +72,7 @@ public class CorruptSchematicStreamer { } } - public FaweClipboard setupClipboard() { + public LinearClipboard setupClipboard() { if (fc != null) { return fc; } @@ -87,11 +81,11 @@ public class CorruptSchematicStreamer { Fawe.debug("No dimensions found! Estimating based on factors:" + dimensions); } if (Settings.IMP.CLIPBOARD.USE_DISK) { - fc = new DiskOptimizedClipboard(dimensions.getBlockX(), dimensions.getBlockY(), dimensions.getBlockZ(), uuid); + fc = new DiskOptimizedClipboard(dimensions, uuid); } else if (Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL == 0) { - fc = new CPUOptimizedClipboard(dimensions.getBlockX(), dimensions.getBlockY(), dimensions.getBlockZ()); + fc = new CPUOptimizedClipboard(dimensions); } else { - fc = new MemoryOptimizedClipboard(dimensions.getBlockX(), dimensions.getBlockY(), dimensions.getBlockZ()); + fc = new MemoryOptimizedClipboard(dimensions); } return fc; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/NBTStreamer.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/NBTStreamer.java deleted file mode 100644 index 7c0bd76a4..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/NBTStreamer.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.boydti.fawe.jnbt; - -import com.boydti.fawe.FaweCache; -import com.boydti.fawe.config.BBC; -import com.boydti.fawe.object.exception.FaweException; - -import com.sk89q.jnbt.NBTInputStream; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.HashMap; -import java.util.function.BiConsumer; - -public class NBTStreamer { - private final NBTInputStream is; - private final HashMap readers; - - public NBTStreamer(NBTInputStream stream) { - this.is = stream; - readers = new HashMap<>(); - } - - /** - * Reads the entire stream and runs the applicable readers - * - * @throws IOException - */ - public void readFully() throws IOException { - is.readNamedTagLazy(readers::get); - is.close(); - } - - /** - * Reads the stream until all readers have been used
- * - Use readFully if you expect a reader to appear more than once - * - Can exit early without having reading the entire file - * - * @throws IOException - */ - public void readQuick() throws IOException { - try { - is.readNamedTagLazy(node -> { - if (readers.isEmpty()) { - throw FaweCache.MANUAL; - } - return readers.remove(node); - }); - } catch (FaweException ignore) {} - is.close(); - } - - public void addReader(String node, BiConsumer run) { - if (run instanceof NBTStreamReader) { - ((NBTStreamReader) run).init(node); - } - readers.put(node, run); - } - - public static abstract class NBTStreamReader implements BiConsumer { - private String node; - - public void init(String node) { - this.node = node; - } - - public String getNode() { - return node; - } - } - - public static abstract class ByteReader implements BiConsumer { - @Override - public void accept(Integer index, Integer value) { - run(index, value); - } - - public abstract void run(int index, int byteValue); - } - - public interface LazyReader extends BiConsumer {} - - public static abstract class LongReader implements BiConsumer { - @Override - public void accept(Integer index, Long value) { - run(index, value); - } - - public abstract void run(int index, long byteValue); - } -} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java deleted file mode 100644 index 93ba38f0b..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java +++ /dev/null @@ -1,420 +0,0 @@ -package com.boydti.fawe.jnbt; - -import com.boydti.fawe.Fawe; -import com.boydti.fawe.config.Settings; -import com.boydti.fawe.object.FaweInputStream; -import com.boydti.fawe.object.FaweOutputStream; -import com.boydti.fawe.object.clipboard.CPUOptimizedClipboard; -import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; -import com.boydti.fawe.object.clipboard.FaweClipboard; -import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard; -import com.boydti.fawe.object.io.FastByteArrayOutputStream; -import com.boydti.fawe.object.io.FastByteArraysInputStream; - -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.NBTInputStream; -import com.sk89q.jnbt.StringTag; -import com.sk89q.worldedit.entity.BaseEntity; -import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; -import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.regions.CuboidRegion; -import com.sk89q.worldedit.registry.state.PropertyKey; -import com.sk89q.worldedit.util.Direction; -import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.biome.BiomeTypes; -import com.sk89q.worldedit.world.block.BlockCategories; -import com.sk89q.worldedit.world.block.BlockID; -import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockType; -import com.sk89q.worldedit.world.block.BlockTypeSwitch; -import com.sk89q.worldedit.world.block.BlockTypeSwitchBuilder; -import com.sk89q.worldedit.world.entity.EntityType; -import com.sk89q.worldedit.world.entity.EntityTypes; -import com.sk89q.worldedit.world.registry.BlockMaterial; -import com.sk89q.worldedit.world.registry.LegacyMapper; -import net.jpountz.lz4.LZ4BlockInputStream; -import net.jpountz.lz4.LZ4BlockOutputStream; - -import java.io.IOException; -import java.util.UUID; -import java.util.function.BiConsumer; - -// TODO FIXME -public class SchematicStreamer extends NBTStreamer { - private final UUID uuid; - private FastByteArrayOutputStream idOut = new FastByteArrayOutputStream(); - private FastByteArrayOutputStream dataOut = new FastByteArrayOutputStream(); - private FastByteArrayOutputStream addOut; - - private FaweOutputStream ids; - private FaweOutputStream datas; - private FaweOutputStream adds; - - public SchematicStreamer(NBTInputStream stream, UUID uuid) { - super(stream); - this.uuid = uuid; - clipboard = new BlockArrayClipboard(new CuboidRegion(BlockVector3.at(0, 0, 0), BlockVector3.at(0, 0, 0)), fc); - } - - public void addBlockReaders() throws IOException { - NBTStreamReader idInit = new NBTStreamReader() { - @Override - public void accept(Integer length, Integer type) { - setupClipboard(length); - ids = new FaweOutputStream(new LZ4BlockOutputStream(idOut)); - } - }; - NBTStreamReader dataInit = new NBTStreamReader() { - @Override - public void accept(Integer length, Integer type) { - setupClipboard(length); - datas = new FaweOutputStream(new LZ4BlockOutputStream(dataOut)); - } - }; - NBTStreamReader addInit = new NBTStreamReader() { - @Override - public void accept(Integer length, Integer type) { - setupClipboard(length*2); - addOut = new FastByteArrayOutputStream(); - adds = new FaweOutputStream(new LZ4BlockOutputStream(addOut)); - } - }; - - addReader("Schematic.Blocks.?", idInit); - addReader("Schematic.Data.?", dataInit); - addReader("Schematic.AddBlocks.?", addInit); - addReader("Schematic.Blocks.#", new ByteReader() { - @Override - public void run(int index, int value) { - try { - ids.write(value); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }); - addReader("Schematic.Data.#", new ByteReader() { - @Override - public void run(int index, int value) { - try { - datas.write(value); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }); - addReader("Schematic.AddBlocks.#", new ByteReader() { - @Override - public void run(int index, int value) { - if (value != 0) { - int first = value & 0x0F; - int second = (value & 0xF0) >> 4; - try { - if (first != 0) adds.write(first); - if (second != 0) adds.write(second); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - }); - ByteReader biomeReader = new ByteReader() { - @Override - public void run(int index, int value) { - BiomeType biome = BiomeTypes.getLegacy(value); - if (biome != null) { - fc.setBiome(index, biome); - } - } - }; - NBTStreamReader initializer23 = new NBTStreamReader() { - @Override - public void accept(Integer value1, Integer value2) { - if (fc == null) setupClipboard(length * width * height); - } - }; - addReader("Schematic.AWEBiomes.?", initializer23); - addReader("Schematic.Biomes.?", initializer23); - addReader("Schematic.AWEBiomes.#", biomeReader); // AWE stores as an int[] - addReader("Schematic.Biomes.#", biomeReader); // FAWE stores as a byte[] (4x smaller) - - // Tiles - addReader("Schematic.TileEntities.#", (BiConsumer) (index, value) -> { - if (fc == null) { - setupClipboard(0); - } - int x = value.getInt("x"); - int y = value.getInt("y"); - int z = value.getInt("z"); - fc.setTile(x, y, z, value); - }); - // Entities - addReader("Schematic.Entities.#", (BiConsumer) (index, compound) -> { - if (fc == null) { - setupClipboard(0); - } - String id = compound.getString("id"); - if (id.isEmpty()) { - return; - } - ListTag positionTag = compound.getListTag("Pos"); - ListTag directionTag = compound.getListTag("Rotation"); - EntityType type = EntityTypes.parse(id); - if (type != null) { - compound.getValue().put("Id", new StringTag(type.getId())); - BaseEntity state = new BaseEntity(type, compound); - fc.createEntity(clipboard, positionTag.asDouble(0), positionTag.asDouble(1), positionTag.asDouble(2), (float) directionTag.asDouble(0), (float) directionTag.asDouble(1), state); - } else { - Fawe.debug("Invalid entity: " + id); - } - }); - } - - @Override - public void readFully() throws IOException { - super.readFully(); - if (ids != null) ids.close(); - if (datas != null) datas.close(); - if (adds != null) adds.close(); - FaweInputStream idIn = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(idOut.toByteArrays()))); - FaweInputStream dataIn = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(dataOut.toByteArrays()))); - - LegacyMapper remap = LegacyMapper.getInstance(); - BlockVector3 dimensions = fc.getDimensions(); - int length = dimensions.getBlockX() * dimensions.getBlockY() * dimensions.getBlockZ(); - if (adds == null) { - for (int i = 0; i < length; i++) { - fc.setBlock(i, remap.getBlockFromLegacyCombinedId(((idIn.read() & 0xFF) << 4) + (dataIn.read() & 0xF))); - } - } else { - FaweInputStream addIn = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(dataOut.toByteArrays()))); - for (int i = 0; i < length; i++) { - fc.setBlock(i, remap.getBlockFromLegacyCombinedId(((addIn.read() & 0xFF) << 8) + ((idIn.read() & 0xFF) << 4) + (dataIn.read() & 0xF))); - } - addIn.close(); - } - idIn.close(); - dataIn.close(); - } - - private void fixStates() { - fc.forEach(new FaweClipboard.BlockReader() { - @Override - public > void run(int x, int y, int z, B block) { - BlockType type = block.getBlockType(); - if (BlockCategories.STAIRS.contains(type)) { - Object half = block.getState(PropertyKey.HALF); - Direction facing = block.getState(PropertyKey.FACING); - - BlockVector3 forward = facing.toBlockVector(); - Direction left = facing.getLeft(); - Direction right = facing.getRight(); - - BlockStateHolder forwardBlock = fc.getBlock(x + forward.getBlockX(), y + forward.getBlockY(), z + forward.getBlockZ()); - BlockType forwardType = forwardBlock.getBlockType(); - if (forwardType.hasProperty(PropertyKey.SHAPE) && forwardType.hasProperty(PropertyKey.FACING)) { - Direction forwardFacing = (Direction) forwardBlock.getState(PropertyKey.FACING); - if (forwardFacing == left) { - BlockStateHolder rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ()); - BlockType rightType = rightBlock.getBlockType(); - if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) { - fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "inner_left")); - } - return; - } else if (forwardFacing == right) { - BlockStateHolder leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ()); - BlockType leftType = leftBlock.getBlockType(); - if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) { - fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "inner_right")); - } - return; - } - } - - BlockStateHolder backwardsBlock = fc.getBlock(x - forward.getBlockX(), y - forward.getBlockY(), z - forward.getBlockZ()); - BlockType backwardsType = backwardsBlock.getBlockType(); - if (backwardsType.hasProperty(PropertyKey.SHAPE) && backwardsType.hasProperty(PropertyKey.FACING)) { - Direction backwardsFacing = (Direction) backwardsBlock.getState(PropertyKey.FACING); - if (backwardsFacing == left) { - BlockStateHolder rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ()); - BlockType rightType = rightBlock.getBlockType(); - if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) { - fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "outer_left")); - } - return; - } else if (backwardsFacing == right) { - BlockStateHolder leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ()); - BlockType leftType = leftBlock.getBlockType(); - if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) { - fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "outer_right")); - } - return; - } - } - } else { - int group = group(type); - if (group == -1) return; - BlockStateHolder set = block; - - if (set.getState(PropertyKey.NORTH) == Boolean.FALSE && merge(group, x, y, z - 1)) set = set.with(PropertyKey.NORTH, true); - if (set.getState(PropertyKey.EAST) == Boolean.FALSE && merge(group, x + 1, y, z)) set = set.with(PropertyKey.EAST, true); - if (set.getState(PropertyKey.SOUTH) == Boolean.FALSE && merge(group, x, y, z + 1)) set = set.with(PropertyKey.SOUTH, true); - if (set.getState(PropertyKey.WEST) == Boolean.FALSE && merge(group, x - 1, y, z)) set = set.with(PropertyKey.WEST, true); - - if (group == 2) { - int ns = ((Boolean) set.getState(PropertyKey.NORTH) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.SOUTH) ? 1 : 0); - int ew = ((Boolean) set.getState(PropertyKey.EAST) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.WEST) ? 1 : 0); - if (Math.abs(ns - ew) != 2 || fc.getBlock(x, y + 1, z).getBlockType().getMaterial().isSolid()) { - set = set.with(PropertyKey.UP, true); - } - } - - if (set != block) fc.setBlock(x, y, z, set); - } - } - }, false); - } - - private BlockTypeSwitch fullCube = new BlockTypeSwitchBuilder<>(false).add(type -> { - BlockMaterial mat = type.getMaterial(); - return (mat.isFullCube() && !mat.isFragileWhenPushed() && mat.getLightValue() == 0 && mat.isOpaque() && mat.isSolid() && !mat.isTranslucent()); - }, true).build(); - - private boolean merge(int group, int x, int y, int z) { - BlockStateHolder block = fc.getBlock(x, y, z); - BlockType type = block.getBlockType(); - return group(type) == group || fullCube.apply(type); - } - - private int group(BlockType type) { - switch (type.getInternalId()) { - case BlockID.ACACIA_FENCE: - case BlockID.BIRCH_FENCE: - case BlockID.DARK_OAK_FENCE: - case BlockID.JUNGLE_FENCE: - case BlockID.OAK_FENCE: - case BlockID.SPRUCE_FENCE: - return 0; - case BlockID.NETHER_BRICK_FENCE: - return 1; - case BlockID.COBBLESTONE_WALL: - case BlockID.MOSSY_COBBLESTONE_WALL: - return 2; - case BlockID.IRON_BARS: - case BlockID.BLACK_STAINED_GLASS_PANE: - case BlockID.BLUE_STAINED_GLASS_PANE: - case BlockID.BROWN_MUSHROOM_BLOCK: - case BlockID.BROWN_STAINED_GLASS_PANE: - case BlockID.CYAN_STAINED_GLASS_PANE: - case BlockID.GLASS_PANE: - case BlockID.GRAY_STAINED_GLASS_PANE: - case BlockID.GREEN_STAINED_GLASS_PANE: - case BlockID.LIGHT_BLUE_STAINED_GLASS_PANE: - case BlockID.LIGHT_GRAY_STAINED_GLASS_PANE: - case BlockID.LIME_STAINED_GLASS_PANE: - case BlockID.MAGENTA_STAINED_GLASS_PANE: - case BlockID.ORANGE_STAINED_GLASS_PANE: - case BlockID.PINK_STAINED_GLASS_PANE: - case BlockID.PURPLE_STAINED_GLASS_PANE: - case BlockID.RED_STAINED_GLASS_PANE: - case BlockID.WHITE_STAINED_GLASS_PANE: - case BlockID.YELLOW_STAINED_GLASS_PANE: - return 3; - default: - return -1; - } - } - - public void addDimensionReaders() { - addReader("Schematic.Height", - (BiConsumer) (index, value) -> height = (value)); - addReader("Schematic.Width", (BiConsumer) (index, value) -> width = (value)); - addReader("Schematic.Length", - (BiConsumer) (index, value) -> length = (value)); - addReader("Schematic.WEOriginX", - (BiConsumer) (index, value) -> originX = (value)); - addReader("Schematic.WEOriginY", - (BiConsumer) (index, value) -> originY = (value)); - addReader("Schematic.WEOriginZ", - (BiConsumer) (index, value) -> originZ = (value)); - addReader("Schematic.WEOffsetX", - (BiConsumer) (index, value) -> offsetX = (value)); - addReader("Schematic.WEOffsetY", - (BiConsumer) (index, value) -> offsetY = (value)); - addReader("Schematic.WEOffsetZ", - (BiConsumer) (index, value) -> offsetZ = (value)); - } - - private int height; - private int width; - private int length; - - private int originX; - private int originY; - private int originZ; - - private int offsetX; - private int offsetY; - private int offsetZ; - - private BlockArrayClipboard clipboard; - private FaweClipboard fc; - - private FaweClipboard setupClipboard(int size) { - if (fc != null) { - if (fc.getDimensions().getX() == 0) { - fc.setDimensions(BlockVector3.at(size, 1, 1)); - } - return fc; - } - if (Settings.IMP.CLIPBOARD.USE_DISK) { - return fc = new DiskOptimizedClipboard(size, 1, 1, uuid); - } else if (Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL == 0) { - return fc = new CPUOptimizedClipboard(size, 1, 1); - } else { - return fc = new MemoryOptimizedClipboard(size, 1, 1); - } - } - - public BlockVector3 getOrigin() { - return BlockVector3.at(originX, originY, originZ); - } - - public BlockVector3 getOffset() { - return BlockVector3.at(offsetX, offsetY, offsetZ); - } - - public BlockVector3 getDimensions() { - return BlockVector3.at(width, height, length); - } - - public void setClipboard(FaweClipboard clipboard) { - this.fc = clipboard; - } - - public Clipboard getClipboard() throws IOException { - try { - setupClipboard(0); - addDimensionReaders(); - addBlockReaders(); - readFully(); - BlockVector3 min = BlockVector3.at(originX, originY, originZ); - BlockVector3 offset = BlockVector3.at(offsetX, offsetY, offsetZ); - BlockVector3 origin = min.subtract(offset); - BlockVector3 dimensions = BlockVector3.at(width, height, length); - fc.setDimensions(dimensions); - fixStates(); - CuboidRegion region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE)); - clipboard.init(region, fc); - clipboard.setOrigin(origin); - return clipboard; - } catch (Throwable e) { - if (fc != null) { - fc.close(); - } - throw e; - } - } -} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java similarity index 53% rename from worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java rename to worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java index fbe06d76c..e1bed5e96 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java @@ -1,49 +1,161 @@ -package com.boydti.fawe.object.brush.visualization.cfi; +package com.boydti.fawe.jnbt.anvil; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.jnbt.streamer.StreamDelegate; +import com.boydti.fawe.jnbt.streamer.ValueReader; import com.boydti.fawe.object.collection.BitArray4096; +import com.boydti.fawe.object.collection.BlockVector3ChunkMap; import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.MathMan; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.NBTConstants; +import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockID; 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.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; + import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.UUID; -public class WritableMCAChunk { +public class MCAChunk implements IChunkSet { public final boolean[] hasSections = new boolean[16]; - public final byte[] skyLight = new byte[65536]; - public final byte[] blockLight = new byte[65536]; public boolean hasBiomes = false; - public final int[] biomes = new int[256]; + public final byte[] biomes = new byte[256]; - public final int[] blocks = new int[65536]; + public final char[] blocks = new char[65536]; - public Map tiles = new HashMap<>(); - public Map entities = new HashMap<>(); + public final BlockVector3ChunkMap tiles = new BlockVector3ChunkMap(); + public final Map entities = new HashMap<>(); public long inhabitedTime = System.currentTimeMillis(); public long lastUpdate; public int modified; public boolean deleted; - public int chunkX, chunkZ; + public int chunkX; + public int chunkZ; - protected WritableMCAChunk() { + public MCAChunk() {} + + private boolean readLayer(Section section) { + if (section.palette == null || section.layer == -1 || section.blocksLength == -1 || section.palette[section.palette.length - 1] == null || section.blocks == null) { + // not initialized + return false; + } + + int bitsPerEntry = MathMan.log2nlz(section.palette.length - 1); + BitArray4096 bitArray = new BitArray4096(section.blocks, bitsPerEntry); + char[] buffer = FaweCache.IMP.SECTION_BITS_TO_CHAR.get(); + bitArray.toRaw(buffer); + int offset = section.layer << 12; + for (int i = 0; i < buffer.length; i++) { + BlockState block = section.palette[buffer[i]]; + blocks[offset + i] = block.getOrdinalChar(); + } + + section.layer = -1; + section.blocksLength = -1; + section.blocks = null; + section.palette = null; + return true; + } + + private static class Section { + public int layer = -1; + public long[] blocks; + public int blocksLength = -1; + public BlockState[] palette; + } + + public MCAChunk(NBTInputStream nis, int chunkX, int chunkZ, boolean readPos) throws IOException { + this.chunkX = chunkX; + this.chunkZ = chunkZ; + read(nis, readPos); + } + + public void read(NBTInputStream nis, boolean readPos) throws IOException { + StreamDelegate root = createDelegate(nis, readPos); + nis.readNamedTagLazy(root); + } + + public StreamDelegate createDelegate(NBTInputStream nis, boolean readPos) { + StreamDelegate root = new StreamDelegate(); + StreamDelegate level = root.add("").add("Level"); + + level.add("InhabitedTime").withLong((i, v) -> inhabitedTime = v); + level.add("LastUpdate").withLong((i, v) -> lastUpdate = v); + + if (readPos) { + level.add("xPos").withInt((i, v) -> MCAChunk.this.chunkX = v); + level.add("zPos").withInt((i, v) -> MCAChunk.this.chunkZ = v); + } + + Section section = new Section(); + + StreamDelegate layers = level.add("Sections"); + StreamDelegate layer = layers.add(); + layer.withInfo((length, type) -> { + section.layer = -1; + section.blocksLength = -1; + }); + layer.add("Y").withInt((i, y) -> section.layer = y); + layer.add("Palette").withElem((ValueReader>) (index, map) -> { + String name = (String) map.get("Name"); + BlockType type = BlockTypes.get(name); + BlockState state = type.getDefaultState(); + Map properties = (Map) map.get("Properties"); + if (properties != null) { + for (Map.Entry entry : properties.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + Property property = type.getProperty(key); + state = state.with(property, property.getValueFor(value)); + } + } + section.palette[index] = state; + readLayer(section); + }); + StreamDelegate blockStates = layer.add("BlockStates"); + blockStates.withInfo((length, type) -> { + if (section.blocks == null) { + section.blocks = FaweCache.IMP.LONG_BUFFER_1024.get(); + } + section.blocksLength = length; + }); + blockStates.withLong((index, value) -> section.blocks[index] = value); + level.add("TileEntities").withElem((ValueReader>) (index, value) -> { + CompoundTag tile = FaweCache.IMP.asTag(value); + int x = tile.getInt("x") & 15; + int y = tile.getInt("y"); + int z = tile.getInt("z") & 15; + tiles.put(x, y, z, tile); + }); + level.add("Entities").withElem((ValueReader>) (index, value) -> { + CompoundTag entity = FaweCache.IMP.asTag(value); + entities.put(entity.getUUID(), entity); + }); + level.add("Biomes").withInt((index, value) -> biomes[index] = (byte) value); + + return root; } public int getX() { @@ -54,14 +166,22 @@ public class WritableMCAChunk { return chunkZ; } - public void setLoc(int X, int Z) { + @Override + public boolean hasSection(int layer) { + return hasSections[layer]; + } + + public void setPosition(int X, int Z) { this.chunkX = X; this.chunkZ = Z; } - public void clear(int X, int Z) { - this.chunkX = X; - this.chunkZ = Z; + @Override + public IChunkSet reset() { + return this.reset(true); + } + + public IChunkSet reset(boolean full) { if (!tiles.isEmpty()) { tiles.clear(); } @@ -71,11 +191,13 @@ public class WritableMCAChunk { modified = 0; deleted = false; hasBiomes = false; - // TODO optimize - for (int i = 0; i < 65536; i++) { - blocks[i] = BlockID.AIR; + if (full) { + for (int i = 0; i < 65536; i++) { + blocks[i] = BlockID.AIR; + } } Arrays.fill(hasSections, false); + return this; } public void write(NBTOutputStream nbtOut) throws IOException { @@ -127,11 +249,10 @@ public class WritableMCAChunk { int num_palette = 0; try { for (int i = blockIndexStart, j = 0; i < blockIndexEnd; i++, j++) { - int stateId = blocks[i]; - int ordinal = BlockState.getFromInternalId(stateId).getOrdinal(); // TODO fixme Remove all use of BlockTypes.BIT_OFFSET so that this conversion isn't necessary + int ordinal = blocks[i]; int palette = blockToPalette[ordinal]; if (palette == Integer.MAX_VALUE) { - BlockState state = BlockTypes.states[ordinal]; +// BlockState state = BlockTypesCache.states[ordinal]; blockToPalette[ordinal] = palette = num_palette; paletteToBlock[num_palette] = ordinal; num_palette++; @@ -149,7 +270,7 @@ public class WritableMCAChunk { for (int i = 0; i < num_palette; i++) { int ordinal = paletteToBlock[i]; - BlockState state = BlockTypes.states[ordinal]; + BlockState state = BlockTypesCache.states[ordinal]; BlockType type = state.getBlockType(); out.writeNamedTag("Name", type.getId()); @@ -192,13 +313,13 @@ public class WritableMCAChunk { } - out.writeNamedTagName("BlockLight", NBTConstants.TYPE_BYTE_ARRAY); - out.writeInt(2048); - out.write(blockLight, layer << 11, 1 << 11); - - out.writeNamedTagName("SkyLight", NBTConstants.TYPE_BYTE_ARRAY); - out.writeInt(2048); - out.write(skyLight, layer << 11, 1 << 11); +// out.writeNamedTagName("BlockLight", NBTConstants.TYPE_BYTE_ARRAY); +// out.writeInt(2048); +// out.write(blockLight, layer << 11, 1 << 11); +// +// out.writeNamedTagName("SkyLight", NBTConstants.TYPE_BYTE_ARRAY); +// out.writeInt(2048); +// out.write(skyLight, layer << 11, 1 << 11); out.writeEndTag(); @@ -250,6 +371,15 @@ public class WritableMCAChunk { return deleted; } + @Override + public boolean isEmpty() { + if (deleted) return true; + for (boolean hasSection : hasSections) { + if (hasSection) return false; + } + return true; + } + public boolean isModified() { return modified != 0; } @@ -272,14 +402,17 @@ public class WritableMCAChunk { return bitMask; } - public void setTile(int x, int y, int z, CompoundTag tile) { + @Override + public boolean setTile(int x, int y, int z, CompoundTag tile) { setModified(); - short pair = MathMan.tripleBlockCoord(x, y, z); if (tile != null) { - tiles.put(pair, tile); + tiles.put(x, y, z, tile); } else { - tiles.remove(pair); + if (tiles.remove(x, y, z) == null) { + return false; + } } + return true; } public void setEntity(CompoundTag entityTag) { @@ -289,17 +422,39 @@ public class WritableMCAChunk { entities.put(new UUID(most, least), entityTag); } - public void setBiome(int x, int z, BiomeType biome) { + @Override + public BiomeType getBiomeType(int x, int z) { + return BiomeTypes.get(this.biomes[(z << 4) | x] & 0xFF); + } + + @Override + public BiomeType[] getBiomes() { + BiomeType[] tmp = new BiomeType[256]; + for (int i = 0; i < 256; i++) { + tmp[i] = BiomeTypes.get(this.biomes[i] & 0xFF); + } + return tmp; + } + + @Override + public boolean setBiome(BlockVector2 pos, BiomeType biome) { + return this.setBiome(pos.getX(), 0, pos.getZ(), biome); + } + + @Override + public boolean setBiome(int x, int y, int z, BiomeType biome) { setModified(); - biomes[x + (z << 4)] = biome.getInternalId(); + biomes[x + (z << 4)] = (byte) biome.getInternalId(); + return true; } public Set getEntities() { return new HashSet<>(entities.values()); } - public Map getTiles() { - return tiles == null ? new HashMap<>() : tiles; + @Override + public Map getTiles() { + return tiles == null ? Collections.emptyMap() : tiles; } public CompoundTag getTile(int x, int y, int z) { @@ -310,94 +465,51 @@ public class WritableMCAChunk { return tiles.get(pair); } - public boolean doesSectionExist(int cy) { - return hasSections[cy]; - } - private final int getIndex(int x, int y, int z) { return x | (z << 4) | (y << 8); } - public int getBlockCombinedId(int x, int y, int z) { + public int getBlockOrdinal(int x, int y, int z) { return blocks[x | (z << 4) | (y << 8)]; } - public BiomeType[] getBiomeArray() { - return null; + @Override + public BlockState getBlock(int x, int y, int z) { + int ordinal = getBlockOrdinal(x, y, z); + return BlockState.getFromOrdinal(ordinal); } public Set getEntityRemoves() { return new HashSet<>(); } - public void setSkyLight(int x, int y, int z, int value) { - setNibble(getIndex(x, y, z), skyLight, value); + @Override + public boolean setBlock(int x, int y, int z, BlockStateHolder holder) { + setBlock(x, y, z, holder.getOrdinalChar()); + holder.applyTileEntity(this, x, y, z); + return true; } - public void setBlockLight(int x, int y, int z, int value) { - setNibble(getIndex(x, y, z), blockLight, value); - } - - public int getSkyLight(int x, int y, int z) { - if (!hasSections[y >> 4]) { - return 0; - } - return getNibble(getIndex(x, y, z), skyLight); - } - - public int getBlockLight(int x, int y, int z) { - if (!hasSections[y >> 4]) { - return 0; - } - return getNibble(getIndex(x, y, z), blockLight); - } - - public void setFullbright() { - for (int layer = 0; layer < 16; layer++) { - if (hasSections[layer]) { - Arrays.fill(skyLight, layer << 7, ((layer + 1) << 7), (byte) 255); - } + @Override + public void setBlocks(int layer, char[] data) { + int offset = layer << 12; + for (int i = 0; i < 4096; i++) { + blocks[offset + i] = data[i]; } } - public void removeLight() { - for (int i = 0; i < 16; i++) { - removeLight(i); + @Override + public char[] getArray(int layer) { + char[] tmp = FaweCache.IMP.SECTION_BITS_TO_CHAR.get(); + int offset = layer << 12; + for (int i = 0; i < 4096; i++) { + tmp[i] = blocks[offset + i]; } + return tmp; } - public void removeLight(int i) { - if (hasSections[i]) { - Arrays.fill(skyLight, i << 7, ((i + 1) << 7), (byte) 0); - Arrays.fill(blockLight, i << 7, ((i + 1) << 7), (byte) 0); - } - } - - public int getNibble(int index, byte[] array) { - int indexShift = index >> 1; - if ((index & 1) == 0) { - return array[indexShift] & 15; - } else { - return array[indexShift] >> 4 & 15; - } - } - - public void setNibble(int index, byte[] array, int value) { - int indexShift = index >> 1; - byte existing = array[indexShift]; - int valueShift = value << 4; - if (existing == value + valueShift) { - return; - } - if ((index & 1) == 0) { - array[indexShift] = (byte) (existing & 240 | value); - } else { - array[indexShift] = (byte) (existing & 15 | valueShift); - } - } - - public void setBlock(int x, int y, int z, int combinedId) { - blocks[getIndex(x, y, z)] = combinedId; + public void setBlock(int x, int y, int z, char ordinal) { + blocks[getIndex(x, y, z)] = ordinal; } public void setBiome(BiomeType biome) { @@ -407,4 +519,9 @@ public class WritableMCAChunk { public void removeEntity(UUID uuid) { entities.remove(uuid); } + + @Override + public boolean trim(boolean aggressive) { + return isEmpty(); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java new file mode 100644 index 000000000..6f3477332 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java @@ -0,0 +1,708 @@ +package com.boydti.fawe.jnbt.anvil; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.beta.Trimable; +import com.boydti.fawe.jnbt.streamer.StreamDelegate; +import com.boydti.fawe.object.RunnableVal4; +import com.boydti.fawe.object.collection.CleanableThreadLocal; +import com.boydti.fawe.object.io.BufferedRandomAccessFile; +import com.boydti.fawe.object.io.FastByteArrayInputStream; +import com.boydti.fawe.util.MainUtil; +import com.boydti.fawe.util.MathMan; +import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.worldedit.world.World; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +/** + * Chunk format: http://minecraft.gamepedia.com/Chunk_format#Entity_format + * e.g.: `.Level.Entities.#` (Starts with a . as the root tag is unnamed) + * Note: This class isn't thread safe. You can use it in an async thread, but not multiple at the same time + */ +public class MCAFile implements Trimable { + + private static Field fieldBuf2; + private static Field fieldBuf3; + + static { + try { + fieldBuf2 = InflaterInputStream.class.getDeclaredField("buf"); + fieldBuf2.setAccessible(true); + fieldBuf3 = NBTInputStream.class.getDeclaredField("buf"); + fieldBuf3.setAccessible(true); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + private final ForkJoinPool pool; + private final byte[] locations; + private boolean readLocations; + + private File file; + private RandomAccessFile raf; + + private boolean deleted; + private int X, Z; + private MCAChunk[] chunks; + private boolean[] chunkInitialized; + private Object[] locks; + + final ThreadLocal byteStore1 = new ThreadLocal() { + @Override + protected byte[] initialValue() { + return new byte[4096]; + } + }; + final ThreadLocal byteStore2 = new ThreadLocal() { + @Override + protected byte[] initialValue() { + return new byte[4096]; + } + }; + final ThreadLocal byteStore3 = new ThreadLocal() { + @Override + protected byte[] initialValue() { + return new byte[1024]; + } + }; + + public MCAFile(ForkJoinPool pool) { + this.pool = pool; + this.locations = new byte[4096]; + this.chunks = new MCAChunk[32 * 32]; + this.chunkInitialized = new boolean[this.chunks.length]; + this.locks = new Object[this.chunks.length]; + for (int i = 0; i < locks.length; i++) { + locks[i] = new Object(); + } + } + + @Override + public boolean trim(boolean aggressive) { + boolean hasChunk = false; + for (int i = 0; i < chunkInitialized.length; i++) { + if (!chunkInitialized[i]) { + chunks[i] = null; + } else { + hasChunk = true; + } + } + CleanableThreadLocal.clean(byteStore1); + CleanableThreadLocal.clean(byteStore2); + CleanableThreadLocal.clean(byteStore3); + return !hasChunk; + } + + public MCAFile init(File file) throws FileNotFoundException { + String[] split = file.getName().split("\\."); + int X = Integer.parseInt(split[1]); + int Z = Integer.parseInt(split[2]); + return init(file, X, Z); + } + + public MCAFile init(File file, int mcrX, int mcrZ) throws FileNotFoundException { + if (raf != null) { + flush(true); + for (int i = 0; i < 4096; i++) { + locations[i] = 0; + } + try { + raf.close(); + } catch (IOException e) { + e.printStackTrace(); + } + raf = null; + } + deleted = false; + Arrays.fill(chunkInitialized, false); + readLocations = false; + this.X = mcrX; + this.Z = mcrZ; + this.file = file; + if (!file.exists()) { + throw new FileNotFoundException(file.getName()); + } + return this; + } + + public MCAFile init(World world, int mcrX, int mcrZ) throws FileNotFoundException { + return init(new File(world.getStoragePath().toFile(), File.separator + "regions" + File.separator + "r." + mcrX + "." + mcrZ + ".mca")); + } + + public int getIndex(int chunkX, int chunkZ) { + return ((chunkX & 31) << 2) + ((chunkZ & 31) << 7); + } + + + private RandomAccessFile getRaf() throws FileNotFoundException { + if (this.raf == null) { + this.raf = new RandomAccessFile(file, "rw"); + } + return this.raf; + } + + private void readHeader() throws IOException { + if (!readLocations) { + readLocations = true; + getRaf(); + if (raf.length() < 8192) { + raf.setLength(8192); + } else { + raf.seek(0); + raf.readFully(locations); + } + } + } + + public void clear() { + if (raf != null) { + try { + raf.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + deleted = false; + readLocations = false; + Arrays.fill(chunkInitialized, false); + } + + @Override + protected void finalize() throws Throwable { + CleanableThreadLocal.clean(byteStore1); + CleanableThreadLocal.clean(byteStore2); + CleanableThreadLocal.clean(byteStore3); + super.finalize(); + } + + public void setDeleted(boolean deleted) { + this.deleted = deleted; + } + + public boolean isDeleted() { + return deleted; + } + + public int getX() { + return X; + } + + public int getZ() { + return Z; + } + + public RandomAccessFile getRandomAccessFile() { + return raf; + } + + public File getFile() { + return file; + } + + public MCAChunk getCachedChunk(int cx, int cz) { + int pair = getIndex(cx, cz); + MCAChunk chunk = chunks[pair]; + if (chunk != null && chunkInitialized[pair]) { + return chunk; + } + return null; + } + + public void setChunk(MCAChunk chunk) { + int cx = chunk.getX(); + int cz = chunk.getZ(); + int pair = getIndex(cx, cz); + chunks[pair] = chunk; + } + + public MCAChunk getChunk(int cx, int cz) throws IOException { + int pair = getIndex(cx, cz); + MCAChunk chunk = chunks[pair]; + if (chunk == null) { + Object lock = locks[pair]; + synchronized (lock) { + chunk = chunks[pair]; + if (chunk == null) { + chunk = new MCAChunk(); + chunk.setPosition(cx, cz); + chunks[pair] = chunk; + } + } + } else if (chunkInitialized[pair]) { + return chunk; + } + synchronized (chunk) { + if (!chunkInitialized[pair]) { + readChunk(chunk, pair); + chunkInitialized[pair] = true; + } + } + return chunk; + } + + private MCAChunk readChunk(MCAChunk chunk, int i) throws IOException { + int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF))) << 12; + if (offset == 0) { + return null; + } + int size = (locations[i + 3] & 0xFF) << 12; + try (NBTInputStream nis = getChunkIS(offset)) { + chunk.read(nis, false); + } + System.out.println("TODO multithreaded"); // TODO + return chunk; + } + + /** + * CX, CZ, OFFSET, SIZE + * + * @param onEach + * @throws IOException + */ + public void forEachSortedChunk(RunnableVal4 onEach) throws IOException { + char[] offsets = new char[(int) (raf.length() / 4096) - 2]; + Arrays.fill(offsets, Character.MAX_VALUE); + char i = 0; + for (int z = 0; z < 32; z++) { + for (int x = 0; x < 32; x++, i += 4) { + int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF))) - 2; + int size = locations[i + 3] & 0xFF; + if (size != 0) { + if (offset < offsets.length) { + offsets[offset] = i; + } else { + Fawe.debug("Ignoring invalid offset " + offset); + } + } + } + } + for (i = 0; i < offsets.length; i++) { + int index = offsets[i]; + if (index != Character.MAX_VALUE) { + int offset = i + 2; + int size = locations[index + 3] & 0xFF; + int index2 = index >> 2; + int x = (index2) & 31; + int z = (index2) >> 5; + onEach.run(x, z, offset << 12, size << 12); + } + } + } + + /** + * @param onEach cx, cz, offset, size + */ + public void forEachChunk(RunnableVal4 onEach) { + int i = 0; + for (int z = 0; z < 32; z++) { + for (int x = 0; x < 32; x++, i += 4) { + int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF))); + int size = locations[i + 3] & 0xFF; + if (size != 0) { + onEach.run(x, z, offset << 12, size << 12); + } + } + } + } + + public void forEachChunk(Consumer onEach) { + int i = 0; + for (int z = 0; z < 32; z++) { + for (int x = 0; x < 32; x++, i += 4) { + int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF))); + int size = locations[i + 3] & 0xFF; + if (size != 0) { + try { + onEach.accept(getChunk(x, z)); + } catch (Throwable ignore) { + } + } + } + } + } + + public int getOffset(int cx, int cz) { + int i = getIndex(cx, cz); + int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i + 2] & 0xFF))); + return offset << 12; + } + + public int getSize(int cx, int cz) { + int i = getIndex(cx, cz); + return (locations[i + 3] & 0xFF) << 12; + } + + public byte[] getChunkCompressedBytes(int offset) throws IOException { + if (offset == 0) { + return null; + } + synchronized (raf) { + raf.seek(offset); + int size = raf.readInt(); + int compression = raf.read(); + byte[] data = new byte[size]; + raf.readFully(data); + return data; + } + } + + private NBTInputStream getChunkIS(int offset) throws IOException { + try { + byte[] data = getChunkCompressedBytes(offset); + FastByteArrayInputStream bais = new FastByteArrayInputStream(data); + InflaterInputStream iis = new InflaterInputStream(bais, new Inflater(), 1); + fieldBuf2.set(iis, byteStore2.get()); + BufferedInputStream bis = new BufferedInputStream(iis); + NBTInputStream nis = new NBTInputStream(bis); + fieldBuf3.set(nis, byteStore3.get()); + return nis; + } catch (IllegalAccessException unlikely) { + unlikely.printStackTrace(); + return null; + } + } + + public void streamChunk(int cx, int cz, StreamDelegate delegate) throws IOException { + streamChunk(getOffset(cx, cz), delegate); + } + + public void streamChunk(int offset, StreamDelegate delegate) throws IOException { + byte[] data = getChunkCompressedBytes(offset); + streamChunk(data, delegate); + } + + public void streamChunk(byte[] data, StreamDelegate delegate) throws IOException { + if (data != null) { + try { + FastByteArrayInputStream bais = new FastByteArrayInputStream(data); + InflaterInputStream iis = new InflaterInputStream(bais, new Inflater(), 1); + fieldBuf2.set(iis, byteStore2.get()); + BufferedInputStream bis = new BufferedInputStream(iis); + NBTInputStream nis = new NBTInputStream(bis); + fieldBuf3.set(nis, byteStore3.get()); + nis.readNamedTagLazy(delegate); + } catch (IllegalAccessException unlikely) { + unlikely.printStackTrace(); + } + } + } + + /** + * @param onEach chunk + */ + public void forEachCachedChunk(Consumer onEach) { + for (int i = 0; i < chunks.length; i++) { + MCAChunk chunk = chunks[i]; + if (chunk != null && this.chunkInitialized[i]) { + onEach.accept(chunk); + } + } + } + + public List getCachedChunks() { + int size = 0; + for (int i = 0; i < chunks.length; i++) { + if (chunks[i] != null && this.chunkInitialized[i]) size++; + } + ArrayList list = new ArrayList<>(size); + for (int i = 0; i < chunks.length; i++) { + MCAChunk chunk = chunks[i]; + if (chunk != null && this.chunkInitialized[i]) { + list.add(chunk); + } + } + return list; + } + + private byte[] toBytes(MCAChunk chunk) throws Exception { + if (chunk.isDeleted()) { + return null; + } + byte[] uncompressed = chunk.toBytes(byteStore3.get()); + byte[] compressed = MainUtil.compress(uncompressed, byteStore2.get(), null); + return compressed; + } + + private byte[] getChunkBytes(int cx, int cz) throws Exception { + MCAChunk mca = getCachedChunk(cx, cz); + if (mca == null) { + int offset = getOffset(cx, cz); + if (offset == 0) { + return null; + } + return getChunkCompressedBytes(offset); + } + return toBytes(mca); + } + + + private void writeSafe(RandomAccessFile raf, int offset, byte[] data) throws IOException { + int len = data.length + 5; + raf.seek(offset); + if (raf.length() - offset < len) { + raf.setLength(((offset + len + 4095) / 4096) * 4096); + } + // Length of remaining data + raf.writeInt(data.length + 1); + // Compression type + raf.write(2); + raf.write(data); + } + + private void writeHeader(RandomAccessFile raf, int cx, int cz, int offsetMedium, int sizeByte, boolean writeTime) throws IOException { + int i = getIndex(cx, cz); + locations[i] = (byte) (offsetMedium >> 16); + locations[i + 1] = (byte) (offsetMedium >> 8); + locations[i + 2] = (byte) (offsetMedium); + locations[i + 3] = (byte) sizeByte; + raf.seek(i); + raf.write((offsetMedium >> 16)); + raf.write((offsetMedium >> 8)); + raf.write((offsetMedium >> 0)); + raf.write(sizeByte); + raf.seek(i + 4096); + if (offsetMedium == 0 && sizeByte == 0) { + raf.writeInt(0); + } else { + raf.writeInt((int) (System.currentTimeMillis() / 1000L)); + } + } + + public void close() { + if (raf == null) return; + synchronized (raf) { + if (raf != null) { + flush(true); + try { + raf.close(); + } catch (IOException e) { + e.printStackTrace(); + } + raf = null; + } + } + } + + public boolean isModified() { + if (isDeleted()) { + return true; + } + for (int i = 0; i < chunks.length; i++) { + MCAChunk chunk = chunks[i]; + if (chunk != null && this.chunkInitialized[i]) { + if (chunk.isModified() || chunk.isDeleted()) { + return true; + } + } + } + return false; + } + + /** + * Write the chunk to the file + * @param wait - If the flush method needs to wait for the pool + */ + public void flush(boolean wait) { + synchronized (raf) { + // If the file is marked as deleted, nothing is written + if (isDeleted()) { + clear(); + file.delete(); + return; + } + + // Chunks that need to be relocated + Int2ObjectOpenHashMap relocate = new Int2ObjectOpenHashMap<>(); + // The position of each chunk + final Int2ObjectOpenHashMap offsetMap = new Int2ObjectOpenHashMap<>(); // Offset -> + // The data of each modified chunk + final Int2ObjectOpenHashMap compressedMap = new Int2ObjectOpenHashMap<>(); + // The data of each chunk that needs to be moved + final Int2ObjectOpenHashMap append = new Int2ObjectOpenHashMap<>(); + boolean[] modified = new boolean[1]; + // Get the current time for the chunk timestamp + long now = System.currentTimeMillis(); + + // Load the chunks into the append or compressed map + final ForkJoinPool finalPool = this.pool; + forEachCachedChunk(chunk -> { + if (chunk.isModified() || chunk.isDeleted()) { + modified[0] = true; + chunk.setLastUpdate(now); + if (!chunk.isDeleted()) { + MCAFile.this.pool.submit(() -> { + try { + byte[] compressed = toBytes(chunk); + int pair = MathMan.pair((short) (chunk.getX() & 31), (short) (chunk.getZ() & 31)); + Int2ObjectOpenHashMap map; + if (getOffset(chunk.getX(), chunk.getZ()) == 0) { + map = append; + } else { + map = compressedMap; + } + synchronized (map) { + map.put(pair, compressed); + } + } catch (Throwable e) { + e.printStackTrace(); + } + }); + } + } + }); + + // If any changes were detected + if (modified[0]) { + file.setLastModified(now); + + // Load the offset data into the offset map + forEachChunk(new RunnableVal4() { + @Override + public void run(Integer cx, Integer cz, Integer offset, Integer size) { + short pair1 = MathMan.pairByte((byte) (cx & 31), (byte) (cz & 31)); + short pair2 = (short) (size >> 12); + offsetMap.put((int) offset, (Integer) MathMan.pair(pair1, pair2)); + } + }); + // Wait for previous tasks + pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + + + int start = 8192; + int written = start; + int end = 8192; + int nextOffset = 8192; + try { + for (int count = 0; count < offsetMap.size(); count++) { + // Get the previous position of the next chunk + Integer loc = offsetMap.get(nextOffset); + while (loc == null) { + nextOffset += 4096; + loc = offsetMap.get(nextOffset); + } + int offset = nextOffset; + + // Get the x/z from the paired location + short cxz = MathMan.unpairX(loc); + int cx = MathMan.unpairShortX(cxz); + int cz = MathMan.unpairShortY(cxz); + + // Get the size from the pair + int size = MathMan.unpairY(loc) << 12; + + nextOffset += size; + end = Math.min(start + size, end); + int pair = getIndex(cx, cz); + byte[] newBytes = relocate.get(pair); + + // newBytes is null if the chunk isn't modified or marked for moving + if (newBytes == null) { + MCAChunk cached = getCachedChunk(cx, cz); + // If the previous offset marks the current write position (start) then we only write the header + if (offset == start) { + if (cached == null || !cached.isModified()) { + writeHeader(raf, cx, cz, start >> 12, size >> 12, true); + start += size; + written = start + size; + continue; + } else { + newBytes = compressedMap.get(pair); + } + } else { + // The chunk needs to be moved, fetch the data if necessary + newBytes = compressedMap.get(pair); + if (newBytes == null) { + if (cached == null || !cached.isDeleted()) { + newBytes = getChunkCompressedBytes(getOffset(cx, cz)); + } + } + } + } + + if (newBytes == null) { + writeHeader(raf, cx, cz, 0, 0, false); + continue; + } + + // The length to be written (compressed data + 5 byte chunk header) + int len = newBytes.length + 5; + int oldSize = (size + 4095) >> 12; + int newSize = (len + 4095) >> 12; + int nextOffset2 = end; + + // If the current write position (start) + length of data to write (len) are longer than the position of the next chunk, we need to move the next chunks + while (start + len > end) { + Integer nextLoc = offsetMap.get(nextOffset2); + if (nextLoc != null) { + short nextCXZ = MathMan.unpairX(nextLoc); + int nextCX = MathMan.unpairShortX(nextCXZ); + int nextCZ = MathMan.unpairShortY(nextCXZ); + MCAChunk cached = getCachedChunk(nextCX, nextCZ); + if (cached == null || !cached.isModified()) { + byte[] nextBytes = getChunkCompressedBytes(nextOffset2); + relocate.put(MathMan.pair((short) (nextCX & 31), (short) (nextCZ & 31)), nextBytes); + } + int nextSize = MathMan.unpairY(nextLoc) << 12; + end += nextSize; + nextOffset2 += nextSize; + } else { + end += 4096; + nextOffset2 += 4096; + } + } + // Write the chunk + chunk header + writeSafe(raf, start, newBytes); + // Write the location data (beginning of file) + writeHeader(raf, cx, cz, start >> 12, newSize, true); + + written = start + newBytes.length + 5; + start += newSize << 12; + } + + // Write all the chunks which need to be appended + if (!append.isEmpty()) { + for (Int2ObjectMap.Entry entry : append.int2ObjectEntrySet()) { + int pair = entry.getIntKey(); + short cx = MathMan.unpairX(pair); + short cz = MathMan.unpairY(pair); + byte[] bytes = entry.getValue(); + int len = bytes.length + 5; + int newSize = (len + 4095) >> 12; + writeSafe(raf, start, bytes); + writeHeader(raf, cx, cz, start >> 12, newSize, true); + written = start + bytes.length + 5; + start += newSize << 12; + } + } + // Round the file length, since the vanilla server doesn't like it for some reason + raf.setLength(4096 * ((written + 4095) / 4096)); + if (raf instanceof BufferedRandomAccessFile) { + ((BufferedRandomAccessFile) raf).flush(); + } + raf.close(); + } catch (Throwable e) { + e.printStackTrace(); + } + if (wait) { + pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + } + } + } + } +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/mcatest/MCATest.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/mcatest/MCATest.java new file mode 100644 index 000000000..8c962316a --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/mcatest/MCATest.java @@ -0,0 +1,14 @@ +package com.boydti.fawe.jnbt.anvil.mcatest; + +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; + +import java.io.File; +import java.io.IOException; + +public class MCATest { + public MCATest() throws IOException { + File file = new File("plugins/FastAsyncWorldEdit/tobitower.schematic"); + Clipboard loaded = ClipboardFormats.findByFile(file).load(file); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/InfoReader.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/InfoReader.java new file mode 100644 index 000000000..1f8091686 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/InfoReader.java @@ -0,0 +1,12 @@ +package com.boydti.fawe.jnbt.streamer; + +import java.io.IOException; + +public interface InfoReader extends StreamReader { + void apply(int length, int type) throws IOException; + + @Override + default void apply(int i, Integer value) throws IOException { + apply(i, value.intValue()); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/IntValueReader.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/IntValueReader.java new file mode 100644 index 000000000..fad915aad --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/IntValueReader.java @@ -0,0 +1,12 @@ +package com.boydti.fawe.jnbt.streamer; + +import java.io.IOException; + +public interface IntValueReader extends ValueReader { + void applyInt(int index, int value) throws IOException; + + @Override + default void apply(int index, Integer value) throws IOException { + applyInt(index, value); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LazyReader.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LazyReader.java new file mode 100644 index 000000000..963daab54 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LazyReader.java @@ -0,0 +1,10 @@ +package com.boydti.fawe.jnbt.streamer; + +import com.sk89q.jnbt.NBTInputStream; + +import java.io.DataInputStream; +import java.io.IOException; + +public interface LazyReader extends StreamReader { + void apply(int index, NBTInputStream stream) throws IOException; +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LongValueReader.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LongValueReader.java new file mode 100644 index 000000000..944317a7a --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LongValueReader.java @@ -0,0 +1,12 @@ +package com.boydti.fawe.jnbt.streamer; + +import java.io.IOException; + +public interface LongValueReader extends ValueReader { + void applyLong(int index, long value) throws IOException; + + @Override + default void apply(int index, Long value) throws IOException { + applyLong(index, value); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamDelegate.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamDelegate.java new file mode 100644 index 000000000..1b0cc5b9a --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamDelegate.java @@ -0,0 +1,224 @@ +package com.boydti.fawe.jnbt.streamer; + +import com.sk89q.jnbt.NBTConstants; +import com.sk89q.jnbt.NBTInputStream; + +import java.io.DataInputStream; +import java.io.IOException; + +public class StreamDelegate { + private static final byte[][] ZERO_KEYS = new byte[0][]; + private static final StreamDelegate[] ZERO_VALUES = new StreamDelegate[0]; + + private byte[] buffer; + private byte[][] keys; + private StreamDelegate[] values; + + private LazyReader lazyReader; + private ValueReader elemReader; + private InfoReader infoReader; + private ValueReader valueReader; + + public StreamDelegate() { + keys = ZERO_KEYS; + values = ZERO_VALUES; + } + + public StreamDelegate addAndGetParent(String name) { + add(name); + return this; + } + + public StreamDelegate add() { + return add(""); + } + + public StreamDelegate add(String name) { + return add(name, new StreamDelegate()); + } + + private StreamDelegate add(String name, StreamDelegate scope) { + if (valueReader != null) { + System.out.println("Scope " + name + " | " + scope + " may not run, as the stream is only read once, and a value reader is already set"); + } + byte[] bytes = name.getBytes(NBTConstants.CHARSET); + int maxSize = bytes.length; + + byte[][] tmpKeys = new byte[keys.length + 1][]; + StreamDelegate[] tmpValues = new StreamDelegate[keys.length + 1]; + tmpKeys[keys.length] = bytes; + tmpValues[keys.length] = scope; + + int i = 0; + for (; i < keys.length; i++) { + byte[] key = keys[i]; + if (key.length >= bytes.length) { + tmpKeys[i] = bytes; + tmpValues[i] = scope; + break; + } + tmpKeys[i] = key; + tmpValues[i] = values[i]; + maxSize = Math.max(maxSize, key.length); + } + for (; i < keys.length; i++) { + byte[] key = keys[i]; + tmpKeys[i + 1] = key; + tmpValues[i + 1] = values[i]; + maxSize = Math.max(maxSize, key.length); + } + + this.keys = tmpKeys; + this.values = tmpValues; + if (this.buffer == null || buffer.length < maxSize) { + buffer = new byte[maxSize]; + } + return scope; + } + + public StreamDelegate get0() { + if (keys.length > 0 && keys[0].length == 0) { + return values[0]; + } + return null; + } + + public StreamDelegate get(DataInputStream is) throws IOException { + int nameLength = is.readShort() & 0xFFFF; + if (nameLength == 0 && keys.length > 0 && keys[0].length == 0) { + return values[0]; + } + if (nameLength > buffer.length) { + is.skipBytes(nameLength); + return null; + } + int index = 0; + outer: + switch (keys.length) { + case 0: + break; + default: { + for (; index < keys.length; index++) { + byte[] key = keys[index]; + if (key.length < nameLength) continue; + if (key.length == nameLength) { + break; + } else { + break outer; + } + } + if (index != keys.length - 1) { + int max; + for (max = index + 1; max < keys.length;) { + byte[] key = keys[max]; + if (key.length == nameLength) { + max++; + continue; + } + break; + } + if (index != max) { + is.readFully(buffer, 0, nameLength); + middle: + for (int i = index; i < max; i++) { + byte[] key = keys[i]; + for (int j = 0; j < nameLength; j++) { + if (buffer[j] != key[j]) { + continue middle; + } + } + return values[i]; + } + return null; + } + } + } + case 1: { + byte[] key = keys[index]; + if (key.length == nameLength) { + int i = 0; + for (; nameLength > 0; nameLength--, i++) { + byte b = is.readByte(); + buffer[i] = b; + if (b != key[i]) { + nameLength--; + break outer; + } + + } + return values[index]; + } + break; + } + } + is.skipBytes(nameLength); + return null; + } + + public StreamDelegate withLong(LongValueReader valueReader) { + return withElem(valueReader); + } + + public StreamDelegate withInt(IntValueReader valueReader) { + return withElem(valueReader); + } + + public StreamDelegate withValue(ValueReader valueReader) { + if (keys.length != 0) { + System.out.println("Reader " + valueReader + " may not run, as the stream is only read once, and a value reader is already set"); + } + this.valueReader = valueReader; + return this; + } + + public StreamDelegate withStream(LazyReader lazyReader) { + this.lazyReader = lazyReader; + return this; + } + + public StreamDelegate withElem(ValueReader elemReader) { + this.elemReader = elemReader; + return this; + } + + public StreamDelegate withInfo(InfoReader infoReader) { + this.infoReader = infoReader; + return this; + } + + public void acceptRoot(NBTInputStream is, int type, int depth) throws IOException { + if (lazyReader != null) { + lazyReader.apply(0, is); + } else if (elemReader != null) { + Object raw = is.readTagPayloadRaw(type, depth); + elemReader.apply(0, raw); + } else if (valueReader != null) { + Object raw = is.readTagPayloadRaw(type, depth); + valueReader.apply(0, raw); + } else { + is.readTagPaylodLazy(type, depth + 1, this); + } + } + + public ValueReader getValueReader() { + return valueReader; + } + + public ValueReader getElemReader() { + return elemReader; + } + + public void acceptInfo(int length, int type) throws IOException { + if (infoReader != null) { + infoReader.apply(length, type); + } + } + + public boolean acceptLazy(int length, NBTInputStream is) throws IOException { + if (lazyReader != null) { + lazyReader.apply(length, is); + return true; + } + return false; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamReader.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamReader.java new file mode 100644 index 000000000..dadcf005e --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamReader.java @@ -0,0 +1,7 @@ +package com.boydti.fawe.jnbt.streamer; + +import java.io.IOException; + +public interface StreamReader { + void apply(int i, T value) throws IOException; +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ValueReader.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ValueReader.java new file mode 100644 index 000000000..63107c0f4 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ValueReader.java @@ -0,0 +1,23 @@ +package com.boydti.fawe.jnbt.streamer; + +import java.io.IOException; + +public interface ValueReader extends StreamReader { + void apply(int index, T value) throws IOException; + + default void applyInt(int index, int value) throws IOException { + apply(index, (T) (Integer) value); + } + + default void applyLong(int index, long value) throws IOException { + apply(index, (T) (Long) value); + } + + default void applyFloat(int index, float value) throws IOException { + apply(index, (T) (Float) value); + } + + default void applyDouble(int index, double value) throws IOException { + apply(index, (T) (Double) value); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/FaweLimit.java b/worldedit-core/src/main/java/com/boydti/fawe/object/FaweLimit.java index bd8bec10b..87d2f84a0 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/FaweLimit.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/FaweLimit.java @@ -1,5 +1,7 @@ package com.boydti.fawe.object; +import com.boydti.fawe.FaweCache; + import java.util.Set; public class FaweLimit { @@ -51,6 +53,47 @@ public class FaweLimit { public boolean MAX_ITERATIONS() { return true; } + + @Override + public boolean isUnlimited() { + return true; + } + + public void THROW_MAX_CHANGES() { + } + + public void THROW_MAX_FAILS() { + } + + public void THROW_MAX_CHECKS() { + } + + public void THROW_MAX_ITERATIONS() { + } + + public void THROW_MAX_BLOCKSTATES() { + } + + public void THROW_MAX_ENTITIES() { + } + + public void THROW_MAX_CHANGES(int amt) { + } + + public void THROW_MAX_FAILS(int amt) { + } + + public void THROW_MAX_CHECKS(int amt) { + } + + public void THROW_MAX_ITERATIONS(int amt) { + } + + public void THROW_MAX_BLOCKSTATES(int amt) { + } + + public void THROW_MAX_ENTITIES(int amt) { + } }; MAX.SPEED_REDUCTION = 0; MAX.INVENTORY_MODE = 0; @@ -92,6 +135,54 @@ public class FaweLimit { return MAX_ENTITIES-- > 0; } + public void THROW_MAX_CHANGES() { + if (MAX_CHANGES-- <= 0) throw FaweCache.MAX_CHANGES; + } + + public void THROW_MAX_FAILS() { + if (MAX_FAILS-- <= 0) throw FaweCache.MAX_CHECKS; + } + + public void THROW_MAX_CHECKS() { + if (MAX_CHECKS-- <= 0) throw FaweCache.MAX_CHECKS; + } + + public void THROW_MAX_ITERATIONS() { + if (MAX_ITERATIONS-- <= 0) throw FaweCache.MAX_ITERATIONS; + } + + public void THROW_MAX_BLOCKSTATES() { + if (MAX_BLOCKSTATES-- <= 0) throw FaweCache.MAX_TILES; + } + + public void THROW_MAX_ENTITIES() { + if (MAX_ENTITIES-- <= 0) throw FaweCache.MAX_ENTITIES; + } + + public void THROW_MAX_CHANGES(int amt) { + if ((MAX_CHANGES -= amt) <= 0) throw FaweCache.MAX_CHANGES; + } + + public void THROW_MAX_FAILS(int amt) { + if ((MAX_FAILS -= amt) <= 0) throw FaweCache.MAX_CHECKS; + } + + public void THROW_MAX_CHECKS(int amt) { + if ((MAX_CHECKS -= amt) <= 0) throw FaweCache.MAX_CHECKS; + } + + public void THROW_MAX_ITERATIONS(int amt) { + if ((MAX_ITERATIONS -= amt) <= 0) throw FaweCache.MAX_ITERATIONS; + } + + public void THROW_MAX_BLOCKSTATES(int amt) { + if ((MAX_BLOCKSTATES -= amt) <= 0) throw FaweCache.MAX_TILES; + } + + public void THROW_MAX_ENTITIES(int amt) { + if ((MAX_ENTITIES -= amt) <= 0) throw FaweCache.MAX_ENTITIES; + } + public boolean isUnlimited() { return MAX_CHANGES == Integer.MAX_VALUE && MAX_FAILS == Integer.MAX_VALUE && diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/Lazy.java b/worldedit-core/src/main/java/com/boydti/fawe/object/Lazy.java new file mode 100644 index 000000000..0110f624b --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/Lazy.java @@ -0,0 +1,8 @@ +package com.boydti.fawe.object; + +import java.util.function.Supplier; + +public interface Lazy extends Supplier { + Supplier init(); + public default T get() { return init().get(); } +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ErodeBrush.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ErodeBrush.java index 0b70366cd..d7c7ff3f6 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ErodeBrush.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ErodeBrush.java @@ -1,11 +1,11 @@ package com.boydti.fawe.object.brush; import com.boydti.fawe.object.clipboard.CPUOptimizedClipboard; -import com.boydti.fawe.object.clipboard.FaweClipboard; -import com.boydti.fawe.object.clipboard.OffsetFaweClipboard; +import com.boydti.fawe.object.clipboard.LinearClipboard; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.command.tool.brush.Brush; +import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.util.Direction; @@ -39,23 +39,23 @@ public class ErodeBrush implements Brush { public void erosion(final EditSession es, int erodeFaces, int erodeRec, int fillFaces, int fillRec, BlockVector3 target, double size) { int brushSize = (int) size + 1; int brushSizeSquared = (int) (size * size); - int dimension = brushSize * 2 + 1; - FaweClipboard buffer1 = new OffsetFaweClipboard(new CPUOptimizedClipboard(dimension, dimension, dimension), brushSize); - FaweClipboard buffer2 = new OffsetFaweClipboard(new CPUOptimizedClipboard(dimension, dimension, dimension), brushSize); + BlockVector3 dimension = BlockVector3.ONE.multiply(brushSize * 2 + 1); + Clipboard buffer1 = new CPUOptimizedClipboard(dimension); + Clipboard buffer2 = new CPUOptimizedClipboard(dimension); final int bx = target.getBlockX(); final int by = target.getBlockY(); final int bz = target.getBlockZ(); - for (int x = -brushSize; x <= brushSize; x++) { + for (int x = -brushSize, relx = 0; x <= brushSize; x++, relx++) { int x0 = x + bx; - for (int y = -brushSize; y <= brushSize; y++) { + for (int y = -brushSize, rely = 0; y <= brushSize; y++, rely++) { int y0 = y + by; - for (int z = -brushSize; z <= brushSize; z++) { + for (int z = -brushSize, relz = 0; z <= brushSize; z++, relz++) { int z0 = z + bz; BlockState state = es.getBlock(x0, y0, z0); - buffer1.setBlock(x, y, z, state); - buffer2.setBlock(x, y, z, state); + buffer1.setBlock(relx, rely, relz, state); + buffer2.setBlock(relx, rely, relz, state); } } } @@ -70,30 +70,28 @@ public class ErodeBrush implements Brush { fillIteration(brushSize, brushSizeSquared, fillFaces, swap % 2 == 0 ? buffer1 : buffer2, swap % 2 == 1 ? buffer1 : buffer2); swap++; } - FaweClipboard finalBuffer = swap % 2 == 0 ? buffer1 : buffer2; + Clipboard finalBuffer = swap % 2 == 0 ? buffer1 : buffer2; - finalBuffer.forEach(new FaweClipboard.BlockReader() { - @Override - public > void run(int x, int y, int z, B block) { - es.setBlock(x + bx, y + by, z + bz, block); - } - }, true); + for (BlockVector3 pos : finalBuffer) { + BlockState block = pos.getBlock(finalBuffer); + es.setBlock(pos.getX() + bx - brushSize, pos.getY() + by - brushSize, pos.getZ() + bz - brushSize, block); + } } private void fillIteration(int brushSize, int brushSizeSquared, int fillFaces, - FaweClipboard current, FaweClipboard target) { + Clipboard current, Clipboard target) { int[] frequency = null; - for (int x = -brushSize; x <= brushSize; x++) { + for (int x = -brushSize, relx = 0; x <= brushSize; x++, relx++) { int x2 = x * x; - for (int z = -brushSize; z <= brushSize; z++) { + for (int z = -brushSize, relz = 0; z <= brushSize; z++, relz++) { int x2y2 = x2 + z * z; - for (int y = -brushSize; y <= brushSize; y++) { + for (int y = -brushSize, rely = 0; y <= brushSize; y++, rely++) { int cube = x2y2 + y * y; - target.setBlock(x, y, z, current.getBlock(x, y, z)); + target.setBlock(x, y, z, current.getBlock(relx, rely, relz)); if (cube >= brushSizeSquared) { continue; } - BaseBlock state = current.getBlock(x, y, z); + BaseBlock state = current.getFullBlock(relx, rely, relz); if (state.getBlockType().getMaterial().isMovementBlocker()) { continue; } @@ -106,7 +104,7 @@ public class ErodeBrush implements Brush { Arrays.fill(frequency, 0); } for (BlockVector3 offs : FACES_TO_CHECK) { - BaseBlock next = current.getBlock(x + offs.getBlockX(), y + offs.getBlockY(), z + offs.getBlockZ()); + BaseBlock next = current.getFullBlock(relx + offs.getBlockX(), rely + offs.getBlockY(), relz + offs.getBlockZ()); if (!next.getBlockType().getMaterial().isMovementBlocker()) { continue; } @@ -118,7 +116,7 @@ public class ErodeBrush implements Brush { } } if (total >= fillFaces) { - target.setBlock(x, y, z, highestState); + target.setBlock(relx, rely, relz, highestState); } } } @@ -126,19 +124,20 @@ public class ErodeBrush implements Brush { } private void erosionIteration(int brushSize, int brushSizeSquared, int erodeFaces, - FaweClipboard current, FaweClipboard target) { + Clipboard current, Clipboard target) { int[] frequency = null; - for (int x = -brushSize; x <= brushSize; x++) { + + for (int x = -brushSize, relx = 0; x <= brushSize; x++, relx++) { int x2 = x * x; - for (int z = -brushSize; z <= brushSize; z++) { + for (int z = -brushSize, relz = 0; z <= brushSize; z++, relz++) { int x2y2 = x2 + z * z; - for (int y = -brushSize; y <= brushSize; y++) { + for (int y = -brushSize, rely = 0; y <= brushSize; y++, rely++) { int cube = x2y2 + y * y; - target.setBlock(x, y, z, current.getBlock(x, y, z)); + target.setBlock(x, y, z, current.getBlock(relx, rely, relz)); if (cube >= brushSizeSquared) { continue; } - BaseBlock state = current.getBlock(x, y, z); + BaseBlock state = current.getFullBlock(relx, rely, relz); if (!state.getBlockType().getMaterial().isMovementBlocker()) { continue; } @@ -151,7 +150,7 @@ public class ErodeBrush implements Brush { Arrays.fill(frequency, 0); } for (BlockVector3 offs : FACES_TO_CHECK) { - BaseBlock next = current.getBlock(x + offs.getBlockX(), y + offs.getBlockY(), z + offs.getBlockZ()); + BaseBlock next = current.getFullBlock(relx + offs.getBlockX(), rely + offs.getBlockY(), relz + offs.getBlockZ()); if (next.getBlockType().getMaterial().isMovementBlocker()) { continue; } @@ -163,7 +162,7 @@ public class ErodeBrush implements Brush { } } if (total >= erodeFaces) { - target.setBlock(x, y, z, highestState); + target.setBlock(relx, rely, relz, highestState); } } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/VisualExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/VisualExtent.java index 92bae872e..d6f32fb43 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/VisualExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/VisualExtent.java @@ -47,7 +47,6 @@ public class VisualExtent extends AbstractDelegateExtent { @Override public Operation commit() { IQueueExtent queue = (IQueueExtent) getExtent(); - queue.sendBlockUpdates(this.player); return null; } @@ -59,7 +58,6 @@ public class VisualExtent extends AbstractDelegateExtent { public void clear() { IQueueExtent queue = (IQueueExtent) getExtent(); - queue.clearBlockUpdates(player); queue.cancel(); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/CFIDrawer.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/CFIDrawer.java new file mode 100644 index 000000000..57715ea79 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/CFIDrawer.java @@ -0,0 +1,142 @@ +package com.boydti.fawe.object.brush.visualization.cfi; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.TextureUtil; +import com.sk89q.worldedit.world.block.BlockID; +import com.sk89q.worldedit.world.block.BlockTypes; + +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.TimeUnit; + +public final class CFIDrawer { + private final HeightMapMCAGenerator gen; + private final TextureUtil tu; + private final ForkJoinPool pool; + + public CFIDrawer(HeightMapMCAGenerator generator, TextureUtil textureUtil) { + this.gen = generator; + this.tu = textureUtil; + this.pool = new ForkJoinPool(); + } + + public CFIDrawer(HeightMapMCAGenerator generator) { + this(generator, Fawe.get().getCachedTextureUtil(false, 0, 100)); + } + + public BufferedImage draw() { + BufferedImage img = new BufferedImage(gen.getWidth(), gen.getLength(), BufferedImage.TYPE_INT_RGB); + final char[] overlay = gen.overlay == null ? gen.floor.get() : gen.overlay.get(); + final char[] floor = gen.floor.get(); + final char[] main = gen.main.get(); + final byte[] heights = gen.heights.get(); + final byte[] biomes = gen.biomes.get(); + final int waterHeight = gen.primitives.waterHeight; + final int width = gen.getWidth(); + final int length = gen.getLength(); + + int[] raw = ((DataBufferInt) img.getRaster().getDataBuffer()).getData(); + + int parallelism = pool.getParallelism(); + int size = (heights.length + parallelism - 1) / parallelism; + for (int i = 0; i < parallelism; i++) { + int start = i * size; + int end = Math.min(heights.length, start + size); + pool.submit((Runnable) () -> { + for (int index = start; index < end; index ++) { + int height = (heights[index] & 0xFF); + char ordinal; + if ((ordinal = overlay[index]) == 0) { + height--; + ordinal = floor[index]; + if (ordinal == 0) { + height--; + ordinal = main[index]; + } + } + // draw ordinal + int color; + switch (ordinal >> 4) { + case 2: + color = getAverageBiomeColor(biomes, width, index); + break; + case 78: + color = (0xDD << 16) + (0xDD << 8) + (0xDD << 0); + break; + default: + color = tu.getColor(BlockTypes.getFromStateOrdinal(ordinal)); + break; + } + int slope = getSlope(heights, width, index, height); + if (slope != 0) { + slope = (slope << 3) + (slope << 2); + int r = MathMan.clamp(((color >> 16) & 0xFF) + slope, 0, 255); + int g = MathMan.clamp(((color >> 8) & 0xFF) + slope, 0, 255); + int b = MathMan.clamp(((color >> 0) & 0xFF) + slope, 0, 255); + color = (r << 16) + (g << 8) + (b << 0); + } + if (height + 1 < waterHeight) { + char waterId = gen.primitives.waterOrdinal; + int waterColor = 0; + switch (waterId) { + case BlockID.WATER: + color = tu.averageColor((0x11 << 16) + (0x66 << 8) + (0xCC), color); + break; + case BlockID.LAVA: + color = (0xCC << 16) + (0x33 << 8) + (0); + break; + default: + color = tu.getColor(BlockTypes.getFromStateOrdinal(waterId)); + break; + } + } + raw[index] = color; + } + }); + } + pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + pool.shutdownNow(); + return img; + } + + private final int getAverageBiomeColor(byte[] biomes, int width, int index) { + int c0 = tu.getBiome(biomes[index] & 0xFF).grassCombined; + int c2 = getBiome(biomes, index + 1 + width, index); + int c1 = getBiome(biomes, index - 1 - width, index); +// int c3 = getBiome(biomes, index + width, index); +// int c4 = getBiome(biomes, index - width, index); + int r = ((c0 >> 16) & 0xFF) + ((c1 >> 16) & 0xFF) + ((c2 >> 16) & 0xFF);// + ((c3 >> 16) & 0xFF) + ((c4 >> 16) & 0xFF); + int g = ((c0 >> 8) & 0xFF) + ((c1 >> 8) & 0xFF) + ((c2 >> 8) & 0xFF);// + ((c3 >> 8) & 0xFF) + ((c4 >> 8) & 0xFF); + int b = ((c0) & 0xFF) + ((c1) & 0xFF) + ((c2) & 0xFF);// + ((c3) & 0xFF) + ((c4) & 0xFF); + r = r * 85 >> 8; + g = g * 85 >> 8; + b = b * 85 >> 8; + return (r << 16) + (g << 8) + (b); + } + + private final int getBiome(byte[] biomes, int newIndex, int index) { + if (newIndex < 0 || newIndex >= biomes.length) newIndex = index; + int biome = biomes[newIndex] & 0xFF; + return tu.getBiome(biome).grassCombined; + } + + private int getSlope(byte[] heights, int width, int index, int height) { + return ( + + getHeight(heights, index + 1, height) +// + getHeight(heights, index + width, height) + + getHeight(heights, index + width + 1, height) + - getHeight(heights, index - 1, height) +// - getHeight(heights, index - width, height) + - getHeight(heights, index - width - 1, height) + ); + } + + private int getHeight(byte[] heights, int index, int height) { + if (index < 0 || index >= heights.length) return height; + return heights[index] & 0xFF; + } +} + + 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 04107f27f..cd980c54f 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 @@ -2,8 +2,12 @@ package com.boydti.fawe.object.brush.visualization.cfi; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IBlocks; import com.boydti.fawe.beta.IChunkGet; -import com.boydti.fawe.beta.implementation.FallbackChunkGet; +import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.beta.implementation.packet.ChunkPacket; +import com.boydti.fawe.beta.implementation.blocks.FallbackChunkGet; +import com.boydti.fawe.jnbt.anvil.MCAChunk; import com.boydti.fawe.object.FaweInputStream; import com.boydti.fawe.object.FaweOutputStream; import com.boydti.fawe.object.Metadatable; @@ -15,8 +19,6 @@ import com.boydti.fawe.object.collection.DifferentialBlockBuffer; import com.boydti.fawe.object.collection.LocalBlockVector2DSet; import com.boydti.fawe.object.collection.SummedAreaTable; import com.boydti.fawe.object.exception.FaweChunkLoadException; -import com.boydti.fawe.object.exception.FaweException; -import com.boydti.fawe.object.schematic.Schematic; import com.boydti.fawe.util.CachedTextureUtil; import com.boydti.fawe.util.RandomTextureUtil; import com.boydti.fawe.util.ReflectionUtils; @@ -54,6 +56,8 @@ 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.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; + import java.awt.image.BufferedImage; import java.io.File; import java.io.FileNotFoundException; @@ -64,18 +68,18 @@ import java.util.Arrays; import java.util.List; import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Supplier; import javax.annotation.Nullable; -// TODO FIXME public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Drawable, VirtualWorld { private final MutableBlockVector3 mutable = new MutableBlockVector3(); private final DifferentialBlockBuffer blocks; protected final DifferentialArray heights; protected final DifferentialArray biomes; - protected final DifferentialArray floor; - protected final DifferentialArray main; - protected DifferentialArray overlay; + protected final DifferentialArray floor; + protected final DifferentialArray main; + protected DifferentialArray overlay; protected final CFIPrimitives primitives = new CFIPrimitives(); private CFIPrimitives oldPrimitives = new CFIPrimitives(); @@ -86,8 +90,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int worldThickness; boolean randomVariation = true; int biomePriority; - int waterId = BlockID.WATER; - int bedrockId = BlockID.BEDROCK; + char waterOrdinal = BlockID.WATER; + char bedrockOrdinal = BlockID.BEDROCK; boolean modifiedMain; @Override @@ -225,13 +229,13 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr blocks = new DifferentialBlockBuffer(width, length); heights = new DifferentialArray<>(new byte[getArea()]); biomes = new DifferentialArray<>(new byte[getArea()]); - floor = new DifferentialArray<>(new int[getArea()]); - main = new DifferentialArray<>(new int[getArea()]); + floor = new DifferentialArray<>(new char[getArea()]); + main = new DifferentialArray<>(new char[getArea()]); - int stone = BlockID.STONE; - int grass = BlockTypes.GRASS_BLOCK.getDefaultState().with(PropertyKey.SNOWY, false).getInternalId(); - Arrays.fill(main.getIntArray(), stone); - Arrays.fill(floor.getIntArray(), grass); + char stone = BlockID.STONE; + char grass = BlockTypes.GRASS_BLOCK.getDefaultState().with(PropertyKey.SNOWY, false).getOrdinalChar(); + Arrays.fill(overlay.getCharArray(), stone); + Arrays.fill(overlay.getCharArray(), grass); } public Metadatable getMetaData() { @@ -259,10 +263,10 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr return player; } - private int[][][] getChunkArray(int x, int z) { - int[][][][][] blocksData = blocks.get(); + private char[][][] getChunkArray(int x, int z) { + char[][][][][] blocksData = blocks.get(); if (blocksData == null) return null; - int[][][][] arr = blocksData[z]; + char[][][][] arr = blocksData[z]; return arr != null ? arr[x] : null; } @@ -279,49 +283,63 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr if (viewer != null) { viewer.view(this); } -// if (chunkOffset != null && player != null) { TODO NOT IMPLEMENTED -// IQueueExtent packetQueue = SetQueue.IMP.getNewQueue(player.getWorld(), true, false); -// -// if (!packetQueue.supports(Capability.CHUNK_PACKETS)) { -// return; -// } -// -// int lenCX = (getWidth() + 15) >> 4; -// int lenCZ = (getLength() + 15) >> 4; -// -// int OX = chunkOffset.getBlockX(); -// int OZ = chunkOffset.getBlockZ(); -// -// Location position = player.getLocation(); -// int pcx = (position.getBlockX() >> 4) - OX; -// int pcz = (position.getBlockZ() >> 4) - OZ; -// -// int scx = Math.max(0, pcx - 15); -// int scz = Math.max(0, pcz - 15); -// int ecx = Math.min(lenCX - 1, pcx + 15); -// int ecz = Math.min(lenCZ - 1, pcz + 15); -// -// for (int cz = scz; cz <= ecz; cz++) { -// for (int cx = scx; cx <= ecx; cx++) { -// final int finalCX = cx; -// final int finalCZ = cz; -// TaskManager.IMP.getPublicForkJoinPool().submit(() -> { -// try { -// FaweChunk toSend = getSnapshot(finalCX, finalCZ); -// toSend.setLoc(HeightMapMCAGenerator.this, finalCX + OX, finalCZ + OZ); -// packetQueue.sendChunkUpdate(toSend, player); -// } catch (Throwable e) { -// e.printStackTrace(); -// } -// }); -// } -// } -// } + if (chunkOffset != null && player != null) { + World world = player.getWorld(); + + int lenCX = (getWidth() + 15) >> 4; + int lenCZ = (getLength() + 15) >> 4; + + + Location position = player.getLocation(); + int pcx = (position.getBlockX() >> 4) - chunkOffset.getBlockX(); + int pcz = (position.getBlockZ() >> 4) - chunkOffset.getBlockZ(); + + int scx = Math.max(0, pcx - 15); + int scz = Math.max(0, pcz - 15); + int ecx = Math.min(lenCX - 1, pcx + 15); + int ecz = Math.min(lenCZ - 1, pcz + 15); + + for (int chunkZ = scz; chunkZ <= ecz; chunkZ++) { + for (int chunkX = scx; chunkX <= ecx; chunkX++) { + + refreshChunk(world, chunkX, chunkZ); + } + } + } + } + + public void refreshChunk(World world, int chunkX, int chunkZ) { + Supplier blocksSupplier = () -> getChunk(chunkX, chunkZ); + + int realChunkX = chunkX + chunkOffset.getBlockX(); + int realChunkZ = chunkZ + chunkOffset.getBlockZ(); + + ChunkPacket packet = new ChunkPacket(realChunkX, realChunkZ, blocksSupplier, true); + world.sendFakeChunk(player, packet); } @Override - public void sendChunk(int chunkX, int chunkZ, int bitMask) { - throw new UnsupportedOperationException("TODO NOT IMPLEMENTED"); // add method to adapter to send custom chunk + public void sendFakeChunk(@Nullable Player player, ChunkPacket packet) { + if (this.player != null) { + player.getWorld().sendFakeChunk(player, packet); + } + } + + @Override + public void refreshChunk(int chunkX, int chunkZ) { + if (chunkOffset != null && player != null) { + refreshChunk(player.getWorld(), chunkX, chunkZ); + } + } + + public IChunkSet getChunk(int chunkX, int chunkZ) { + // TODO don't generate new Writeable MCA chunk + System.out.println("TODO don't generate new Writeable MCA chunk"); + MCAChunk tmp = new MCAChunk(); + int bx = chunkX << 4; + int bz = chunkZ << 4; + write(tmp, bx, bx + 15, bz, bz + 15); + return tmp; } public TextureUtil getRawTextureUtil() { @@ -349,8 +367,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } } - public void setBedrockId(int bedrockId) { - this.primitives.bedrockId = bedrockId; + public void setBedrock(BlockState bedrock) { + this.primitives.bedrockOrdinal = bedrock.getOrdinalChar(); } public void setFloorThickness(int floorThickness) { @@ -365,8 +383,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr this.primitives.waterHeight = waterHeight; } - public void setWaterId(int waterId) { - this.primitives.waterId = waterId; + public void setWater(BlockState water) { + this.primitives.waterOrdinal = water.getOrdinalChar(); } public void setTextureRandomVariation(boolean randomVariation) { @@ -390,10 +408,10 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } public void smooth(BlockVector2 min, BlockVector2 max, int radius, int iterations) { - int snowLayer = BlockTypes.SNOW.getInternalId(); - int snowBlock = BlockTypes.SNOW_BLOCK.getInternalId(); + int snowLayer = BlockTypes.SNOW.getDefaultState().getOrdinalChar(); + int snowBlock = BlockTypes.SNOW_BLOCK.getDefaultState().getOrdinalChar(); - int[] floor = this.floor.get(); + char[] floor = this.floor.get(); byte[] heights = this.heights.get(); int width = getWidth(); @@ -421,8 +439,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int index = zIndex + minX; for (int x = minX; x <= maxX; x++, index++, localIndex++) { int combined = floor[index]; - if (BlockTypes.getFromStateId(combined) == BlockTypes.SNOW) { - layers[localIndex] = (char) (((heights[index] & 0xFF) << 3) + (floor[index] >> BlockTypes.BIT_OFFSET) - 7); + if (BlockTypes.getFromStateOrdinal(combined) == BlockTypes.SNOW) { + layers[localIndex] = (char) (((heights[index] & 0xFF) << 3) + (floor[index] >> BlockTypesCache.BIT_OFFSET) - 7); } else { layers[localIndex] = (char) ((heights[index] & 0xFF) << 3); } @@ -453,16 +471,15 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr private final void setLayerHeight(int index, int blockHeight, int layerHeight) { int floorState = floor.get()[index]; - BlockType type = BlockTypes.getFromStateId(floorState); - switch (type.getInternalId()) { + switch (floorState) { case BlockID.SNOW: case BlockID.SNOW_BLOCK: if (layerHeight != 0) { this.heights.setByte(index, (byte) (blockHeight + 1)); - this.floor.setInt(index, BlockTypes.SNOW.getInternalId() + layerHeight); + this.floor.setInt(index, BlockTypes.SNOW.getDefaultState().getOrdinalChar() + layerHeight); } else { this.heights.setByte(index, (byte) blockHeight); - this.floor.setInt(index, BlockTypes.SNOW_BLOCK.getInternalId()); + this.floor.setInt(index, BlockTypes.SNOW_BLOCK.getDefaultState().getOrdinalChar()); } break; default: @@ -479,16 +496,15 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr private final void setLayerHeightRaw(int index, int blockHeight, int layerHeight) { int floorState = floor.get()[index]; - BlockType type = BlockTypes.getFromStateId(floorState); - switch (type.getInternalId()) { + switch (floorState) { case BlockID.SNOW: case BlockID.SNOW_BLOCK: if (layerHeight != 0) { this.heights.getByteArray()[index] = (byte) (blockHeight + 1); - this.floor.getIntArray()[index] = BlockTypes.SNOW.getInternalId() + layerHeight; + this.overlay.getCharArray()[index] = (char) (BlockTypes.SNOW.getDefaultState().getOrdinalChar() + layerHeight); } else { this.heights.getByteArray()[index] = (byte) blockHeight; - this.floor.getIntArray()[index] = BlockTypes.SNOW_BLOCK.getInternalId(); + this.overlay.getCharArray()[index] = BlockTypes.SNOW_BLOCK.getDefaultState().getOrdinalChar(); } break; default: @@ -498,7 +514,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } private void smooth(BufferedImage img, Mask mask, boolean white, int radius, int iterations) { - int[] floor = this.floor.get(); + char[] floor = this.floor.get(); byte[] heights = this.heights.get(); long[] copy = new long[heights.length]; @@ -511,8 +527,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int j = 0; j < iterations; j++) { for (int i = 0; i < heights.length; i++) { int combined = floor[i]; - if (BlockTypes.getFromStateId(combined) == BlockTypes.SNOW) { - layers[i] = (char) (((heights[i] & 0xFF) << 3) + (floor[i] >> BlockTypes.BIT_OFFSET) - 7); + if (BlockTypes.getFromStateOrdinal(combined) == BlockTypes.SNOW) { + layers[i] = (char) (((heights[i] & 0xFF) << 3) + (floor[i] >> BlockTypesCache.BIT_OFFSET) - 7); } else { layers[i] = (char) ((heights[i] & 0xFF) << 3); } @@ -609,12 +625,11 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } } Clipboard clipboard = holder.getClipboard(); - Schematic schematic = new Schematic(clipboard); Transform transform = holder.getTransform(); if (transform.isIdentity()) { - schematic.paste(this, mutable, false); + clipboard.paste(this, mutable, false); } else { - schematic.paste(this, mutable, false, transform); + clipboard.paste(this, mutable, false, transform); } if (x + distance < getWidth()) { x += distance; @@ -658,12 +673,11 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } } Clipboard clipboard = holder.getClipboard(); - Schematic schematic = new Schematic(clipboard); Transform transform = holder.getTransform(); if (transform.isIdentity()) { - schematic.paste(this, mutable, false); + clipboard.paste(this, mutable, false); } else { - schematic.paste(this, mutable, false, transform); + clipboard.paste(this, mutable, false, transform); } if (x + distance < getWidth()) { x += distance; @@ -704,7 +718,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr return setBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ(), block); } - private boolean setBlock(int x, int y, int z, int combined) { + private boolean setBlock(int x, int y, int z, char combined) { int index = z * getWidth() + x; if (index < 0 || index >= getArea()) return false; int height = heights.getByte(index) & 0xFF; @@ -713,8 +727,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr floor.setInt(index, combined); return true; case 1: - int mainId = main.getInt(index); - int floorId = floor.getInt(index); + char mainId = overlay.getChar(index); + char floorId = overlay.getChar(index); floor.setInt(index, combined); byte currentHeight = heights.getByte(index); @@ -751,13 +765,13 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr // return getSnapshot(null, chunkX, chunkZ); // } // -// private FaweChunk getSnapshot(final WritableMCAChunk chunk, int chunkX, int chunkZ) { -// return new LazyFaweChunk(this, chunkX, chunkZ) { +// private FaweChunk getSnapshot(final MCAChunk chunk, int chunkX, int chunkZ) { +// return new LazyFaweChunk(this, chunkX, chunkZ) { // @Override -// public WritableMCAChunk getChunk() { -// WritableMCAChunk tmp = chunk; +// public MCAChunk getChunk() { +// MCAChunk tmp = chunk; // if (tmp == null) { -// tmp = new WritableMCAChunk(); +// tmp = new MCAChunk(); // } // tmp.setLoc(HeightMapMCAGenerator.this, chunkX, chunkZ); // int cbx = chunkX << 4; @@ -773,7 +787,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr // // @Override // public void addToQueue() { -// WritableMCAChunk cached = getCachedChunk(); +// MCAChunk cached = getCachedChunk(); // if (cached != null) setChunk(cached); // } // }; @@ -870,7 +884,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int cz = scz; cz <= ecz; cz++) { for (int cx = scx; cx <= ecx; cx++) { - world.sendChunk(cx + OX, cz + OZ, 0); + world.refreshChunk(cx + OX, cz + OZ); } } } @@ -890,20 +904,19 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr return BiomeTypes.get(biomes.getByte(index)); } -// @Override - public int getCombinedId4Data(int x, int y, int z) throws FaweChunkLoadException { + public int getOrdinal(int x, int y, int z) throws FaweChunkLoadException { int index = z * getWidth() + x; if (y < 0) return 0; if (index < 0 || index >= getArea() || x < 0 || x >= getWidth()) return 0; int height = heights.getByte(index) & 0xFF; if (y > height) { if (y == height + 1) { - return overlay != null ? overlay.getInt(index) : 0; + return overlay != null ? overlay.getChar(index) : 0; } if (blocks != null) { short chunkX = (short) (x >> 4); short chunkZ = (short) (z >> 4); - int[][][] map = getChunkArray(chunkX, chunkZ); + char[][][] map = getChunkArray(chunkX, chunkZ); if (map != null) { int combined = get(map, x, y, z); if (combined != 0) { @@ -912,16 +925,16 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } } if (y <= primitives.waterHeight) { - return primitives.waterId << 4; + return primitives.waterOrdinal; } return 0; } else if (y == height) { - return floor.getInt(index); + return overlay.getChar(index); } else { if (blocks != null) { short chunkX = (short) (x >> 4); short chunkZ = (short) (z >> 4); - int[][][] map = getChunkArray(chunkX, chunkZ); + char[][][] map = getChunkArray(chunkX, chunkZ); if (map != null) { int combined = get(map, x, y, z); if (combined != 0) { @@ -929,13 +942,13 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } } } - return main.getInt(index); + return overlay.getChar(index); } } @Override public boolean setBlock(int x, int y, int z, BlockStateHolder block) throws WorldEditException { - return this.setBlock(x, y, z, block.getInternalId()); + return this.setBlock(x, y, z, block.getOrdinalChar()); } @Override @@ -950,7 +963,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public BlockState getFloor(int x, int z) { int index = z * getWidth() + x; - return BlockState.getFromInternalId(floor.getInt(index)); + return BlockState.getFromOrdinal(overlay.getChar(index)); } public int getHeight(int x, int z) { @@ -964,19 +977,19 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setFloor(int x, int z, BlockStateHolder block) { int index = z * getWidth() + x; - floor.setInt(index, block.getInternalId()); + floor.setInt(index, block.getOrdinalChar()); } @Override public BlockState getBlock(int x, int y, int z) { - return BlockState.getFromInternalId(getCombinedId4Data(x, y, z)); + return BlockState.getFromOrdinal(getOrdinal(x, y, z)); } @Override public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) { int index = z * getWidth() + x; if (index < 0 || index >= getArea()) index = Math.floorMod(index, getArea()); - return ((heights.getByte(index) & 0xFF) << 3) + (floor.getInt(index) & 0xFF) + 1; + return ((heights.getByte(index) & 0xFF) << 3) + (overlay.getChar(index) & 0xFF) + 1; } @Override @@ -1017,9 +1030,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr @Override public BufferedImage draw() { - // TODO NOT IMPLEMENTED -// return new HeightMapMCADrawer(this).draw(); - return null; + return new CFIDrawer(this).draw(); } public void setBiomePriority(int value) { @@ -1044,12 +1055,12 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int maxIndex = getArea() - 1; biomes.record(() -> floor.record(() -> main.record(() -> { - int[] mainArr = main.get(); - int[] floorArr = floor.get(); + char[] mainArr = main.get(); + char[] floorArr = floor.get(); byte[] biomesArr = biomes.get(); int index = 0; - int[] buffer = new int[2]; + char[] buffer = new char[2]; for (int z = 0; z < img.getHeight(); z++) { mutable.mutZ(z); for (int x = 0; x < img.getWidth(); x++, index++) { @@ -1065,7 +1076,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } int color = img.getRGB(x, z); if (textureUtil.getIsBlockCloserThanBiome(buffer, color, primitives.biomePriority)) { - int combined = buffer[0]; + char combined = buffer[0]; mainArr[index] = combined; floorArr[index] = combined; } @@ -1079,23 +1090,20 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); TextureUtil textureUtil = getTextureUtil(); - int widthIndex = img.getWidth() - 1; int heightIndex = img.getHeight() - 1; - int maxIndex = getArea() - 1; biomes.record(() -> floor.record(() -> main.record(() -> { - int[] mainArr = main.get(); - int[] floorArr = floor.get(); + char[] mainArr = main.get(); + char[] floorArr = floor.get(); byte[] biomesArr = biomes.get(); - int[] buffer = new int[2]; + char[] buffer = new char[2]; int index = 0; for (int y = 0; y < img.getHeight(); y++) { - boolean yBiome = y > 0 && y < heightIndex; for (int x = 0; x < img.getWidth(); x++, index++) { int color = img.getRGB(x, y); if (textureUtil.getIsBlockCloserThanBiome(buffer, color, primitives.biomePriority)) { - int combined = buffer[0]; + char combined = buffer[0]; mainArr[index] = combined; floorArr[index] = combined; } @@ -1135,8 +1143,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr TextureUtil textureUtil = getTextureUtil(); floor.record(() -> main.record(() -> { - int[] mainArr = main.get(); - int[] floorArr = floor.get(); + char[] mainArr = main.get(); + char[] floorArr = floor.get(); int index = 0; for (int z = 0; z < getLength(); z++) { @@ -1147,7 +1155,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int color = img.getRGB(x, z); BlockType block = textureUtil.getNearestBlock(color); if (block != null) { - int combined = block.getInternalId(); + char combined = block.getDefaultState().getOrdinalChar(); mainArr[index] = combined; floorArr[index] = combined; } @@ -1165,8 +1173,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr floor.record(() -> main.record(() -> { - int[] mainArr = main.get(); - int[] floorArr = floor.get(); + char[] mainArr = main.get(); + char[] floorArr = floor.get(); int index = 0; for (int z = 0; z < getLength(); z++) { @@ -1178,7 +1186,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int color = img.getRGB(x, z); BlockType block = textureUtil.getNearestBlock(color); if (block != null) { - int combined = block.getInternalId(); + char combined = block.getDefaultState().getOrdinalChar(); mainArr[index] = combined; floorArr[index] = combined; } else { @@ -1197,8 +1205,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr TextureUtil textureUtil = getTextureUtil(); floor.record(() -> main.record(() -> { - int[] mainArr = main.get(); - int[] floorArr = floor.get(); + char[] mainArr = main.get(); + char[] floorArr = floor.get(); int index = 0; for (int z = 0; z < img.getHeight(); z++) { @@ -1206,7 +1214,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int color = img.getRGB(x, z); BlockType block = textureUtil.getNearestBlock(color); if (block != null) { - int combined = block.getInternalId(); + char combined = block.getDefaultState().getOrdinalChar(); mainArr[index] = combined; floorArr[index] = combined; } else { @@ -1224,8 +1232,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr TextureUtil textureUtil = getTextureUtil(); floor.record(() -> main.record(() -> { - int[] mainArr = main.get(); - int[] floorArr = floor.get(); + char[] mainArr = main.get(); + char[] floorArr = floor.get(); int index = 0; for (int y = 0; y < img.getHeight(); y++) { @@ -1233,8 +1241,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int color = img.getRGB(x, y); BlockType[] layer = textureUtil.getNearestLayer(color); if (layer != null) { - floorArr[index] = layer[0].getInternalId(); - mainArr[index] = layer[1].getInternalId(); + floorArr[index] = layer[0].getDefaultState().getOrdinalChar(); + mainArr[index] = layer[1].getDefaultState().getOrdinalChar(); } index++; } @@ -1260,18 +1268,18 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setOverlay(BufferedImage img, Pattern pattern, boolean white) { if (pattern instanceof BlockStateHolder) { - setOverlay(img, ((BlockStateHolder) pattern).getInternalId(), white); + setOverlay(img, ((BlockStateHolder) pattern).getOrdinalChar(), white); } else if (pattern instanceof BlockType) { - setOverlay(img, ((BlockType) pattern).getInternalId(), white); + setOverlay(img, ((BlockType) pattern).getDefaultState().getOrdinalChar(), white); } else { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); if (overlay == null) { - overlay = new DifferentialArray<>(new int[getArea()]); + overlay = new DifferentialArray<>(new char[getArea()]); } overlay.record(() -> { - int[] overlayArr = overlay.get(); + char[] overlayArr = overlay.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1281,7 +1289,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr .nextInt(256) <= height) { mutable.mutX(x); mutable.mutY(height); - overlayArr[index] = pattern.apply(mutable).getInternalId(); + overlayArr[index] = pattern.apply(mutable).getOrdinalChar(); } } } @@ -1292,14 +1300,14 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setMain(BufferedImage img, Pattern pattern, boolean white) { if (pattern instanceof BlockStateHolder) { - setMain(img, ((BlockStateHolder) pattern).getInternalId(), white); + setMain(img, ((BlockStateHolder) pattern).getOrdinalChar(), white); } else { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); primitives.modifiedMain = true; main.record(() -> { - int[] mainArr = main.get(); + char[] mainArr = main.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1309,7 +1317,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr .nextInt(256) <= height) { mutable.mutX(x); mutable.mutY(height); - mainArr[index] = pattern.apply(mutable).getInternalId(); + mainArr[index] = pattern.apply(mutable).getOrdinalChar(); } } } @@ -1319,13 +1327,13 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setFloor(BufferedImage img, Pattern pattern, boolean white) { if (pattern instanceof BlockStateHolder) { - setFloor(img, ((BlockStateHolder) pattern).getInternalId(), white); + setFloor(img, ((BlockStateHolder) pattern).getOrdinalChar(), white); } else { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); floor.record(() -> { - int[] floorArr = floor.get(); + char[] floorArr = floor.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1335,7 +1343,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr .nextInt(256) <= height) { mutable.mutX(x); mutable.mutY(height); - floorArr[index] = pattern.apply(mutable).getInternalId(); + floorArr[index] = pattern.apply(mutable).getOrdinalChar(); } } } @@ -1345,15 +1353,15 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setColumn(BufferedImage img, Pattern pattern, boolean white) { if (pattern instanceof BlockStateHolder) { - setColumn(img, ((BlockStateHolder) pattern).getInternalId(), white); + setColumn(img, ((BlockStateHolder) pattern).getOrdinalChar(), white); } else { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); primitives.modifiedMain = true; main.record(() -> floor.record(() -> { - int[] floorArr = floor.get(); - int[] mainArr = main.get(); + char[] floorArr = floor.get(); + char[] mainArr = main.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1363,7 +1371,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr .nextInt(256) <= height) { mutable.mutX(x); mutable.mutY(height); - int combined = pattern.apply(mutable).getInternalId(); + char combined = pattern.apply(mutable).getOrdinalChar(); mainArr[index] = combined; floorArr[index] = combined; } @@ -1375,10 +1383,10 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setOverlay(Mask mask, Pattern pattern) { if (pattern instanceof BlockStateHolder) { - setOverlay(mask, ((BlockStateHolder) pattern).getInternalId()); + setOverlay(mask, ((BlockStateHolder) pattern).getOrdinalChar()); } else { int index = 0; - if (overlay == null) overlay = new DifferentialArray<>(new int[getArea()]); + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); for (int x = 0; x < getWidth(); x++, index++) { @@ -1386,7 +1394,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - overlay.setInt(index, pattern.apply(mutable).getInternalId()); + overlay.setInt(index, pattern.apply(mutable).getOrdinalChar()); } } } @@ -1395,7 +1403,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setFloor(Mask mask, Pattern pattern) { if (pattern instanceof BlockStateHolder) { - setFloor(mask, ((BlockStateHolder) pattern).getInternalId()); + setFloor(mask, ((BlockStateHolder) pattern).getOrdinalChar()); } else { int index = 0; for (int z = 0; z < getLength(); z++) { @@ -1405,7 +1413,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - floor.setInt(index, pattern.apply(mutable).getInternalId()); + floor.setInt(index, pattern.apply(mutable).getOrdinalChar()); } } } @@ -1414,7 +1422,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setMain(Mask mask, Pattern pattern) { if (pattern instanceof BlockStateHolder) { - setMain(mask, ((BlockStateHolder) pattern).getInternalId()); + setMain(mask, ((BlockStateHolder) pattern).getOrdinalChar()); } else { primitives.modifiedMain = true; int index = 0; @@ -1425,7 +1433,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - main.setInt(index, pattern.apply(mutable).getInternalId()); + main.setInt(index, pattern.apply(mutable).getOrdinalChar()); } } } @@ -1434,7 +1442,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setColumn(Mask mask, Pattern pattern) { if (pattern instanceof BlockStateHolder) { - setColumn(mask, ((BlockStateHolder) pattern).getInternalId()); + setColumn(mask, ((BlockStateHolder) pattern).getOrdinalChar()); } else { primitives.modifiedMain = true; int index = 0; @@ -1445,7 +1453,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - int combined = pattern.apply(mutable).getInternalId(); + int combined = pattern.apply(mutable).getOrdinalChar(); floor.setInt(index, combined); main.setInt(index, combined); } @@ -1460,10 +1468,10 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setFloor(Pattern value) { if (value instanceof BlockStateHolder) { - setFloor(((BlockStateHolder) value).getInternalId()); + setFloor(((BlockStateHolder) value).getOrdinalChar()); } else { floor.record(() -> { - int[] floorArr = floor.get(); + char[] floorArr = floor.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1471,7 +1479,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); - floorArr[index] = value.apply(mutable).getInternalId(); + floorArr[index] = value.apply(mutable).getOrdinalChar(); } } }); @@ -1480,11 +1488,11 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setColumn(Pattern value) { if (value instanceof BlockStateHolder) { - setColumn(((BlockStateHolder) value).getInternalId()); + setColumn(((BlockStateHolder) value).getOrdinalChar()); } else { main.record(() -> floor.record(() -> { - int[] floorArr = floor.get(); - int[] mainArr = main.get(); + char[] floorArr = floor.get(); + char[] mainArr = main.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1492,7 +1500,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); - int combined = value.apply(mutable).getInternalId(); + char combined = value.apply(mutable).getOrdinalChar(); mainArr[index] = combined; floorArr[index] = combined; } @@ -1503,10 +1511,10 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setMain(Pattern value) { if (value instanceof BlockStateHolder) { - setMain(((BlockStateHolder) value).getInternalId()); + setMain(((BlockStateHolder) value).getOrdinalChar()); } else { main.record(() -> { - int[] mainArr = main.get(); + char[] mainArr = main.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1514,7 +1522,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); - mainArr[index] = value.apply(mutable).getInternalId(); + mainArr[index] = value.apply(mutable).getOrdinalChar(); } } }); @@ -1522,12 +1530,12 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } public void setOverlay(Pattern value) { - if (overlay == null) overlay = new DifferentialArray<>(new int[getArea()]); + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); if (value instanceof BlockStateHolder) { - setOverlay(((BlockStateHolder) value).getInternalId()); + setOverlay(((BlockStateHolder) value).getOrdinalChar()); } else { overlay.record(() -> { - int[] overlayArr = overlay.get(); + char[] overlayArr = overlay.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1535,7 +1543,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); - overlayArr[index] = value.apply(mutable).getInternalId(); + overlayArr[index] = value.apply(mutable).getOrdinalChar(); } } }); @@ -1564,19 +1572,19 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } @Override - public WritableMCAChunk write(WritableMCAChunk chunk, int csx, int cex, int csz, int cez) { + public MCAChunk write(MCAChunk chunk, int csx, int cex, int csz, int cez) { byte[] heights = this.heights.get(); byte[] biomes = this.biomes.get(); - int[] main = this.main.get(); - int[] floor = this.floor.get(); - int[] overlay = this.overlay != null ? this.overlay.get() : null; + char[] main = this.main.get(); + char[] floor = this.floor.get(); + char[] overlay = this.overlay != null ? this.overlay.get() : null; try { int[] indexes = FaweCache.IMP.INDEX_STORE.get(); int index; int maxY = 0; int minY = Integer.MAX_VALUE; - int[] heightMap = chunk.biomes; + byte[] heightMap = chunk.biomes; int globalIndex; for (int z = csz; z <= cez; z++) { globalIndex = z * getWidth() + csx; @@ -1584,7 +1592,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int x = csx; x <= cex; x++, index++, globalIndex++) { indexes[index] = globalIndex; int height = heights[globalIndex] & 0xFF; - heightMap[index] = height; + heightMap[index] = (byte) height; maxY = Math.max(maxY, height); minY = Math.min(minY, height); } @@ -1599,7 +1607,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } if (primitives.waterHeight != 0) { int maxIndex = primitives.waterHeight << 8; - Arrays.fill(chunk.blocks, 0, maxIndex, primitives.waterId); + Arrays.fill(chunk.blocks, 0, maxIndex, primitives.waterOrdinal); } if (primitives.modifiedMain) { // If the main block is modified, we can't short circuit this @@ -1607,7 +1615,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr index = (z & 15) << 4; for (int x = csx; x <= cex; x++, index++) { globalIndex = indexes[index]; - int mainCombined = main[globalIndex]; + char mainCombined = main[globalIndex]; for (int y = 0; y < minY; y++) { chunk.blocks[index + (y << 8)] = mainCombined; } @@ -1615,7 +1623,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } } else { int maxIndex = minY << 8; - Arrays.fill(chunk.blocks, 0, maxIndex, BlockID.STONE); + Arrays.fill(chunk.blocks, 0, maxIndex, (char) BlockID.STONE); } final boolean hasFloorThickness = primitives.floorThickness != 0 || primitives.worldThickness != 0; @@ -1630,13 +1638,13 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr index = (z & 15) << 4; for (int x = csx; x <= cex; x++, index++) { globalIndex = indexes[index]; - int height = heightMap[index]; + int height = heightMap[index] & 0xFF; int maxMainY = height; int minMainY = minY; - int mainCombined = main[globalIndex]; + char mainCombined = main[globalIndex]; - int floorCombined = floor[globalIndex]; + char floorCombined = floor[globalIndex]; if (hasFloorThickness) { if (x > 0) maxMainY = Math.min(heights[globalIndex - 1] & 0xFF, maxMainY); if (x < getWidth() - 1) maxMainY = Math.min(heights[globalIndex + 1] & 0xFF, maxMainY); @@ -1671,33 +1679,33 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } if (hasOverlay) { - int overlayCombined = overlay[globalIndex]; + char overlayCombined = overlay[globalIndex]; int overlayIndex = index + (height + 1 << 8); chunk.blocks[overlayIndex] = overlayCombined; } - if (primitives.bedrockId != 0) { - chunk.blocks[index] = primitives.bedrockId; + if (primitives.bedrockOrdinal != 0) { + chunk.blocks[index] = primitives.bedrockOrdinal; } } } - int[][][] localBlocks = getChunkArray(chunk.getX(), chunk.getZ()); + char[][][] localBlocks = getChunkArray(chunk.getX(), chunk.getZ()); if (localBlocks != null) { index = 0; for (int layer = 0; layer < 16; layer++) { int by = layer << 4; int ty = by + 15; for (int y = by; y <= ty; y++, index += 256) { - int[][] yBlocks = localBlocks[y]; + char[][] yBlocks = localBlocks[y]; if (yBlocks != null) { chunk.hasSections[layer] = true; for (int z = 0; z < yBlocks.length; z++) { - int[] zBlocks = yBlocks[z]; + char[] zBlocks = yBlocks[z]; if (zBlocks != null) { int zIndex = index + (z << 4); for (int x = 0; x < zBlocks.length; x++, zIndex++) { - int combined = zBlocks[x]; + char combined = zBlocks[x]; if (combined == 0) continue; chunk.blocks[zIndex] = combined; } @@ -1719,24 +1727,24 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr return chunk; } - private void setUnsafe(int[][][] map, int combined, int x, int y, int z) { - int[][] yMap = map[y]; + private void setUnsafe(char[][][] map, char combined, int x, int y, int z) { + char[][] yMap = map[y]; if (yMap == null) { - map[y] = yMap = new int[16][]; + map[y] = yMap = new char[16][]; } - int[] zMap = yMap[z]; + char[] zMap = yMap[z]; if (zMap == null) { - yMap[z] = zMap = new int[16]; + yMap[z] = zMap = new char[16]; } zMap[x] = combined; } - private int get(int[][][] map, int x, int y, int z) { - int[][] yMap = map[y]; + private int get(char[][][] map, int x, int y, int z) { + char[][] yMap = map[y]; if (yMap == null) { return 0; } - int[] zMap = yMap[z & 15]; + char[] zMap = yMap[z & 15]; if (zMap == null) { return 0; } @@ -1745,7 +1753,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr private void setOverlay(Mask mask, int combined) { int index = 0; - if (overlay == null) overlay = new DifferentialArray<>(new int[getArea()]); + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); for (int x = 0; x < getWidth(); x++, index++) { @@ -1807,29 +1815,29 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } } - private void setFloor(int value) { + private void setFloor(char value) { floor.record(() -> Arrays.fill(floor.get(), value)); } - private void setColumn(int value) { + private void setColumn(char value) { setFloor(value); setMain(value); } - private void setMain(int value) { + private void setMain(char value) { primitives.modifiedMain = true; main.record(() -> Arrays.fill(main.get(), value)); } - private void setOverlay(int value) { - if (overlay == null) overlay = new DifferentialArray<>(new int[getArea()]); + private void setOverlay(char value) { + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); overlay.record(() -> Arrays.fill(overlay.get(), value)); } - private void setOverlay(BufferedImage img, int combined, boolean white) { + private void setOverlay(BufferedImage img, char combined, boolean white) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); - if (overlay == null) overlay = new DifferentialArray<>(new int[getArea()]); + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); overlay.record(() -> { int index = 0; @@ -1845,7 +1853,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr }); } - private void setMain(BufferedImage img, int combined, boolean white) { + private void setMain(BufferedImage img, char combined, boolean white) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); primitives.modifiedMain = true; @@ -1864,7 +1872,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr }); } - private void setFloor(BufferedImage img, int combined, boolean white) { + private void setFloor(BufferedImage img, char combined, boolean white) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); @@ -1882,7 +1890,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr }); } - private void setColumn(BufferedImage img, int combined, boolean white) { + private void setColumn(BufferedImage img, char combined, boolean white) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); primitives.modifiedMain = true; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/MCAWriter.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/MCAWriter.java index ef09226ff..63002f492 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/MCAWriter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/MCAWriter.java @@ -1,5 +1,6 @@ package com.boydti.fawe.object.brush.visualization.cfi; +import com.boydti.fawe.jnbt.anvil.MCAChunk; import com.boydti.fawe.object.collection.CleanableThreadLocal; import com.boydti.fawe.object.io.BufferedRandomAccessFile; import com.boydti.fawe.util.MainUtil; @@ -71,7 +72,7 @@ public abstract class MCAWriter implements Extent { public abstract boolean shouldWrite(int chunkX, int chunkZ); - public abstract WritableMCAChunk write(WritableMCAChunk input, int startX, int endX, int startZ, int endZ); + public abstract MCAChunk write(MCAChunk input, int startX, int endX, int startZ, int endZ); public void generate() throws IOException { if (!folder.exists()) { @@ -80,9 +81,12 @@ public abstract class MCAWriter implements Extent { final ForkJoinPool pool = new ForkJoinPool(); int tcx = (width - 1) >> 4; int tcz = (length - 1) >> 4; - final ThreadLocal chunkStore = ThreadLocal.withInitial(() -> { - WritableMCAChunk chunk = new WritableMCAChunk(); - Arrays.fill(chunk.blocks, BlockID.AIR); + final ThreadLocal chunkStore = new ThreadLocal() { + @Override + protected MCAChunk initialValue() { + MCAChunk chunk = new MCAChunk(); + Arrays.fill(chunk.blocks, (char) BlockID.AIR); +// Arrays.fill(chunk.skyLight, (byte) 255); return chunk; }); final ThreadLocal byteStore1 = ThreadLocal.withInitial(() -> new byte[500000]); @@ -122,12 +126,13 @@ public abstract class MCAWriter implements Extent { if (shouldWrite(cx, cz)) { pool.submit(() -> { try { - WritableMCAChunk chunk = chunkStore.get(); - chunk.clear(fcx, fcz); + MCAChunk chunk = chunkStore.get(); + chunk.reset(); + chunk.setPosition(fcx, fcz); chunk = write(chunk, csx, cex, csz, cez); if (chunk != null) { // Generation offset - chunk.setLoc( fcx + (getOffsetX() >> 4), fcz + (getOffsetZ() >> 4)); + chunk.setPosition( fcx + (getOffsetX() >> 4), fcz + (getOffsetZ() >> 4)); // Compress byte[] bytes = chunk.toBytes(byteStore1.get()); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java index 125f43f34..d594d07d4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java @@ -17,6 +17,8 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; + import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -457,7 +459,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { public int maxZ; public DiskStorageSummary(int x, int z) { - blocks = new int[BlockTypes.states.length]; + blocks = new int[BlockTypesCache.states.length]; minX = x; maxX = x; minZ = z; @@ -482,7 +484,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { HashMap map = new HashMap<>(); for (int i = 0; i < blocks.length; i++) { if (blocks[i] != 0) { - BlockState state = BlockTypes.states[i]; + BlockState state = BlockTypesCache.states[i]; map.put(state, blocks[i]); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/AbstractDelegateFaweClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/AbstractDelegateFaweClipboard.java deleted file mode 100644 index 6d9bd9b52..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/AbstractDelegateFaweClipboard.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.boydti.fawe.object.clipboard; - -import com.boydti.fawe.jnbt.NBTStreamer; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.entity.BaseEntity; -import com.sk89q.worldedit.entity.Entity; -import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockStateHolder; -import java.util.List; - -public class AbstractDelegateFaweClipboard extends FaweClipboard { - private final FaweClipboard parent; - - public AbstractDelegateFaweClipboard(FaweClipboard parent) { - this.parent = parent; - } - - @Override - public BaseBlock getBlock(int x, int y, int z) { - return parent.getBlock(x, y, z); - } - - @Override - public > boolean setBlock(int x, int y, int z, B block) { - return parent.setBlock(x, y, z, block); - } - - @Override - public > boolean setBlock(int index, B block) { - return parent.setBlock(index, block); - } - - @Override - public boolean hasBiomes() { - return parent.hasBiomes(); - } - - @Override - public boolean setBiome(int x, int z, BiomeType biome) { - return parent.setBiome(x, z, biome); - } - - @Override - public BiomeType getBiome(int x, int z) { - return parent.getBiome(x, z); - } - - @Override - public BiomeType getBiome(int index) { - return parent.getBiome(index); - } - - @Override - public BaseBlock getBlock(int index) { - return parent.getBlock(index); - } - - @Override - public void setBiome(int index, BiomeType biome) { - parent.setBiome(index, biome); - } - - @Override - public boolean setTile(int x, int y, int z, CompoundTag tag) { - return parent.setTile(x, y, z, tag); - } - - @Override - public Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) { - return parent.createEntity(world, x, y, z, yaw, pitch, entity); - } - - @Override - public List getEntities() { - return parent.getEntities(); - } - - @Override - public boolean remove(ClipboardEntity clipboardEntity) { - return parent.remove(clipboardEntity); - } - - @Override - public void setOrigin(BlockVector3 offset) { - parent.setOrigin(offset); - } - - @Override - public void setDimensions(BlockVector3 dimensions) { - parent.setDimensions(dimensions); - } - - @Override - public void flush() { - parent.flush(); - } - - @Override - public void close() { - parent.close(); - } - - @Override - public BlockVector3 getDimensions() { - return parent.getDimensions(); - } - - @Override - public void forEach(BlockReader task, boolean air) { - parent.forEach(task, air); - } - - @Override - public void streamBiomes(NBTStreamer.ByteReader task) { - parent.streamBiomes(task); - } - - @Override - public void streamCombinedIds(NBTStreamer.ByteReader task) { - parent.streamCombinedIds(task); - } - - @Override - public List getTileEntities() { - return parent.getTileEntities(); - } -} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java index 3eaab8e9d..506f39ed5 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java @@ -1,6 +1,6 @@ package com.boydti.fawe.object.clipboard; -import com.boydti.fawe.jnbt.NBTStreamer; +import com.boydti.fawe.jnbt.streamer.IntValueReader; import com.boydti.fawe.object.IntegerTrio; import com.boydti.fawe.util.ReflectionUtils; import com.sk89q.jnbt.CompoundTag; @@ -8,41 +8,38 @@ import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; -import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.Location; 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.block.BlockTypes; + +import javax.annotation.Nullable; +import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.UUID; -public class CPUOptimizedClipboard extends FaweClipboard { - private int length; - private int height; - private int width; - private int area; - private int volume; +public class CPUOptimizedClipboard extends LinearClipboard { private BiomeType[] biomes = null; - private int[] states; + private char[] states; private final HashMap nbtMapLoc; private final HashMap nbtMapIndex; - private final HashSet entities; + private final HashSet entities; - public CPUOptimizedClipboard(int width, int height, int length) { - this.width = width; - this.height = height; - this.length = length; - this.area = width * length; - this.volume = area * height; - this.states = new int[volume]; + public CPUOptimizedClipboard(BlockVector3 dimensions) { + super(dimensions); + this.states = new char[getVolume()]; nbtMapLoc = new HashMap<>(); nbtMapIndex = new HashMap<>(); entities = new HashSet<>(); @@ -54,7 +51,7 @@ public class CPUOptimizedClipboard extends FaweClipboard { } @Override - public boolean setBiome(int x, int z, BiomeType biome) { + public boolean setBiome(int x, int y, int z, BiomeType biome) { setBiome(getIndex(x, 0, z), biome); return true; } @@ -62,19 +59,24 @@ public class CPUOptimizedClipboard extends FaweClipboard { @Override public void setBiome(int index, BiomeType biome) { if (biomes == null) { - biomes = new BiomeType[area]; + biomes = new BiomeType[getArea()]; } biomes[index] = biome; } @Override - public void streamBiomes(NBTStreamer.ByteReader task) { + public void streamBiomes(IntValueReader task) { if (!hasBiomes()) return; int index = 0; - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++, index++) { - task.run(index, biomes[index].getInternalId()); + try { + for (int z = 0; z < getLength(); z++) { + for (int x = 0; x < getWidth(); x++, index++) { + task.applyInt(index, biomes[index].getInternalId()); + } } + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); } } @@ -87,7 +89,7 @@ public class CPUOptimizedClipboard extends FaweClipboard { } @Override - public BiomeType getBiome(int x, int z) { + public BiomeType getBiomeType(int x, int z) { return getBiome(getIndex(x, 0, z)); } @@ -107,109 +109,63 @@ public class CPUOptimizedClipboard extends FaweClipboard { return nbtMapIndex.get(index); } - @Override - public void setDimensions(BlockVector3 dimensions) { - width = dimensions.getBlockX(); - height = dimensions.getBlockY(); - length = dimensions.getBlockZ(); - area = width * length; - int newVolume = area * height; - if (newVolume != volume) { - volume = newVolume; - states = new int[volume]; - } - } - - @Override - public BlockVector3 getDimensions() { - return BlockVector3.at(width, height, length); - } - private int yLast; private int yLastI; private int zLast; private int zLastI; public int getIndex(int x, int y, int z) { - return x + ((yLast == y) ? yLastI : (yLastI = (yLast = y) * area)) + ((zLast == z) ? zLastI - : (zLastI = (zLast = z) * width)); + return x + ((yLast == y) ? yLastI : (yLastI = (yLast = y) * getArea())) + ((zLast == z) ? zLastI + : (zLastI = (zLast = z) * getWidth())); } @Override - public BaseBlock getBlock(int x, int y, int z) { + public BaseBlock getFullBlock(int x, int y, int z) { int index = getIndex(x, y, z); - return getBlock(index); + return getFullBlock(index); } @Override - public BaseBlock getBlock(int index) { - int combinedId = states[index]; - BlockType type = BlockTypes.getFromStateId(combinedId); - BaseBlock base = type.withStateId(combinedId).toBaseBlock(); - if (type.getMaterial().hasContainer()) { + public BaseBlock getFullBlock(int index) { + BlockState block = getBlock(index); + if (block.getMaterial().hasContainer()) { CompoundTag nbt = getTag(index); if (nbt != null) { - return base.toBaseBlock(nbt); + return block.toBaseBlock(nbt); } } - return base; + return block.toBaseBlock(); } @Override - public void forEach(final BlockReader task, boolean air) { - if (air) { - for (int y = 0, index = 0; y < height; y++) { - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++, index++) { - BaseBlock block = getBlock(index); - task.run(x, y, z, block); - } - } - } - } else { - for (int y = 0, index = 0; y < height; y++) { - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++, index++) { - BaseBlock block = getBlock(index); - if (!block.getMaterial().isAir()) { - task.run(x, y, z, block); - } - } - } - } - } + public BlockState getBlock(int index) { + char ordinal = states[index]; + return BlockState.getFromOrdinal(ordinal); } @Override - public void streamCombinedIds(NBTStreamer.ByteReader task) { - int index = 0; - for (int y = 0; y < height; y++) { - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++) { - task.run(index, states[index++]); - } - } - } + public BlockState getBlock(int x, int y, int z) { + return getBlock(getIndex(x, y, z)); } @Override - public List getTileEntities() { + public Collection getTileEntities() { convertTilesToIndex(); for (Map.Entry entry : nbtMapIndex.entrySet()) { int index = entry.getKey(); CompoundTag tag = entry.getValue(); Map values = ReflectionUtils.getMap(tag.getValue()); if (!values.containsKey("x")) { - int y = index / area; - index -= y * area; - int z = index / width; - int x = index - (z * width); + int y = index / getArea(); + index -= y * getArea(); + int z = index / getWidth(); + int x = index - (z * getWidth()); values.put("x", new IntTag(x)); values.put("y", new IntTag(y)); values.put("z", new IntTag(z)); } } - return new ArrayList<>(nbtMapIndex.values()); + return nbtMapIndex.values(); } @Override @@ -234,7 +190,7 @@ public class CPUOptimizedClipboard extends FaweClipboard { @Override public > boolean setBlock(int index, B block) { - states[index] = block.getInternalId(); + states[index] = block.getOrdinalChar(); boolean hasNbt = block instanceof BaseBlock && block.hasNbtData(); if (hasNbt) { setTile(index, block.getNbtData()); @@ -242,9 +198,10 @@ public class CPUOptimizedClipboard extends FaweClipboard { return true; } + @Nullable @Override - public Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) { - FaweClipboard.ClipboardEntity ret = new ClipboardEntity(world, x, y, z, yaw, pitch, entity); + public Entity createEntity(Location location, BaseEntity entity) { + BlockArrayClipboard.ClipboardEntity ret = new BlockArrayClipboard.ClipboardEntity(location, entity); entities.add(ret); return ret; } @@ -255,7 +212,21 @@ public class CPUOptimizedClipboard extends FaweClipboard { } @Override - public boolean remove(ClipboardEntity clipboardEntity) { - return entities.remove(clipboardEntity); + public void removeEntity(Entity entity) { + this.entities.remove(entity); + } + + @Nullable + @Override + public void removeEntity(int x, int y, int z, UUID uuid) { + Iterator iter = this.entities.iterator(); + while (iter.hasNext()) { + BlockArrayClipboard.ClipboardEntity entity = iter.next(); + UUID entUUID = entity.getState().getNbtData().getUUID(); + if (uuid.equals(entUUID)) { + iter.remove(); + return; + } + } } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DelegateClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DelegateClipboard.java new file mode 100644 index 000000000..1cb9b41d5 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DelegateClipboard.java @@ -0,0 +1,165 @@ +package com.boydti.fawe.object.clipboard; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; +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 javax.annotation.Nullable; +import java.net.URI; +import java.util.List; +import java.util.UUID; + +public class DelegateClipboard implements Clipboard { + private final Clipboard parent; + + public DelegateClipboard(Clipboard parent) { + this.parent = parent; + } + + public Clipboard getParent() { + return parent; + } + + @Override + public URI getURI() { + return parent.getURI(); + } + + @Override + public void setOrigin(BlockVector3 offset) { + parent.setOrigin(offset); + } + + @Override + public BlockVector3 getDimensions() { + return parent.getDimensions(); + } + + @Override + public Region getRegion() { + return parent.getRegion(); + } + + @Override + public BlockVector3 getOrigin() { + return parent.getOrigin(); + } + + @Override + public boolean hasBiomes() { + return parent.hasBiomes(); + } + + @Override + public void removeEntity(Entity entity) { + parent.removeEntity(entity); + } + + @Override + public BlockVector3 getMinimumPoint() { + return parent.getMinimumPoint(); + } + + @Override + public BlockVector3 getMaximumPoint() { + return parent.getMaximumPoint(); + } + + @Override + public List getEntities(Region region) { + return parent.getEntities(region); + } + + @Override + public List getEntities() { + return parent.getEntities(); + } + + @Override + @Nullable + public Entity createEntity(Location location, BaseEntity entity) { + return parent.createEntity(location, entity); + } + + @Override + @Nullable + public void removeEntity(int x, int y, int z, UUID uuid) { + parent.removeEntity(x, y, z, uuid); + } + + @Override + public boolean isWorld() { + return parent.isWorld(); + } + + @Override + public BlockState getBlock(BlockVector3 position) { + return parent.getBlock(position); + } + + @Override + public BlockState getBlock(int x, int y, int z) { + return parent.getBlock(x, y, z); + } + + @Override + public BaseBlock getFullBlock(BlockVector3 position) { + return parent.getFullBlock(position); + } + + @Override + public BaseBlock getFullBlock(int x, int y, int z) { + return parent.getFullBlock(x, y, z); + } + + @Override + public BiomeType getBiome(BlockVector2 position) { + return parent.getBiome(position); + } + + @Override + public BiomeType getBiomeType(int x, int z) { + return parent.getBiomeType(x, z); + } + + @Override + @Deprecated + public > boolean setBlock(BlockVector3 position, T block) throws WorldEditException { + return parent.setBlock(position, block); + } + + @Override + public > boolean setBlock(int x, int y, int z, T block) throws WorldEditException { + return parent.setBlock(x, y, z, block); + } + + @Override + public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { + return parent.setTile(x, y, z, tile); + } + + @Override + public boolean setBiome(BlockVector2 position, BiomeType biome) { + return parent.setBiome(position, biome); + } + + @Override + public boolean setBiome(int x, int y, int z, BiomeType biome) { + return parent.setBiome(x, y, z, biome); + } + + @Override + public void close() { + parent.close(); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java index 7f581f4e1..0f40e10b7 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java @@ -2,7 +2,7 @@ package com.boydti.fawe.object.clipboard; import com.boydti.fawe.Fawe; import com.boydti.fawe.config.Settings; -import com.boydti.fawe.jnbt.NBTStreamer; +import com.boydti.fawe.jnbt.streamer.IntValueReader; import com.boydti.fawe.object.IntegerTrio; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.ReflectionUtils; @@ -15,6 +15,8 @@ import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BaseBlock; @@ -22,20 +24,29 @@ 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.block.BlockTypes; + +import javax.annotation.Nullable; import java.io.Closeable; +import java.io.DataInputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.net.URI; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; /** @@ -43,18 +54,12 @@ import java.util.UUID; * - Uses an auto closable RandomAccessFile for getting / setting id / data * - I don't know how to reduce nbt / entities to O(2) complexity, so it is stored in memory. */ -public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { +public class DiskOptimizedClipboard extends LinearClipboard implements Closeable { private static int HEADER_SIZE = 14; - protected int length; - protected int height; - protected int width; - protected int area; - protected int volume; - private final HashMap nbtMap; - private final HashSet entities; + private final HashSet entities; private final File file; private RandomAccessFile braf; @@ -63,11 +68,65 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { private FileChannel fileChannel; private boolean hasBiomes; - public DiskOptimizedClipboard(int width, int height, int length, UUID uuid) { - this(width, height, length, MainUtil.getFile(Fawe.get() != null ? Fawe.imp().getDirectory() : new File("."), Settings.IMP.PATHS.CLIPBOARD + File.separator + uuid + ".bd")); + public DiskOptimizedClipboard(BlockVector3 dimensions, UUID uuid) { + this(dimensions, MainUtil.getFile(Fawe.get() != null ? Fawe.imp().getDirectory() : new File("."), Settings.IMP.PATHS.CLIPBOARD + File.separator + uuid + ".bd")); + } + + public DiskOptimizedClipboard(BlockVector3 dimensions) { + this(dimensions, MainUtil.getFile(Fawe.imp() != null ? Fawe.imp().getDirectory() : new File("."), Settings.IMP.PATHS.CLIPBOARD + File.separator + UUID.randomUUID() + ".bd")); + } + + public DiskOptimizedClipboard(BlockVector3 dimensions, File file) { + super(dimensions); + if (getWidth() > Character.MAX_VALUE || getHeight() > Character.MAX_VALUE || getLength() > Character.MAX_VALUE) { + throw new IllegalArgumentException("Too large"); + } + try { + nbtMap = new HashMap<>(); + entities = new HashSet<>(); + this.file = file; + try { + if (!file.exists()) { + File parent = file.getParentFile(); + if (parent != null) { + file.getParentFile().mkdirs(); + } + file.createNewFile(); + } + } catch (Exception e) { + e.printStackTrace(); + } + this.braf = new RandomAccessFile(file, "rw"); + long volume = (long) getArea() * 2L + (long) HEADER_SIZE; + braf.setLength(0); + braf.setLength(volume); + init(); + // write getLength() etc + byteBuffer.putChar(2, (char) getWidth()); + byteBuffer.putChar(4, (char) getHeight()); + byteBuffer.putChar(6, (char) getLength()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public URI getURI() { + return file.toURI(); + } + + private static BlockVector3 readSize(File file) { + try (DataInputStream is = new DataInputStream(new FileInputStream(file))) { + is.skipBytes(2); + return BlockVector3.at(is.readChar(), is.readChar(), is.readChar()); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } } public DiskOptimizedClipboard(File file) { + super(readSize(file)); try { nbtMap = new HashMap<>(); entities = new HashSet<>(); @@ -75,16 +134,9 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { this.braf = new RandomAccessFile(file, "rw"); braf.setLength(file.length()); init(); - width = byteBuffer.getChar(2); - height = byteBuffer.getChar(4); - length = byteBuffer.getChar(6); - area = width * length; - this.volume = length * width * height; - - if (braf.length() - HEADER_SIZE == (volume << 2) + area) { + if (braf.length() - HEADER_SIZE == (getVolume() << 1) + getArea()) { hasBiomes = true; } - autoCloseTask(); } catch (IOException e) { throw new RuntimeException(e); } @@ -107,7 +159,7 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { hasBiomes = true; close(); this.braf = new RandomAccessFile(file, "rw"); - this.braf.setLength(HEADER_SIZE + (volume << 2) + area); + this.braf.setLength(HEADER_SIZE + (getVolume() << 1) + getArea()); init(); } catch (IOException e) { e.printStackTrace(); @@ -123,7 +175,7 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { } @Override - public boolean setBiome(int x, int z, BiomeType biome) { + public boolean setBiome(int x, int y, int z, BiomeType biome) { setBiome(getIndex(x, 0, z), biome); return true; } @@ -131,7 +183,7 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { @Override public void setBiome(int index, BiomeType biome) { if (initBiome()) { - byteBuffer.put(HEADER_SIZE + (volume << 2) + index, (byte) biome.getInternalId()); + byteBuffer.put(HEADER_SIZE + (getVolume() << 1) + index, (byte) biome.getInternalId()); } } @@ -140,36 +192,36 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { if (!hasBiomes()) { return null; } - int biomeId = byteBuffer.get(HEADER_SIZE + (volume << 2) + index) & 0xFF; + int biomeId = byteBuffer.get(HEADER_SIZE + (getVolume() << 1) + index) & 0xFF; return BiomeTypes.get(biomeId); } @Override - public void streamBiomes(NBTStreamer.ByteReader task) { + public void streamBiomes(IntValueReader task) { if (!hasBiomes()) return; int index = 0; - int mbbIndex = HEADER_SIZE + (volume << 2); - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++, index++, mbbIndex++) { - int biome = byteBuffer.get(mbbIndex) & 0xFF; - task.run(index, biome); + int mbbIndex = HEADER_SIZE + (getVolume() << 1); + try { + for (int z = 0; z < getLength(); z++) { + for (int x = 0; x < getWidth(); x++, index++, mbbIndex++) { + int biome = byteBuffer.get(mbbIndex) & 0xFF; + task.applyInt(index, biome); + } } + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); } } @Override - public BiomeType getBiome(int x, int z) { + public BiomeType getBiomeType(int x, int z) { return getBiome(getIndex(x, 0, z)); } - @Override - public BlockVector3 getDimensions() { - return BlockVector3.at(width, height, length); - } - public BlockArrayClipboard toClipboard() { try { - CuboidRegion region = new CuboidRegion(BlockVector3.at(0, 0, 0), BlockVector3.at(width - 1, height - 1, length - 1)); + CuboidRegion region = new CuboidRegion(BlockVector3.at(0, 0, 0), BlockVector3.at(getWidth() - 1, getHeight() - 1, getLength() - 1)); int ox = byteBuffer.getShort(8); int oy = byteBuffer.getShort(10); int oz = byteBuffer.getShort(12); @@ -182,45 +234,9 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { return null; } - public DiskOptimizedClipboard(int width, int height, int length, File file) { - try { - nbtMap = new HashMap<>(); - entities = new HashSet<>(); - this.file = file; - this.width = width; - this.height = height; - this.length = length; - this.area = width * length; - this.volume = width * length * height; - try { - if (!file.exists()) { - File parent = file.getParentFile(); - if (parent != null) { - file.getParentFile().mkdirs(); - } - file.createNewFile(); - } - } catch (Exception e) { - e.printStackTrace(); - } - this.braf = new RandomAccessFile(file, "rw"); - long volume = (long) width * (long) height * (long) length * 4L + (long) HEADER_SIZE; - braf.setLength(0); - braf.setLength(volume); - if (width * height * length != 0) { - init(); - // write length etc - byteBuffer.putChar(2, (char) width); - byteBuffer.putChar(4, (char) height); - byteBuffer.putChar(6, (char) length); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - @Override public void setOrigin(BlockVector3 offset) { + super.setOrigin(offset); try { byteBuffer.putShort(8, (short) offset.getBlockX()); byteBuffer.putShort(10, (short) offset.getBlockY()); @@ -230,38 +246,11 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { } } - @Override - public void setDimensions(BlockVector3 dimensions) { - try { - width = dimensions.getBlockX(); - height = dimensions.getBlockY(); - length = dimensions.getBlockZ(); - area = width * length; - volume = width * length * height; - long size = width * height * length * 4L + HEADER_SIZE + (hasBiomes() ? area : 0); - if (braf.length() < size) { - close(); - this.braf = new RandomAccessFile(file, "rw"); - braf.setLength(size); - init(); - } - byteBuffer.putChar(2, (char) width); - byteBuffer.putChar(4, (char) height); - byteBuffer.putChar(6, (char) length); - } catch (IOException e) { - e.printStackTrace(); - } - } - @Override public void flush() { byteBuffer.force(); } - public DiskOptimizedClipboard(int width, int height, int length) { - this(width, height, length, MainUtil.getFile(Fawe.imp() != null ? Fawe.imp().getDirectory() : new File("."), Settings.IMP.PATHS.CLIPBOARD + File.separator + UUID.randomUUID() + ".bd")); - } - private void closeDirectBuffer(ByteBuffer cb) { if (cb == null || !cb.isDirect()) return; // we could use this type cast and call functions without reflection code, @@ -287,11 +276,6 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { } } - @Override - protected void finalize() throws Throwable { - close(); - } - @Override public void close() { try { @@ -311,174 +295,80 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { } } - private void autoCloseTask() { -// TaskManager.IMP.laterAsync(new Runnable() { -// @Override -// public void run() { -// if (raf != null && System.currentTimeMillis() - lastAccessed > 10000) { -// close(); -// } else if (raf == null) { -// return; -// } else { -// TaskManager.IMP.laterAsync(this, 200); -// } -// } -// }, 200); - } - private int ylast; private int ylasti; private int zlast; private int zlasti; @Override - public void streamCombinedIds(NBTStreamer.ByteReader task) { - try { - byteBuffer.force(); - int pos = HEADER_SIZE; - int index = 0; - for (int y = 0; y < height; y++) { - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++, pos += 4) { - int combinedId = byteBuffer.getInt(pos); - task.run(index++, combinedId); - } - } - } - } catch (Throwable e) { - e.printStackTrace(); - } - } - - @Override - public List getTileEntities() { - return new ArrayList<>(nbtMap.values()); - } - - @Override - public void forEach(BlockReader task, boolean air) { - byteBuffer.force(); - int pos = HEADER_SIZE; - IntegerTrio trio = new IntegerTrio(); - final boolean hasTile = !nbtMap.isEmpty(); - if (air) { - if (hasTile) { - for (int y = 0; y < height; y++) { - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++, pos += 4) { - int combinedId = byteBuffer.getInt(pos); - BlockType type = BlockTypes.getFromStateId(combinedId); - BlockState state = type.withStateId(combinedId); - if (type.getMaterial().hasContainer()) { - trio.set(x, y, z); - CompoundTag nbt = nbtMap.get(trio); - if (nbt != null) { - task.run(x, y, z, state.toBaseBlock(nbt)); - continue; - } - } - task.run(x, y, z, state); - } - } - } - } else { - for (int y = 0; y < height; y++) { - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++, pos += 4) { - int combinedId = byteBuffer.getInt(pos); - BlockState state = BlockState.getFromInternalId(combinedId); - task.run(x, y, z, state); - } - } - } - } - } else { - for (int y = 0; y < height; y++) { - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++, pos += 4) { - int combinedId = byteBuffer.getInt(pos); - BlockType type = BlockTypes.getFromStateId(combinedId); - if (!type.getMaterial().isAir()) { - BlockState state = type.withStateId(combinedId); - if (type.getMaterial().hasContainer()) { - trio.set(x, y, z); - CompoundTag nbt = nbtMap.get(trio); - if (nbt != null) { - task.run(x, y, z, state.toBaseBlock(nbt)); - continue; - } - } - task.run(x, y, z, state); - } - } - } - } - } + public Collection getTileEntities() { + return nbtMap.values(); } public int getIndex(int x, int y, int z) { - return x + (ylast == y ? ylasti : (ylasti = (ylast = y) * area)) + (zlast == z - ? zlasti : (zlasti = (zlast = z) * width)); + return x + (ylast == y ? ylasti : (ylasti = (ylast = y) * getArea())) + (zlast == z + ? zlasti : (zlasti = (zlast = z) * getWidth())); } @Override - public BaseBlock getBlock(int x, int y, int z) { - try { - int index = HEADER_SIZE + (getIndex(x, y, z) << 2); - int combinedId = byteBuffer.getInt(index); - BlockType type = BlockTypes.getFromStateId(combinedId); - BaseBlock base = type.withStateId(combinedId).toBaseBlock(); - if (type.getMaterial().hasContainer() && !nbtMap.isEmpty()) { - CompoundTag nbt = nbtMap.get(new IntegerTrio(x, y, z)); - if (nbt != null) { - return base.toBaseBlock(nbt); - } - } - return base; - } catch (IndexOutOfBoundsException ignore) { - } catch (Exception e) { - e.printStackTrace(); - } - return BlockTypes.AIR.getDefaultState().toBaseBlock(); + public BaseBlock getFullBlock(int x, int y, int z) { + return toBaseBlock(getBlock(x, y, z), x, y, z); } - @Override - public BaseBlock getBlock(int i) { - try { - int diskIndex = HEADER_SIZE + (i << 2); - int combinedId = byteBuffer.getInt(diskIndex); - BlockType type = BlockTypes.getFromStateId(combinedId); - BaseBlock base = type.withStateId(combinedId).toBaseBlock(); - if (type.getMaterial().hasContainer() && !nbtMap.isEmpty()) { - CompoundTag nbt; - if (nbtMap.size() < 4) { - nbt = null; - for (Map.Entry entry : nbtMap.entrySet()) { - IntegerTrio key = entry.getKey(); - int index = getIndex(key.x, key.y, key.z); - if (index == i) { - nbt = entry.getValue(); - break; - } + private BaseBlock toBaseBlock(BlockState state, int i) { + if (state.getMaterial().hasContainer() && !nbtMap.isEmpty()) { + CompoundTag nbt; + if (nbtMap.size() < 4) { + nbt = null; + for (Map.Entry entry : nbtMap.entrySet()) { + IntegerTrio key = entry.getKey(); + int index = getIndex(key.x, key.y, key.z); + if (index == i) { + nbt = entry.getValue(); + break; } - } else { - // x + z * width + y * area; - int y = i / area; - int newI = i - y * area; - int z = newI / width; - int x = newI - z * width; - nbt = nbtMap.get(new IntegerTrio(x, y, z)); - } - if (nbt != null) { - return base.toBaseBlock(nbt); } + } else { + int y = i / getArea(); + int newI = i - y * getArea(); + int z = newI / getWidth(); + int x = newI - z * getWidth(); + nbt = nbtMap.get(new IntegerTrio(x, y, z)); } - return base; + return state.toBaseBlock(nbt); + } + return state.toBaseBlock(); + } + + private BaseBlock toBaseBlock(BlockState state, int x, int y, int z) { + if (state.getMaterial().hasContainer() && !nbtMap.isEmpty()) { + CompoundTag nbt = nbtMap.get(new IntegerTrio(x, y, z)); + return state.toBaseBlock(nbt); + } + return state.toBaseBlock(); + } + + @Override + public BaseBlock getFullBlock(int i) { + return toBaseBlock(getBlock(i), i); + } + + @Override + public BlockState getBlock(int index) { + try { + int diskIndex = HEADER_SIZE + (index << 1); + char ordinal = byteBuffer.getChar(diskIndex); + return BlockState.getFromOrdinal(ordinal); } catch (IndexOutOfBoundsException ignore) { } catch (Exception e) { e.printStackTrace(); } - return BlockTypes.AIR.getDefaultState().toBaseBlock(); + return BlockTypes.AIR.getDefaultState(); + } + + @Override + public BlockState getBlock(int x, int y, int z) { + return getBlock(getIndex(x, y, z)); } @Override @@ -494,9 +384,9 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { @Override public > boolean setBlock(int x, int y, int z, B block) { try { - int index = HEADER_SIZE + (getIndex(x, y, z) << 2); - int combined = block.getInternalId(); - byteBuffer.putInt(index, combined); + int index = HEADER_SIZE + (getIndex(x, y, z) << 1); + char ordinal = block.getOrdinalChar(); + byteBuffer.putChar(index, ordinal); boolean hasNbt = block instanceof BaseBlock && block.hasNbtData(); if (hasNbt) { setTile(x, y, z, block.getNbtData()); @@ -511,15 +401,15 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { @Override public > boolean setBlock(int i, B block) { try { - int combined = block.getInternalId(); - int index = HEADER_SIZE + (i << 2); - byteBuffer.putInt(index, combined); + char ordinal = block.getOrdinalChar(); + int index = HEADER_SIZE + (i << 1); + byteBuffer.putChar(index, ordinal); boolean hasNbt = block instanceof BaseBlock && block.hasNbtData(); if (hasNbt) { - int y = i / area; - int newI = i - y * area; - int z = newI / width; - int x = newI - z * width; + int y = i / getArea(); + int newI = i - y * getArea(); + int z = newI / getWidth(); + int x = newI - z * getWidth(); setTile(x, y, z, block.getNbtData()); } return true; @@ -529,9 +419,10 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { return false; } + @Nullable @Override - public Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) { - FaweClipboard.ClipboardEntity ret = new ClipboardEntity(world, x, y, z, yaw, pitch, entity); + public Entity createEntity(Location location, BaseEntity entity) { + BlockArrayClipboard.ClipboardEntity ret = new BlockArrayClipboard.ClipboardEntity(location, entity); entities.add(ret); return ret; } @@ -542,7 +433,21 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { } @Override - public boolean remove(ClipboardEntity clipboardEntity) { - return entities.remove(clipboardEntity); + public void removeEntity(Entity entity) { + this.entities.remove(entity); + } + + @Nullable + @Override + public void removeEntity(int x, int y, int z, UUID uuid) { + Iterator iter = this.entities.iterator(); + while (iter.hasNext()) { + BlockArrayClipboard.ClipboardEntity entity = iter.next(); + UUID entUUID = entity.getState().getNbtData().getUUID(); + if (uuid.equals(entUUID)) { + iter.remove(); + return; + } + } } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/EmptyClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/EmptyClipboard.java index 3cee79a4a..e67d1bc38 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/EmptyClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/EmptyClipboard.java @@ -2,6 +2,7 @@ package com.boydti.fawe.object.clipboard; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; @@ -39,6 +40,11 @@ public class EmptyClipboard implements Clipboard { public void setOrigin(BlockVector3 origin) { } + @Override + public void removeEntity(Entity entity) { + + } + @Override public BlockVector3 getMinimumPoint() { return BlockVector3.ZERO; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/FaweClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/FaweClipboard.java deleted file mode 100644 index 75011e299..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/FaweClipboard.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.boydti.fawe.object.clipboard; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.boydti.fawe.jnbt.NBTStreamer; -import com.boydti.fawe.util.ReflectionUtils; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.IntTag; -import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.entity.BaseEntity; -import com.sk89q.worldedit.entity.Entity; -import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockStateHolder; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import javax.annotation.Nullable; - -public abstract class FaweClipboard { - public abstract BaseBlock getBlock(int x, int y, int z); - - public abstract > boolean setBlock(int index, B block); - - public abstract > boolean setBlock(int x, int y, int z, B block); - - /** - * Returns true if the clipboard has biome data. This can be checked since {@link Extent#getBiome(BlockVector2)} - * strongly suggests returning {@link com.sk89q.worldedit.world.biome.BiomeTypes#OCEAN} instead of {@code null} - * if biomes aren't present. However, it might not be desired to set areas to ocean if the clipboard is defaulting - * to ocean, instead of having biomes explicitly set. - * - * @return true if the clipboard has biome data set - */ - public boolean hasBiomes() { - return false; - } - - public abstract boolean setBiome(int x, int z, BiomeType biome); - - public abstract BiomeType getBiome(int x, int z); - - public abstract BiomeType getBiome(int index); - - public abstract BaseBlock getBlock(int index); - - public abstract void setBiome(int index, BiomeType biome); - - public abstract boolean setTile(int x, int y, int z, CompoundTag tag); - - public abstract Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity); - - public abstract List getEntities(); - - public abstract boolean remove(ClipboardEntity clipboardEntity); - - public void setOrigin(BlockVector3 offset) { - } // Do nothing - - public abstract void setDimensions(BlockVector3 dimensions); - - public abstract BlockVector3 getDimensions(); - - /** - * The locations provided are relative to the clipboard min - * - * @param task - * @param air - */ - public abstract void forEach(BlockReader task, boolean air); - - public interface BlockReader { - > void run(int x, int y, int z, B block); - } - - public abstract void streamBiomes(NBTStreamer.ByteReader task); - - public void streamCombinedIds(NBTStreamer.ByteReader task) { - forEach(new BlockReader() { - private int index; - - @Override - public > void run(int x, int y, int z, B block) { - task.run(index++, block.getInternalId()); - } - }, true); - } - - public List getTileEntities() { - final List tiles = new ArrayList<>(); - forEach(new BlockReader() { - - @Override - public > void run(int x, int y, int z, B block) { - if(!(block instanceof BaseBlock)) return; - BaseBlock base = (BaseBlock)block; - CompoundTag tag = base.getNbtData(); - if (tag != null) { - Map values = ReflectionUtils.getMap(tag.getValue()); - values.put("x", new IntTag(x)); - values.put("y", new IntTag(y)); - values.put("z", new IntTag(z)); - tiles.add(tag); - } - } - }, false); - return tiles; - } - - public void close() {} - - public void flush() {} - - /** - * Stores entity data. - */ - public class ClipboardEntity implements Entity { - private final BaseEntity entity; - private final Extent world; - private final double x, y, z; - private final float yaw, pitch; - - public ClipboardEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) { - checkNotNull(entity); - checkNotNull(world); - this.world = world; - this.x = x; - this.y = y; - this.z = z; - this.yaw = yaw; - this.pitch = pitch; - this.entity = new BaseEntity(entity); - } - - @Override - public boolean remove() { - return FaweClipboard.this.remove(this); - } - - @Nullable - @Override - public T getFacet(Class cls) { - return null; - } - - /** - * Get the entity state. This is not a copy. - * - * @return the entity - */ - BaseEntity getEntity() { - return entity; - } - - @Override - public BaseEntity getState() { - return new BaseEntity(entity); - } - - @Override - public Location getLocation() { - return new Location(world, x, y, z, yaw, pitch); - } - - @Override - public Extent getExtent() { - return world; - } - - @Override - public boolean setLocation(Location location) { - //Should not be teleporting this entity - return false; - } - } -} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/LinearClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/LinearClipboard.java new file mode 100644 index 000000000..d7c4c32fe --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/LinearClipboard.java @@ -0,0 +1,105 @@ +package com.boydti.fawe.object.clipboard; + +import com.boydti.fawe.beta.implementation.filter.block.AbstractFilterBlock; +import com.boydti.fawe.jnbt.streamer.IntValueReader; +import com.google.common.collect.ForwardingIterator; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +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 java.io.Closeable; +import java.util.Collection; +import java.util.Iterator; + +/** + * Best used when clipboard selections are small, or using legacy formats + * (Small being < Integer.MAX_VALUE/BLOCK_SIZE_BYTES blocks) + */ +public abstract class LinearClipboard extends SimpleClipboard implements Clipboard, Closeable { + public LinearClipboard(BlockVector3 dimensions) { + super(dimensions); + } + + public abstract > boolean setBlock(int i, B block); + + public abstract BaseBlock getFullBlock(int i); + + public abstract BlockState getBlock(int i); + + public abstract void setBiome(int index, BiomeType biome); + + public abstract BiomeType getBiome(int index); + + /** + * The locations provided are relative to the clipboard min + * + * @param task + * @param air + */ + public abstract void streamBiomes(IntValueReader task); + + public abstract Collection getTileEntities(); + + public void close() {} + + public void flush() {} + + @Override + protected void finalize() { + close(); + } + + @Override + public Iterator iterator() { + Region region = getRegion(); + if (region instanceof CuboidRegion) { + Iterator iter = ((CuboidRegion) region).iterator_old(); + LinearFilter filter = new LinearFilter(); + + return new ForwardingIterator() { + @Override + protected Iterator delegate() { + return iter; + } + + @Override + public BlockVector3 next() { + return filter.next(super.next()); + } + }; + } else { + return super.iterator(); + } + } + + private class LinearFilter extends AbstractFilterBlock { + private int index = -1; + private BlockVector3 position; + + private LinearFilter next(BlockVector3 position) { + this.position = position; + index++; + return this; + } + @Override + public BaseBlock getFullBlock() { + return LinearClipboard.this.getFullBlock(index); + } + + @Override + public void setFullBlock(BaseBlock block) { + LinearClipboard.this.setBlock(index, block); + } + + @Override + public BlockVector3 getPosition() { + return position; + } + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/LinearClipboardBuilder.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/LinearClipboardBuilder.java new file mode 100644 index 000000000..d1d9f2448 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/LinearClipboardBuilder.java @@ -0,0 +1,28 @@ +package com.boydti.fawe.object.clipboard; + +import com.boydti.fawe.object.io.FastByteArrayOutputStream; + +import java.util.function.BiFunction; + +public class LinearClipboardBuilder { + FastByteArrayOutputStream blocksOut = new FastByteArrayOutputStream(); + FastByteArrayOutputStream biomesOut = new FastByteArrayOutputStream(); + + public int width, height, length; + + public void setWidth(int width) { + this.width = width; + } + + public void setHeight(int height) { + this.height = height; + } + + public void setLength(int length) { + this.length = length; + } + + public LinearClipboard build() { + return null; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java index 7d36969d6..5cedda7ad 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java @@ -1,7 +1,7 @@ package com.boydti.fawe.object.clipboard; import com.boydti.fawe.config.Settings; -import com.boydti.fawe.jnbt.NBTStreamer; +import com.boydti.fawe.jnbt.streamer.IntValueReader; import com.boydti.fawe.object.IntegerTrio; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.ReflectionUtils; @@ -10,33 +10,34 @@ import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; -import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; 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.block.BlockTypes; + +import javax.annotation.Nullable; +import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; -import net.jpountz.util.SafeUtils; +import java.util.UUID; -public class MemoryOptimizedClipboard extends FaweClipboard { +public class MemoryOptimizedClipboard extends LinearClipboard { - private static final int BLOCK_SIZE = 1048576 * 4; + private static final int BLOCK_SIZE = 1048576 * 2; private static final int BLOCK_MASK = 1048575; private static final int BLOCK_SHIFT = 20; - private int length; - private int height; - private int width; - private int area; - private int volume; - private byte[][] states; private byte[] buffer = new byte[MainUtil.getMaxCompressedLength(BLOCK_SIZE)]; @@ -45,7 +46,7 @@ public class MemoryOptimizedClipboard extends FaweClipboard { private final HashMap nbtMapLoc; private final HashMap nbtMapIndex; - private final HashSet entities; + private final HashSet entities; private int lastCombinedIdsI = -1; @@ -55,17 +56,13 @@ public class MemoryOptimizedClipboard extends FaweClipboard { private int compressionLevel; - public MemoryOptimizedClipboard(int width, int height, int length) { - this(width, height, length, Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL); + public MemoryOptimizedClipboard(BlockVector3 dimensions) { + this(dimensions, Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL); } - public MemoryOptimizedClipboard(int width, int height, int length, int compressionLevel) { - this.width = width; - this.height = height; - this.length = length; - this.area = width * length; - this.volume = area * height; - states = new byte[1 + (volume >> BLOCK_SHIFT)][]; + public MemoryOptimizedClipboard(BlockVector3 dimensions, int compressionLevel) { + super(dimensions); + states = new byte[1 + (getVolume() >> BLOCK_SHIFT)][]; nbtMapLoc = new HashMap<>(); nbtMapIndex = new HashMap<>(); entities = new HashSet<>(); @@ -89,7 +86,7 @@ public class MemoryOptimizedClipboard extends FaweClipboard { } @Override - public boolean setBiome(int x, int z, BiomeType biome) { + public boolean setBiome(int x, int y, int z, BiomeType biome) { setBiome(getIndex(x, 0, z), biome); return true; } @@ -97,19 +94,24 @@ public class MemoryOptimizedClipboard extends FaweClipboard { @Override public void setBiome(int index, BiomeType biome) { if (biomes == null) { - biomes = new byte[area]; + biomes = new byte[getArea()]; } biomes[index] = (byte) biome.getInternalId(); } @Override - public void streamBiomes(NBTStreamer.ByteReader task) { + public void streamBiomes(IntValueReader task) { if (!hasBiomes()) return; int index = 0; - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++, index++) { - task.run(index, biomes[index] & 0xFF); + try { + for (int z = 0; z < getLength(); z++) { + for (int x = 0; x < getWidth(); x++, index++) { + task.applyInt(index, biomes[index] & 0xFF); + } } + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); } } @@ -122,7 +124,7 @@ public class MemoryOptimizedClipboard extends FaweClipboard { } @Override - public BiomeType getBiome(int x, int z) { + public BiomeType getBiomeType(int x, int z) { return getBiome(getIndex(x, 0, z)); } @@ -131,22 +133,24 @@ public class MemoryOptimizedClipboard extends FaweClipboard { return nbtMapIndex.get(index); } - public int getCombinedId(int index) { + public int getOrdinal(int index) { int i = index >> BLOCK_SHIFT; - if (i == lastCombinedIdsI) { - if (lastCombinedIds == null) { + int li = (index & BLOCK_MASK) << 1; + if (i != lastCombinedIdsI) { + saveCombinedIds(); + byte[] compressed = states[lastCombinedIdsI = i]; + if (compressed != null) { + lastCombinedIds = MainUtil.decompress(compressed, lastCombinedIds, BLOCK_SIZE, compressionLevel); + } else { + lastCombinedIds = null; return 0; } - return lastCombinedIds[index & BLOCK_MASK] & 0xFF; } - saveCombinedIds(); - byte[] compressed = states[lastCombinedIdsI = i]; - if (compressed == null) { - lastCombinedIds = null; - return BlockTypes.AIR.getInternalId(); + if (lastCombinedIds == null) { + return 0; } - lastCombinedIds = MainUtil.decompress(compressed, lastCombinedIds, BLOCK_SIZE, compressionLevel); - return SafeUtils.readIntBE(lastCombinedIds, index & BLOCK_MASK); + int ordinal = ((lastCombinedIds[li] << 8) + lastCombinedIds[li + 1]); + return ordinal; } private void saveCombinedIds() { @@ -156,26 +160,6 @@ public class MemoryOptimizedClipboard extends FaweClipboard { saveCombinedIds = false; } - @Override - public void setDimensions(BlockVector3 dimensions) { - width = dimensions.getBlockX(); - height = dimensions.getBlockY(); - length = dimensions.getBlockZ(); - area = width * length; - int newVolume = area * height; - if (newVolume != volume) { - volume = newVolume; - states = new byte[1 + (volume >> BLOCK_SHIFT)][]; - lastCombinedIdsI = -1; - saveCombinedIds = false; - } - } - - @Override - public BlockVector3 getDimensions() { - return BlockVector3.at(width, height, length); - } - private int lastI; private int lastIMin; private int lastIMax; @@ -207,45 +191,30 @@ public class MemoryOptimizedClipboard extends FaweClipboard { } lastCombinedIds = new byte[BLOCK_SIZE]; } - int li = (index & BLOCK_MASK) << 2; - lastCombinedIds[li] = (byte) ((v >>> 24) & 0xFF); - lastCombinedIds[li + 1] = (byte) ((v >>> 16) & 0xFF); - lastCombinedIds[li + 2] = (byte) ((v >>> 8) & 0xFF); - lastCombinedIds[li + 3] = (byte) ((v >>> 0) & 0xFF); + int li = (index & BLOCK_MASK) << 1; + lastCombinedIds[li] = (byte) ((v >>> 8) & 0xFF); + lastCombinedIds[li + 1] = (byte) (v & 0xFF); saveCombinedIds = true; } @Override - public void streamCombinedIds(NBTStreamer.ByteReader task) { - int index = 0; - for (int y = 0; y < height; y++) { - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++) { - int id = getCombinedId(index); - task.run(index++, id); - } - } - } - } - - @Override - public List getTileEntities() { + public Collection getTileEntities() { convertTilesToIndex(); for (Map.Entry entry : nbtMapIndex.entrySet()) { int index = entry.getKey(); CompoundTag tag = entry.getValue(); Map values = ReflectionUtils.getMap(tag.getValue()); if (!values.containsKey("x")) { - int y = index / area; - index -= y * area; - int z = index / width; - int x = index - (z * width); + int y = index / getArea(); + index -= y * getArea(); + int z = index / getWidth(); + int x = index - (z * getWidth()); values.put("x", new IntTag(x)); values.put("y", new IntTag(y)); values.put("z", new IntTag(z)); } } - return new ArrayList<>(nbtMapIndex.values()); + return nbtMapIndex.values(); } private int ylast; @@ -254,52 +223,36 @@ public class MemoryOptimizedClipboard extends FaweClipboard { private int zlasti; public int getIndex(int x, int y, int z) { - return x + ((ylast == y) ? ylasti : (ylasti = (ylast = y) * area)) + ((zlast == z) ? zlasti : (zlasti = (zlast = z) * width)); + return x + ((ylast == y) ? ylasti : (ylasti = (ylast = y) * getArea())) + ((zlast == z) ? zlasti : (zlasti = (zlast = z) * getWidth())); } @Override - public BaseBlock getBlock(int x, int y, int z) { + public BaseBlock getFullBlock(int x, int y, int z) { int index = getIndex(x, y, z); - return getBlock(index); + return getFullBlock(index); } @Override - public BaseBlock getBlock(int index) { - int combinedId = getCombinedId(index); - BlockType type = BlockTypes.getFromStateId(combinedId); - BaseBlock base = type.withStateId(combinedId).toBaseBlock(); - if (type.getMaterial().hasContainer()) { + public BaseBlock getFullBlock(int index) { + BlockState block = getBlock(index); + if (block.getMaterial().hasContainer()) { CompoundTag nbt = getTag(index); if (nbt != null) { - return base.toBaseBlock(nbt); + return block.toBaseBlock(nbt); } } - return base; + return block.toBaseBlock(); } @Override - public void forEach(final BlockReader task, final boolean air) { - if (air) { - for (int y = 0, index = 0; y < height; y++) { - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++, index++) { - BaseBlock block = getBlock(index); - task.run(x, y, z, block); - } - } - } - } else { - for (int y = 0, index = 0; y < height; y++) { - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++, index++) { - BaseBlock block = getBlock(index); - if (!block.getMaterial().isAir()) { - task.run(x, y, z, block); - } - } - } - } - } + public BlockState getBlock(int index) { + int ordinal = getOrdinal(index); + return BlockState.getFromOrdinal(ordinal); + } + + @Override + public BlockState getBlock(int x, int y, int z) { + return getBlock(getIndex(x, y, z)); } public int size() { @@ -344,9 +297,10 @@ public class MemoryOptimizedClipboard extends FaweClipboard { return true; } + @Nullable @Override - public Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) { - FaweClipboard.ClipboardEntity ret = new ClipboardEntity(world, x, y, z, yaw, pitch, entity); + public Entity createEntity(Location location, BaseEntity entity) { + BlockArrayClipboard.ClipboardEntity ret = new BlockArrayClipboard.ClipboardEntity(location, entity); entities.add(ret); return ret; } @@ -357,7 +311,21 @@ public class MemoryOptimizedClipboard extends FaweClipboard { } @Override - public boolean remove(ClipboardEntity clipboardEntity) { - return entities.remove(clipboardEntity); + public void removeEntity(Entity entity) { + this.entities.remove(entity); + } + + @Nullable + @Override + public void removeEntity(int x, int y, int z, UUID uuid) { + Iterator iter = this.entities.iterator(); + while (iter.hasNext()) { + BlockArrayClipboard.ClipboardEntity entity = iter.next(); + UUID entUUID = entity.getState().getNbtData().getUUID(); + if (uuid.equals(entUUID)) { + iter.remove(); + return; + } + } } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MultiClipboardHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MultiClipboardHolder.java index b9e3ad59d..6a1345c50 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MultiClipboardHolder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MultiClipboardHolder.java @@ -35,11 +35,8 @@ public class MultiClipboardHolder extends URIClipboardHolder { super(URI.create(""), EmptyClipboard.INSTANCE); holders = new ArrayList<>(); URI uri = URI.create(""); - if (clipboard instanceof BlockArrayClipboard) { - FaweClipboard fc = ((BlockArrayClipboard) clipboard).IMP; - if (fc instanceof DiskOptimizedClipboard) { - uri = ((DiskOptimizedClipboard) fc).getFile().toURI(); - } + if (clipboard.getURI() != null) { + uri = clipboard.getURI(); } add(uri, clipboard); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/OffsetFaweClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/OffsetFaweClipboard.java deleted file mode 100644 index 3982df681..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/OffsetFaweClipboard.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.boydti.fawe.object.clipboard; - -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockStateHolder; - -public class OffsetFaweClipboard extends AbstractDelegateFaweClipboard { - private final int ox, oy, oz; - - public OffsetFaweClipboard(FaweClipboard parent, int ox, int oy, int oz) { - super(parent); - this.ox = ox; - this.oy = oy; - this.oz = oz; - } - - public OffsetFaweClipboard(FaweClipboard parent, int offset) { - this(parent, offset, offset, offset); - } - - @Override - public BaseBlock getBlock(int x, int y, int z) { - return super.getBlock(x + ox, y + oy, z + oz); - } - - @Override - public > boolean setBlock(int x, int y, int z, B block) { - return super.setBlock(ox + x, oy + y, oz + z, block); - } - - @Override - public boolean setBiome(int x, int z, BiomeType biome) { - return super.setBiome(ox + x, oz + z, biome); - } - - @Override - public BiomeType getBiome(int x, int z) { - return super.getBiome(ox + x, oz + z); - } - - @Override - public boolean setTile(int x, int y, int z, CompoundTag tag) { - return super.setTile(ox + x, oy + y, oz + z, tag); - } - - @Override - public void forEach(final BlockReader task, boolean air) { - super.forEach(new BlockReader() { - @Override - public > void run(int x, int y, int z, B block) { - task.run(x - ox, y - oy, z - oz, block); - } - }, air); - } -} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/ReadOnlyClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/ReadOnlyClipboard.java index 7ad3a4fdc..620c582ba 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/ReadOnlyClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/ReadOnlyClipboard.java @@ -1,21 +1,21 @@ package com.boydti.fawe.object.clipboard; -import com.boydti.fawe.jnbt.NBTStreamer; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; + import java.util.List; -public abstract class ReadOnlyClipboard extends FaweClipboard { +public abstract class ReadOnlyClipboard extends SimpleClipboard { public final Region region; public ReadOnlyClipboard(Region region) { + super(region.getDimensions()); this.region = region; } @@ -32,52 +32,10 @@ public abstract class ReadOnlyClipboard extends FaweClipboard { } @Override - public BlockVector3 getDimensions() { - return region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1); - } - - @Override - public void setDimensions(BlockVector3 dimensions) { + public boolean setBiome(int x, int y, int z, BiomeType biome) { throw new UnsupportedOperationException("Clipboard is immutable"); } - @Override - public BaseBlock getBlock(int index) { - throw new UnsupportedOperationException("World based clipboards do not provide index access"); - } - - @Override - public BiomeType getBiome(int index) { - throw new UnsupportedOperationException("World based clipboards do not provide index access"); - } - - @Override - public boolean setBiome(int x, int z, BiomeType biome) { - throw new UnsupportedOperationException("Clipboard is immutable"); - } - - @Override - public void setBiome(int index, BiomeType biome) { - throw new UnsupportedOperationException("Clipboard is immutable"); - } - - @Override - public void streamBiomes(NBTStreamer.ByteReader task) { - BlockVector3 dim = getDimensions(); - int index = 0; - for (int z = 0; z <= dim.getBlockZ(); z++) { - for (int x = 0; x <= dim.getBlockX(); x++, index++) { - task.run(index, getBiome(x, z).getInternalId()); - } - } - } - - @Override - public abstract BaseBlock getBlock(int x, int y, int z); - - @Override - public abstract BiomeType getBiome(int x, int z); - @Override public abstract List getEntities(); @@ -86,23 +44,18 @@ public abstract class ReadOnlyClipboard extends FaweClipboard { throw new UnsupportedOperationException("Clipboard is immutable"); } - @Override - public boolean setBlock(int index, BlockStateHolder block) { - throw new UnsupportedOperationException("Clipboard is immutable"); - } - @Override public boolean setTile(int x, int y, int z, CompoundTag tag) { throw new UnsupportedOperationException("Clipboard is immutable"); } @Override - public Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) { + public Entity createEntity(Location location, BaseEntity entity) { throw new UnsupportedOperationException("Clipboard is immutable"); } @Override - public boolean remove(ClipboardEntity clipboardEntity) { + public void removeEntity(Entity entity) { throw new UnsupportedOperationException("Clipboard is immutable"); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/ResizableClipboardBuilder.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/ResizableClipboardBuilder.java index ffa48eb06..50a3e47bd 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/ResizableClipboardBuilder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/ResizableClipboardBuilder.java @@ -23,7 +23,6 @@ public class ResizableClipboardBuilder extends MemoryOptimizedHistory { private int maxY = Integer.MIN_VALUE; private int maxZ = Integer.MIN_VALUE; - public ResizableClipboardBuilder(World world) { super(world); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/SimpleClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/SimpleClipboard.java new file mode 100644 index 000000000..b614e5bef --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/SimpleClipboard.java @@ -0,0 +1,81 @@ +package com.boydti.fawe.object.clipboard; + +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; + +import java.io.Closeable; + +public abstract class SimpleClipboard implements Clipboard, Closeable { + private final BlockVector3 size; + private final int area; + private final int volume; + private BlockVector3 origin; + + public SimpleClipboard(BlockVector3 dimensions) { + this.size = dimensions; + long longVolume = (long) getWidth() * (long) getHeight() * (long) getLength(); + if (longVolume >= Integer.MAX_VALUE >> 2) { + throw new IllegalArgumentException("Dimensions are too large for this clipboard format."); + } + this.area = getWidth() * getLength(); + this.volume = (int) longVolume; + this.origin = BlockVector3.ZERO; + } + + @Override + public void setOrigin(BlockVector3 offset) { + this.origin = offset; + } + + @Override + public BlockVector3 getOrigin() { + return origin; + } + + @Override + public BlockVector3 getMinimumPoint() { + return BlockVector3.ZERO; + } + + @Override + public BlockVector3 getMaximumPoint() { + return size.subtract(BlockVector3.ONE); + } + + @Override + public Region getRegion() { + return new CuboidRegion(BlockVector3.at(0, 0, 0), BlockVector3.at(getWidth() - 1, getHeight() - 1, getLength() - 1)); + } + + @Override + public final BlockVector3 getDimensions() { + return size; + } + + @Override + public final int getWidth() { + return size.getBlockX(); + } + + @Override + public final int getHeight() { + return size.getBlockY(); + } + + @Override + public final int getLength() { + return size.getBlockZ(); + } + + @Override + public int getArea() { + return area; + } + + @Override + public int getVolume() { + return volume; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java index bdf159e48..43a9a45d8 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java @@ -17,6 +17,8 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockTypes; + +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -45,7 +47,7 @@ public class WorldCopyClipboard extends ReadOnlyClipboard { } @Override - public BaseBlock getBlock(int x, int y, int z) { + public BaseBlock getFullBlock(int x, int y, int z) { return extent.getFullBlock(BlockVector3.at(mx + x, my + y, mz + z)); } @@ -54,7 +56,7 @@ public class WorldCopyClipboard extends ReadOnlyClipboard { } @Override - public BiomeType getBiome(int x, int z) { + public BiomeType getBiomeType(int x, int z) { return extent.getBiome(MutableBlockVector2.setComponents(mx + x, mz + z)); } @@ -70,84 +72,7 @@ public class WorldCopyClipboard extends ReadOnlyClipboard { } @Override - public void forEach(BlockReader task, boolean air) { - BlockVector3 min = region.getMinimumPoint(); - BlockVector3 max = region.getMaximumPoint(); - MutableBlockVector3 pos = new MutableBlockVector3(); - if (region instanceof CuboidRegion) { - if (air) { - ((CuboidRegion) region).setUseOldIterator(true); - RegionVisitor visitor = new RegionVisitor(region, pos1 -> { - BaseBlock block = getBlockAbs(pos1.getBlockX(), pos1.getBlockY(), pos1.getBlockZ()); - int x = pos1.getBlockX() - mx; - int y = pos1.getBlockY() - my; - int z = pos1.getBlockZ() - mz; - if (block.hasNbtData()) { - Map values = ReflectionUtils.getMap(block.getNbtData().getValue()); - values.put("x", new IntTag(x)); - values.put("y", new IntTag(y)); - values.put("z", new IntTag(z)); - } - task.run(x, y, z, block); - return true; - }); - Operations.completeBlindly(visitor); - } else { - CuboidRegion cuboidEquivalent = new CuboidRegion(region.getMinimumPoint(), region.getMaximumPoint()); - cuboidEquivalent.setUseOldIterator(true); - RegionVisitor visitor = new RegionVisitor(cuboidEquivalent, new RegionFunction() { - @Override - public boolean apply(BlockVector3 pos) throws WorldEditException { - int x = pos.getBlockX() - mx; - int y = pos.getBlockY() - my; - int z = pos.getBlockZ() - mz; - if (region.contains(pos)) { - BaseBlock block = extent.getFullBlock(pos); - if (block.hasNbtData()) { - Map values = ReflectionUtils.getMap(block.getNbtData().getValue()); - values.put("x", new IntTag(x)); - values.put("y", new IntTag(y)); - values.put("z", new IntTag(z)); - } - if (!block.getBlockType().getMaterial().isAir()) { - task.run(x, y, z, block); - } - } + public void close() { - return true; - } - }); - Operations.completeBlindly(visitor); - } - } else { - for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { - pos.mutY(y); - int yy = pos.getBlockY() - my; - for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { - pos.mutZ(z); - int zz = pos.getBlockZ() - mz; - for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { - pos.mutX(x); - int xx = pos.getBlockX() - mx; - if (region.contains(pos)) { -// BlockState block = getBlockAbs(x, y, z); - BaseBlock block = extent.getFullBlock(pos); - if (!air && block.getBlockType().getMaterial().isAir()) { - continue; - } - if (block.hasNbtData()) { - Map values = ReflectionUtils.getMap(block.getNbtData().getValue()); - values.put("x", new IntTag(xx)); - values.put("y", new IntTag(yy)); - values.put("z", new IntTag(zz)); - } - task.run(xx, yy, zz, block); - } else if (air) { - task.run(xx, yy, zz, BlockTypes.AIR.getDefaultState()); - } - } - } - } - } } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCutClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCutClipboard.java index 7340f9959..33d3c28a6 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCutClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCutClipboard.java @@ -6,6 +6,8 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockTypes; +import java.io.IOException; + public class WorldCutClipboard extends WorldCopyClipboard { public WorldCutClipboard(EditSession editSession, Region region, boolean copyEntities, boolean copyBiome) { super(editSession, region, copyEntities, copyBiome); @@ -16,7 +18,7 @@ public class WorldCutClipboard extends WorldCopyClipboard { } @Override - public BaseBlock getBlock(int x, int y, int z) { + public BaseBlock getFullBlock(int x, int y, int z) { int xx = mx + x; int yy = my + y; int zz = mz + z; @@ -33,8 +35,7 @@ public class WorldCutClipboard extends WorldCopyClipboard { } @Override - public void forEach(BlockReader task, boolean air) { - super.forEach(task, air); + public void close() { if (extent instanceof EditSession) { ((EditSession) extent).flushQueue(); } else { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BitArray4096.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BitArray4096.java index 6dc10cd77..f9e434db3 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BitArray4096.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BitArray4096.java @@ -28,6 +28,10 @@ public final class BitArray4096 { this.data = new long[longLen]; } + public long[] getData() { + return data; + } + public final void setAt(int index, int value) { if (longLen == 0) return; int bitIndexStart = index * bitsPerEntry; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BlockVector3ChunkMap.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BlockVector3ChunkMap.java index bdb98ccd6..07bfbcb45 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BlockVector3ChunkMap.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BlockVector3ChunkMap.java @@ -47,6 +47,11 @@ public class BlockVector3ChunkMap implements Map, IAdaptedMa return map.put(key, value); } + public T remove(int x, int y, int z) { + short key = MathMan.tripleBlockCoord(x, y, z); + return map.remove(key); + } + public boolean contains(int x, int y, int z) { short key = MathMan.tripleBlockCoord(x, y, z); return map.containsKey(key); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/CleanableThreadLocal.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/CleanableThreadLocal.java index 094b5ca1f..9a1c6ff34 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/CleanableThreadLocal.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/CleanableThreadLocal.java @@ -5,7 +5,10 @@ import com.boydti.fawe.util.MainUtil; import java.lang.ref.Reference; import java.lang.reflect.Array; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.LongAdder; import java.util.function.Consumer; import java.util.function.Function; @@ -51,25 +54,47 @@ public class CleanableThreadLocal extends ThreadLocal { } } - public static void clean(ThreadLocal instance) { + public List getAll() { + List list = new ArrayList<>(); + iterate(this, new Consumer() { + Method methodGetEntry; + Field fieldValue; + @Override + public void accept(Object tlm) { + try { + if (methodGetEntry == null) { + methodGetEntry = tlm.getClass().getDeclaredMethod("getEntry", ThreadLocal.class); + methodGetEntry.setAccessible(true); + } + Object entry = methodGetEntry.invoke(tlm, CleanableThreadLocal.this); + if (entry != null) { + if (fieldValue == null) { + fieldValue = entry.getClass().getDeclaredField("value"); + fieldValue.setAccessible(true); + } + Object value = fieldValue.get(entry); + if (value != null) { + list.add((T) value); + } + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + }); + return list; + } + + public static void iterate(ThreadLocal instance, Consumer withMap) { try { Thread[] threads = MainUtil.getThreads(); Field tl = Thread.class.getDeclaredField("threadLocals"); tl.setAccessible(true); - Method methodRemove = null; for (Thread thread : threads) { if (thread != null) { Object tlm = tl.get(thread); if (tlm != null) { - if (methodRemove == null) { - methodRemove = tlm.getClass().getDeclaredMethod("remove", ThreadLocal.class); - methodRemove.setAccessible(true); - } - if (methodRemove != null) { - try { - methodRemove.invoke(tlm, instance); - } catch (Throwable ignore) {} - } + withMap.accept(tlm); } } } @@ -78,6 +103,29 @@ public class CleanableThreadLocal extends ThreadLocal { } } + public static void clean(ThreadLocal instance) { + iterate(instance, new Consumer() { + Method methodRemove; + @Override + public void accept(Object tlm) { + try { + if (methodRemove == null) { + methodRemove = tlm.getClass().getDeclaredMethod("remove", ThreadLocal.class); + methodRemove.setAccessible(true); + } + if (methodRemove != null) { + try { + methodRemove.invoke(tlm, instance); + } catch (Throwable ignore) { + } + } + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + }); + } + public static void cleanAll() { try { // Get a reference to the thread locals table of the current thread diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialArray.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialArray.java index 9141e7d6d..ac1ccfae0 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialArray.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialArray.java @@ -265,9 +265,9 @@ public final class DifferentialArray implements DifferentialCollection { return dataBytes[index]; } -// public char getChar(int index) { -// return dataChars[index]; -// } + public char getChar(int index) { + return dataChars[index]; + } public int getInt(int index) { return dataInts[index]; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialBlockBuffer.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialBlockBuffer.java index 233b3f9fa..a7bf2eed4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialBlockBuffer.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialBlockBuffer.java @@ -9,12 +9,12 @@ import java.lang.reflect.Array; * Records changes made through the {@link #set(int, int, int, int)} method
* Changes are not recorded if you edit the raw data */ -public final class DifferentialBlockBuffer implements DifferentialCollection { +public final class DifferentialBlockBuffer implements DifferentialCollection { private final int width, length; private final int t1, t2; - private int[][][][][] data; - private int[][][][][] changes; + private char[][][][][] data; + private char[][][][][] changes; public DifferentialBlockBuffer(int width, int length) { this.width = width; @@ -24,7 +24,7 @@ public final class DifferentialBlockBuffer implements DifferentialCollection> 4; int chunkZ = z >> 4; if (data == null) { - data = new int[t1][][][][]; - changes = new int[0][][][][]; + data = new char[t1][][][][]; + changes = new char[0][][][][]; } - int[][][][] arr = data[chunkZ]; + char[][][][] arr = data[chunkZ]; if (arr == null) { - arr = data[chunkZ] = new int[t2][][][]; + arr = data[chunkZ] = new char[t2][][][]; } - int[][][] arr2 = arr[chunkX]; + char[][][] arr2 = arr[chunkX]; if (arr2 == null) { - arr2 = arr[chunkX] = new int[256][][]; + arr2 = arr[chunkX] = new char[256][][]; } - int[][] yMap = arr2[y]; + char[][] yMap = arr2[y]; if (yMap == null) { - arr2[y] = yMap = new int[16][]; + arr2[y] = yMap = new char[16][]; } boolean newSection; int current; - int[] zMap = yMap[localZ]; + char[] zMap = yMap[localZ]; if (zMap == null) { - yMap[localZ] = zMap = new int[16]; + yMap[localZ] = zMap = new char[16]; if (changes == null) { - changes = new int[t1][][][][]; + changes = new char[t1][][][][]; } else if (changes != null && changes.length != 0) { - initialChange(changes, chunkX, chunkZ, localX, localZ, y, (int) -combined); + initialChange(changes, chunkX, chunkZ, localX, localZ, y, (char) -combined); } } else { - if (changes == null || changes.length == 0) changes = new int[t1][][][][]; - appendChange(changes, chunkX, chunkZ, localX, localZ, y, (int) (zMap[localX] - combined)); + if (changes == null || changes.length == 0) changes = new char[t1][][][][]; + appendChange(changes, chunkX, chunkZ, localX, localZ, y, (char) (zMap[localX] - combined)); } zMap[localX] = combined; } - private void initialChange(int[][][][][] src, int chunkX, int chunkZ, int localX, int localZ, int y, int combined) { - int[][][][] arr = src[chunkZ]; + private void initialChange(char[][][][][] src, int chunkX, int chunkZ, int localX, int localZ, int y, char combined) { + char[][][][] arr = src[chunkZ]; if (arr == null) { - src[chunkZ] = new int[0][][][]; + src[chunkZ] = new char[0][][][]; return; } else if (arr.length == 0) return; - int[][][] arr2 = arr[chunkX]; + char[][][] arr2 = arr[chunkX]; if (arr2 == null) { - arr[chunkX] = new int[0][][]; + arr[chunkX] = new char[0][][]; return; } else if (arr2.length == 0) return; - int[][] yMap = arr2[y]; + char[][] yMap = arr2[y]; if (yMap == null) { - arr2[y] = new int[0][]; + arr2[y] = new char[0][]; return; } else if (yMap.length == 0) return; - int[] zMap = yMap[localZ]; + char[] zMap = yMap[localZ]; if (zMap == null) { - yMap[localZ] = new int[0]; + yMap[localZ] = new char[0]; return; } else if (zMap.length == 0) return; @@ -189,23 +189,23 @@ public final class DifferentialBlockBuffer implements DifferentialCollection> BlockTypes.BIT_OFFSET; + int statesIndex = current.modifyIndex(stateId, i) >> BlockTypesCache.BIT_OFFSET; BlockState state = type.withPropertyId(statesIndex); int existingOrdinal = transformed[state.getOrdinal()]; - int existing = BlockTypes.states[existingOrdinal].getInternalId(); - //states[statesIndex] << BlockTypes.BIT_OFFSET; - BlockState newState = state.withPropertyId(property.modifyIndex(existing, index) >> BlockTypes.BIT_OFFSET); + int existing = BlockTypesCache.states[existingOrdinal].getInternalId(); + //states[statesIndex] << BlockTypesCache.BIT_OFFSET; + BlockState newState = state.withPropertyId(property.modifyIndex(existing, index) >> BlockTypesCache.BIT_OFFSET); transformed[state.getOrdinal()] = newState.getOrdinal(); } } @@ -135,7 +137,7 @@ public class PropertyPattern extends AbstractExtentPattern { } else { String regex = charSequence.toString(); blockTypeList = new ArrayList<>(); - for (BlockType myType : BlockTypes.values) { + for (BlockType myType : BlockTypesCache.values) { if (myType.getId().matches(regex)) { blockTypeList.add(myType); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/RandomFullClipboardPattern.java b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/RandomFullClipboardPattern.java index 53a8f473e..049311906 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/RandomFullClipboardPattern.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/RandomFullClipboardPattern.java @@ -2,7 +2,6 @@ package com.boydti.fawe.object.pattern; import static com.google.common.base.Preconditions.checkNotNull; -import com.boydti.fawe.object.schematic.Schematic; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.Clipboard; @@ -47,12 +46,11 @@ public class RandomFullClipboardPattern extends AbstractPattern { holder.setTransform(transform); } Clipboard clipboard = holder.getClipboard(); - Schematic schematic = new Schematic(clipboard); Transform newTransform = holder.getTransform(); if (newTransform.isIdentity()) { - schematic.paste(extent, set, false); + clipboard.paste(extent, set, false); } else { - schematic.paste(extent, set, false, newTransform); + clipboard.paste(extent, set, false, newTransform); } return true; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/SolidRandomOffsetPattern.java b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/SolidRandomOffsetPattern.java index d17295c68..a6283fd65 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/SolidRandomOffsetPattern.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/SolidRandomOffsetPattern.java @@ -9,6 +9,8 @@ import com.sk89q.worldedit.math.MutableBlockVector3; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; + import java.util.SplittableRandom; public class SolidRandomOffsetPattern extends AbstractPattern { @@ -21,7 +23,7 @@ public class SolidRandomOffsetPattern extends AbstractPattern { public static boolean[] getTypes() { boolean[] types = new boolean[BlockTypes.size()]; - for (BlockType type : BlockTypes.values) { + for (BlockType type : BlockTypesCache.values) { types[type.getInternalId()] = type.getMaterial().isSolid(); } return types; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java b/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java deleted file mode 100644 index 3365342c1..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java +++ /dev/null @@ -1,304 +0,0 @@ -package com.boydti.fawe.object.schematic; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.boydti.fawe.object.clipboard.FaweClipboard; -import com.boydti.fawe.object.clipboard.ReadOnlyClipboard; -import com.boydti.fawe.util.EditSessionBuilder; -import com.boydti.fawe.util.MaskTraverser; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.MaxChangedBlocksException; -import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.entity.Entity; -import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; -import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; -import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; -import com.sk89q.worldedit.extent.transform.BlockTransformExtent; -import com.sk89q.worldedit.function.RegionFunction; -import com.sk89q.worldedit.function.mask.ExistingBlockMask; -import com.sk89q.worldedit.function.mask.Mask; -import com.sk89q.worldedit.function.operation.ForwardExtentCopy; -import com.sk89q.worldedit.function.operation.Operation; -import com.sk89q.worldedit.function.operation.Operations; -import com.sk89q.worldedit.function.visitor.RegionVisitor; -import com.sk89q.worldedit.math.BlockVector2; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.math.MutableBlockVector2; -import com.sk89q.worldedit.math.transform.Transform; -import com.sk89q.worldedit.regions.CuboidRegion; -import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.world.World; -import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockStateHolder; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import javax.annotation.Nullable; - -public class Schematic { - - private final Clipboard clipboard; - - public Schematic(Clipboard clipboard) { - checkNotNull(clipboard); - this.clipboard = clipboard; - } - - /** - * Get the schematic for a region - * - * @param region - */ - public Schematic(Region region) { - checkNotNull(region); - checkNotNull(region.getWorld(), - "World cannot be null (use the other constructor for the region)"); - EditSession session = new EditSessionBuilder(region.getWorld()).allowedRegionsEverywhere() - .autoQueue(false).build(); - this.clipboard = new BlockArrayClipboard(region, ReadOnlyClipboard.of(session, region)); - } - - @Nullable - public Clipboard getClipboard() { - return clipboard; - } - - /** - * Forwards to paste(world, to, true, true, null) - * - * @param world - * @param to - * @return - */ - public EditSession paste(World world, BlockVector3 to) { - return paste(world, to, true, true, null); - } - - public void save(File file, ClipboardFormat format) throws IOException { - checkNotNull(file); - checkNotNull(format); - if (!file.exists()) { - File parent = file.getParentFile(); - if (parent != null) { - parent.mkdirs(); - } - file.createNewFile(); - } - save(new FileOutputStream(file), format); - } - - /** - * Save this schematic to a stream - * - * @param stream - * @param format - * @throws IOException - */ - public void save(OutputStream stream, ClipboardFormat format) throws IOException { - checkNotNull(stream); - checkNotNull(format); - try (ClipboardWriter writer = format.getWriter(stream)) { - writer.write(clipboard); - } - } - - public EditSession paste(World world, BlockVector3 to, boolean allowUndo, boolean pasteAir, - @Nullable Transform transform) { - return paste(world, to, allowUndo, pasteAir, true, transform); - } - - /** - * Paste this schematic in a world - * - * @param world - * @param to - * @param allowUndo - * @param pasteAir - * @param transform - * @return - */ - public EditSession paste(World world, BlockVector3 to, boolean allowUndo, boolean pasteAir, - boolean copyEntities, @Nullable Transform transform) { - checkNotNull(world); - checkNotNull(to); - EditSession editSession; - if (world instanceof EditSession) { - editSession = (EditSession) world; - } else { - EditSessionBuilder builder = new EditSessionBuilder(world).autoQueue(true) - .checkMemory(false).allowedRegionsEverywhere().limitUnlimited(); - if (allowUndo) { - editSession = builder.build(); - } else { - editSession = builder.changeSetNull().fastmode(true).build(); - } - } - Extent extent = clipboard; - Mask sourceMask = editSession.getSourceMask(); - if (transform != null && !transform.isIdentity()) { - extent = new BlockTransformExtent(clipboard, transform); - } else if (sourceMask == null) { - paste(editSession, to, pasteAir); - editSession.flushQueue(); - return editSession; - } - ForwardExtentCopy copy = new ForwardExtentCopy(extent, clipboard.getRegion(), - clipboard.getOrigin(), editSession, to); - if (transform != null && !transform.isIdentity()) { - copy.setTransform(transform); - } - copy.setCopyingEntities(copyEntities); - if (sourceMask != null) { - new MaskTraverser(sourceMask).reset(extent); - copy.setSourceMask(sourceMask); - editSession.setSourceMask(null); - } - if (!pasteAir) { - copy.setSourceMask(new ExistingBlockMask(clipboard)); - } - try { - Operations.completeLegacy(copy); - } catch (MaxChangedBlocksException e) { - e.printStackTrace(); - } - editSession.flushQueue(); - return editSession; - } - - public void paste(Extent extent, BlockVector3 to, boolean pasteAir, @Nullable Transform transform) { - Extent source = clipboard; - if (transform != null && !transform.isIdentity()) { - source = new BlockTransformExtent(clipboard, transform); - } - ForwardExtentCopy copy = new ForwardExtentCopy(source, clipboard.getRegion(), clipboard.getOrigin(), extent, to); - if (transform != null) { - copy.setTransform(transform); - } - copy.setCopyingBiomes(!(clipboard instanceof BlockArrayClipboard) || ((BlockArrayClipboard) clipboard).IMP - .hasBiomes()); - if (extent instanceof EditSession) { - EditSession editSession = (EditSession) extent; - Mask sourceMask = editSession.getSourceMask(); - if (sourceMask != null) { - new MaskTraverser(sourceMask).reset(extent); - copy.setSourceMask(sourceMask); - editSession.setSourceMask(null); - } - } - if (!pasteAir) { - copy.setSourceMask(new ExistingBlockMask(clipboard)); - } - Operations.completeBlindly(copy); - } - - public void paste(Extent extent, BlockVector3 to, boolean pasteAir) { - Region region = clipboard.getRegion().clone(); - final BlockVector3 bot = clipboard.getMinimumPoint(); - final BlockVector3 origin = clipboard.getOrigin(); - - final boolean copyBiomes = - !(clipboard instanceof BlockArrayClipboard) || ((BlockArrayClipboard) clipboard).IMP - .hasBiomes(); - - // Optimize for BlockArrayClipboard - if (clipboard instanceof BlockArrayClipboard && region instanceof CuboidRegion) { - // To is relative to the world origin (player loc + small clipboard offset) (As the positions supplied are relative to the clipboard min) - final int relx = to.getBlockX() + bot.getBlockX() - origin.getBlockX(); - final int rely = to.getBlockY() + bot.getBlockY() - origin.getBlockY(); - final int relz = to.getBlockZ() + bot.getBlockZ() - origin.getBlockZ(); - - BlockArrayClipboard bac = (BlockArrayClipboard) clipboard; - if (copyBiomes) { - bac.IMP.forEach(new FaweClipboard.BlockReader() { - MutableBlockVector2 mpos2d = new MutableBlockVector2(); - - { - mpos2d.setComponents(Integer.MIN_VALUE, Integer.MIN_VALUE); - } - - @Override - public > void run(int x, int y, int z, B block) { - try { - int xx = x + relx; - int zz = z + relz; - if (xx != mpos2d.getBlockX() || zz != mpos2d.getBlockZ()) { - mpos2d.setComponents(xx, zz); - extent.setBiome(mpos2d, bac.IMP.getBiome(x, z)); - } - if (!pasteAir && block.getBlockType().getMaterial().isAir()) { - return; - } - extent.setBlock(xx, y + rely, zz, block); - } catch (WorldEditException e) { - throw new RuntimeException(e); - } - } - }, true); - } else { - bac.IMP.forEach(new FaweClipboard.BlockReader() { - @Override - public > void run(int x, int y, int z, B block) { - try { - extent.setBlock(x + relx, y + rely, z + relz, block); - } catch (WorldEditException e) { - throw new RuntimeException(e); - } - } - }, pasteAir); - } - } else { - // To must be relative to the clipboard origin ( player location - clipboard origin ) (as the locations supplied are relative to the world origin) - final int relx = to.getBlockX() - origin.getBlockX(); - final int rely = to.getBlockY() - origin.getBlockY(); - final int relz = to.getBlockZ() - origin.getBlockZ(); - Operation visitor = new RegionVisitor(region, new RegionFunction() { - // MutableBlockVector2 mpos2d_2 = new MutableBlockVector2(); - MutableBlockVector2 mpos2d = new MutableBlockVector2(); - - { - mpos2d.setComponents(Integer.MIN_VALUE, Integer.MIN_VALUE); - } - - @Override - public boolean apply(BlockVector3 mutable) throws WorldEditException { - BlockState block = clipboard.getBlock(mutable); - int xx = mutable.getBlockX() + relx; - int zz = mutable.getBlockZ() + relz; - if (copyBiomes && xx != mpos2d.getBlockX() && zz != mpos2d.getBlockZ()) { - mpos2d.setComponents(xx, zz); -// extent.setBiome(mpos2d, clipboard.getBiome(mpos2d_2.setComponents(mutable.getBlockX(), mutable.getBlockZ()))); - extent.setBiome(mpos2d, clipboard - .getBiome(BlockVector2.at(mutable.getBlockX(), mutable.getBlockZ()))); - } - if (!pasteAir && block.getBlockType().getMaterial().isAir()) { - return false; - } - extent.setBlock(xx, mutable.getBlockY() + rely, zz, block); - return false; - } - }); - Operations.completeBlindly(visitor); - } - // Entity offset is the paste location subtract the clipboard origin (entity's location is already relative to the world origin) - final int entityOffsetX = to.getBlockX() - origin.getBlockX(); - final int entityOffsetY = to.getBlockY() - origin.getBlockY(); - final int entityOffsetZ = to.getBlockZ() - origin.getBlockZ(); - // entities - for (Entity entity : clipboard.getEntities()) { - // skip players on pasting schematic - if (entity.getState() != null && entity.getState().getType().getId() - .equals("minecraft:player")) { - continue; - } - Location pos = entity.getLocation(); - Location newPos = new Location(pos.getExtent(), pos.getX() + entityOffsetX, - pos.getY() + entityOffsetY, pos.getZ() + entityOffsetZ, pos.getYaw(), - pos.getPitch()); - extent.createEntity(newPos, entity.getState()); - } - } -} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/visualizer/SchemVis.java b/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/visualizer/SchemVis.java index f8aa4e6d5..368a98397 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/visualizer/SchemVis.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/visualizer/SchemVis.java @@ -1,16 +1,16 @@ //package com.boydti.fawe.object.schematic.visualizer; // +//import com.boydti.fawe.FaweCache; +//import com.boydti.fawe.beta.IBlocks; +//import com.boydti.fawe.beta.IChunk; +//import com.boydti.fawe.beta.IQueueExtent; //import com.boydti.fawe.config.BBC; -//import com.boydti.fawe.jnbt.anvil.MCAChunk; -//import com.boydti.fawe.jnbt.anvil.MCAQueue; //import com.boydti.fawe.object.*; //import com.boydti.fawe.object.brush.visualization.ImmutableVirtualWorld; //import com.boydti.fawe.object.clipboard.LazyClipboardHolder; //import com.boydti.fawe.object.clipboard.MultiClipboardHolder; //import com.boydti.fawe.object.clipboard.URIClipboardHolder; -//import com.boydti.fawe.object.exception.FaweException; //import com.boydti.fawe.object.io.NonCloseableInputStream; -//import com.boydti.fawe.object.queue.LazyFaweChunk; //import com.boydti.fawe.object.schematic.Schematic; //import com.boydti.fawe.util.*; //import com.google.common.io.ByteSource; @@ -32,6 +32,7 @@ //import com.sk89q.worldedit.session.ClipboardHolder; //import com.sk89q.worldedit.util.Location; //import com.sk89q.worldedit.util.TargetBlock; +//import com.sk89q.worldedit.world.World; //import com.sk89q.worldedit.world.biome.BiomeType; //import com.sk89q.worldedit.world.biome.BiomeTypes; //import com.sk89q.worldedit.world.block.BlockState; @@ -55,15 +56,15 @@ // private static final WeakHashMap DIMENSION_CACHE = new WeakHashMap<>(); // // private final Long2ObjectOpenHashMap> files; -// private final Long2ObjectOpenHashMap chunks; // TODO use soft references OR clear chunks outside view distance +// private final Long2ObjectOpenHashMap chunks; // TODO use soft references OR clear chunks outside view distance // // private final MutableBlockVector2 lastPos = new MutableBlockVector2(); -// private final FawePlayer player; +// private final Player player; // private final Location origin; // private final BlockVector2 chunkOffset; // private BlockVector2 lastPosition; // -// public static SchemVis create(FawePlayer player, Collection files) throws IOException { +// public static SchemVis create(Player player, Collection files) throws IOException { // checkNotNull(player); // checkNotNull(files); // SchemVis visExtent = new SchemVis(player); @@ -75,14 +76,14 @@ // return visExtent; // } // -// public SchemVis(FawePlayer player) { +// public SchemVis(Player player) { // this.files = new Long2ObjectOpenHashMap<>(); // this.chunks = new Long2ObjectOpenHashMap<>(); // this.player = player; // // // Set the origin to somewhere around where the player currently is -// Location pos = player.toWorldEditPlayer().getLocation(); -// this.origin = player.getPlayer().getLocation(); +// Location pos = player.getLocation(); +// this.origin = player.getLocation(); // this.chunkOffset = BlockVector2.at(pos.getBlockX() >> 4,pos.getBlockZ() >> 4); // } // @@ -168,12 +169,12 @@ // } // } // // Resend relevant chunks -// IQueueExtent packetQueue = SetQueue.IMP.getNewQueue(this.player.getWorld(), true, false); -// if (packetQueue.supports(Capability.CHUNK_PACKETS)) { -// ArrayDeque toSend = new ArrayDeque<>(); -// ObjectIterator> iter = chunks.long2ObjectEntrySet().fastIterator(); +// World world = this.player.getWorld(); +// +// ArrayDeque toSend = new ArrayDeque<>(); +// ObjectIterator> iter = chunks.long2ObjectEntrySet().fastIterator(); // while (iter.hasNext()) { -// Long2ObjectMap.Entry mcaChunkEntry = iter.next(); +// Long2ObjectMap.Entry mcaChunkEntry = iter.next(); // long curChunkPos = mcaChunkEntry.getLongKey(); // Map.Entry curFileEntry = files.get(curChunkPos); // if (curFileEntry != null) { @@ -189,7 +190,6 @@ // } // } // for (long curChunkPos : toSend) send(packetQueue, MathMan.unpairIntX(curChunkPos), MathMan.unpairIntY(curChunkPos)); -// } // } catch (IOException e) { // throw new RuntimeException(e); // } @@ -263,9 +263,11 @@ // * Replace the blocks with glass, to indicate it's been selected // * @param chunk // */ -// private void select(MCAChunk chunk) { +// private void select(IBlocks chunk) { // for (int layer = 0; layer < 16; layer++) { -// int[] ids = chunk.ids[layer]; +// if (!chunk.hasSection(layer)) continue; +// +// char[] ids = chunk.ids[layer]; // if (ids != null) { // for (int i = 0; i < ids.length; i++) { // // TODO FIXME update to 1.13 @@ -279,14 +281,7 @@ // * Cache a chunk // * @param chunk // */ -// private void cacheChunk(MCAChunk chunk, boolean selected) { -// long pair = MathMan.pairInt(chunk.getX(), chunk.getZ()); -// // Light chunk -// for (int layer = 0; layer < 16; layer++) { -// if (chunk.skyLight[layer] != null) { -// Arrays.fill(chunk.skyLight[layer], (byte) 255); -// } -// } +// private void cacheChunk(IChunk chunk, boolean selected) { // if (selected) { // select(chunk); // } @@ -366,7 +361,7 @@ // } // } else { // try { -// player.sendMessage(BBC.getPrefix() + "Converting: " + file); +// player.print("Converting: " + file); // cached.createNewFile(); // try (FileInputStream in = new FileInputStream(file)) { // ClipboardFormat format = ClipboardFormats.findByFile(file); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/string/JoinedCharSequence.java b/worldedit-core/src/main/java/com/boydti/fawe/object/string/JoinedCharSequence.java new file mode 100644 index 000000000..5b134a468 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/string/JoinedCharSequence.java @@ -0,0 +1,70 @@ +package com.boydti.fawe.object.string; + +public class JoinedCharSequence implements CharSequence { + private char join; + private int len2; + private int len1; + private int length; + private String a; + private String b; + + public JoinedCharSequence init(String a, char join, String b) { + this.len1 = a.length(); + this.len2 = b.length(); + this.length = len1 + len2 + 1; + this.join = join; + this.a = a; + this.b = b; + return this; + } + @Override + public int length() { + return length; + } + + @Override + public char charAt(int index) { + if (index < len1) { + return a.charAt(index); + } + if (index == len1) { + return join; + } + return b.charAt(index - len1 - 1); + } + + @Override + public CharSequence subSequence(int start, int end) { + char[] chars = new char[end - start]; + for (int i = start, j = 0; i < end; i++, j++) { + chars[j] = charAt(i); + } + return new String(chars); + } + + @Override + public int hashCode() { + int h = 0; + for (int i = 0; i < length; i++) { + h = 31 * h + charAt(i); + } + return h; + } + + @Override + public String toString() { + return (String) subSequence(0, length); + } + + @Override + public boolean equals(Object obj) { + CharSequence anotherString = (CharSequence) obj; + if (length == anotherString.length()) { + for (int i = length - 1; i >= 0; i--) { + if (charAt(i) != anotherString.charAt(i)) return false; + } + return true; + } + return false; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweChunkManager.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweChunkManager.java index edb5c1b5b..f5e8bda51 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweChunkManager.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweChunkManager.java @@ -93,7 +93,7 @@ public class FaweChunkManager extends ChunkManager { EditSession editSession = new EditSessionBuilder(pos1.getWorld()).checkMemory(false).fastmode(true).limitUnlimited().changeSetNull().autoQueue(false).build(); World world = editSession.getWorld(); CuboidRegion region = new CuboidRegion(BlockVector3.at(pos1.getX(), pos1.getY(), pos1.getZ()), BlockVector3.at(pos2.getX(), pos2.getY(), pos2.getZ())); - world.regenerate(region, editSession); + editSession.regenerate(region); editSession.flushQueue(); TaskManager.IMP.task(whenDone); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweLocalBlockQueue.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweLocalBlockQueue.java index d73720c03..4dbe22115 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweLocalBlockQueue.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweLocalBlockQueue.java @@ -122,7 +122,7 @@ public class FaweLocalBlockQueue extends LocalBlockQueue { @Override public void refreshChunk(int x, int z) { - world.sendChunk(x, z, 0); + world.refreshChunk(x, z); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/PlotSquaredFeature.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/PlotSquaredFeature.java index 547ab1e0e..de8056506 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/PlotSquaredFeature.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/PlotSquaredFeature.java @@ -38,14 +38,16 @@ public class PlotSquaredFeature extends FaweMaskManager { public PlotSquaredFeature() { super("PlotSquared"); Fawe.debug("Optimizing PlotSquared"); - setupBlockQueue(); - setupSchematicHandler(); - setupChunkManager(); - if (Settings.PLATFORM.equalsIgnoreCase("bukkit")) { - new FaweTrim(); + if (com.boydti.fawe.config.Settings.IMP.PLOTSQUARED_HOOK) { + setupBlockQueue(); + setupSchematicHandler(); + setupChunkManager(); + if (Settings.PLATFORM.equalsIgnoreCase("bukkit")) { + new FaweTrim(); + } + if (MainCommand.getInstance().getCommand("generatebiome") == null) { + new PlotSetBiome(); } - if (MainCommand.getInstance().getCommand("generatebiome") == null) { - new PlotSetBiome(); } // TODO: revisit this later on /* diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java b/worldedit-core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java index 7a37a9a09..2fca05f4e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java @@ -51,7 +51,7 @@ public class DelegateTextureUtil extends TextureUtil { } @Override - public boolean getIsBlockCloserThanBiome(int[] blockAndBiomeIdOutput, int color, int biomePriority) { + public boolean getIsBlockCloserThanBiome(char[] blockAndBiomeIdOutput, int color, int biomePriority) { return parent.getIsBlockCloserThanBiome(blockAndBiomeIdOutput, color, biomePriority); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java b/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java index 497b0fa25..790e2b36a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java @@ -5,8 +5,10 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweAPI; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.IQueueExtent; -import com.boydti.fawe.beta.implementation.ParallelQueueExtent; +import com.boydti.fawe.beta.implementation.processors.LimitProcessor; +import com.boydti.fawe.beta.implementation.queue.ParallelQueueExtent; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.logging.LoggingChangeSet; @@ -420,10 +422,14 @@ public class EditSessionBuilder { } else { // this.extent = new HeightBoundExtent(this.extent, this.limit, 0, maxY); } + IBatchProcessor limitProcessor = regionExtent; + if (limit != null && !limit.isUnlimited()) { + limitProcessor = new LimitProcessor(limit, limitProcessor); + } if (regionExtent != null && queue != null && combineStages) { - queue.addProcessor(regionExtent); + queue.addProcessor(limitProcessor); } else if (regionExtent != null) { - this.extent = regionExtent; + this.extent = limitProcessor.construct(regionExtent.getExtent()); } if (this.limit.STRIP_NBT != null && !this.limit.STRIP_NBT.isEmpty()) { System.out.println("TODO add batch processor for strip nbt"); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/Jars.java b/worldedit-core/src/main/java/com/boydti/fawe/util/Jars.java index 41d165e25..e991ee747 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/Jars.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/Jars.java @@ -10,11 +10,11 @@ import java.util.Base64; public enum Jars { - MM_v1_4_0("https://github.com/InventivetalentDev/MapManager/releases/download/1.4.0-SNAPSHOT/MapManager_v1.4.0-SNAPSHOT.jar", - "AEO5SKBUGN4YJRS8XGGNLBM2QRZPTI1KF0/1W1URTGA=", 163279), + MM_v1_7_3("https://github.com/InventivetalentDev/MapManager/releases/download/1.7.3-SNAPSHOT/MapManager_v1.7.3-SNAPSHOT.jar", + "M3YLUQZZ66K2DMVDCYLEU38U3ZKRKHRAXQGGPVKFO6G=", 554831), - PL_v3_6_0("https://github.com/InventivetalentDev/PacketListenerAPI/releases/download/3.6.0-SNAPSHOT/PacketListenerAPI_v3.6.0-SNAPSHOT.jar", - "OYBE75VIU+NNWHRVREBLDARWA+/TBDQZ1RC562QULBA=", 166508), + PL_v3_7_3("https://github.com/InventivetalentDev/PacketListenerAPI/releases/download/3.7.3-SNAPSHOT/PacketListenerAPI_v3.7.3-SNAPSHOT.jar", + "ETDBRZLN5PRVDFR/MSQDPM6JJER3WQOKHCN8FUXO5ZM=", 167205), ; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/RandomTextureUtil.java b/worldedit-core/src/main/java/com/boydti/fawe/util/RandomTextureUtil.java index fbcc60bab..a210a1544 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/RandomTextureUtil.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/RandomTextureUtil.java @@ -41,7 +41,7 @@ public class RandomTextureUtil extends CachedTextureUtil { } @Override - public boolean getIsBlockCloserThanBiome(int[] blockAndBiomeIdOutput, int color, int biomePriority) { + public boolean getIsBlockCloserThanBiome(char[] blockAndBiomeIdOutput, int color, int biomePriority) { BlockType block = getNearestBlock(color); int[] mix = biomeMixes.getOrDefault(color, null); if (mix == null) { @@ -55,8 +55,8 @@ public class RandomTextureUtil extends CachedTextureUtil { int biomeId = mix[index]; int biomeAvColor = mix[3]; int blockColor = getColor(block); - blockAndBiomeIdOutput[0] = block.getInternalId(); - blockAndBiomeIdOutput[1] = biomeId; + blockAndBiomeIdOutput[0] = block.getDefaultState().getOrdinalChar(); + blockAndBiomeIdOutput[1] = (char) biomeId; if (colorDistance(biomeAvColor, color) - biomePriority > colorDistance(blockColor, color)) { return true; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/StringMan.java b/worldedit-core/src/main/java/com/boydti/fawe/util/StringMan.java index 1cf6e56ba..cf34d9b85 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/StringMan.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/StringMan.java @@ -59,8 +59,8 @@ public class StringMan { } public static String prettyFormat(double d) { - if (d == Double.MIN_VALUE) return "-∞"; - if (d == Double.MAX_VALUE) return "∞"; + if (d == Double.MIN_VALUE || d == Double.NEGATIVE_INFINITY) return "-∞"; + if (d == Double.MAX_VALUE || d == Double.POSITIVE_INFINITY) return "∞"; if(d == (long) d) return String.format("%d",(long)d); else return String.format("%s",d); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/TaskManager.java b/worldedit-core/src/main/java/com/boydti/fawe/util/TaskManager.java index 56fd8b979..3e3a78fc8 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/TaskManager.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/TaskManager.java @@ -1,9 +1,8 @@ package com.boydti.fawe.util; import com.boydti.fawe.Fawe; -import com.boydti.fawe.beta.implementation.QueueHandler; +import com.boydti.fawe.beta.implementation.queue.QueueHandler; import com.boydti.fawe.config.Settings; -import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.object.RunnableVal; import org.jetbrains.annotations.NotNull; @@ -13,7 +12,6 @@ import java.util.Collection; import java.util.Iterator; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/TextureUtil.java b/worldedit-core/src/main/java/com/boydti/fawe/util/TextureUtil.java index ef3fa7a7a..bb9fdf171 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/TextureUtil.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/TextureUtil.java @@ -1,7 +1,7 @@ package com.boydti.fawe.util; import com.boydti.fawe.Fawe; -import com.boydti.fawe.beta.SingleFilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.SingleFilterBlock; import com.boydti.fawe.config.Settings; import com.boydti.fawe.util.image.ImageUtil; import com.google.gson.Gson; @@ -12,6 +12,7 @@ import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.registry.BlockMaterial; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -497,13 +498,13 @@ public class TextureUtil implements TextureHolder { return biomes[biome]; } - public boolean getIsBlockCloserThanBiome(int[] blockAndBiomeIdOutput, int color, + public boolean getIsBlockCloserThanBiome(char[] blockAndBiomeIdOutput, int color, int biomePriority) { BlockType block = getNearestBlock(color); TextureUtil.BiomeColor biome = getNearestBiome(color); int blockColor = getColor(block); blockAndBiomeIdOutput[0] = block.getDefaultState().getOrdinalChar(); - blockAndBiomeIdOutput[1] = biome.id; + blockAndBiomeIdOutput[1] = (char) biome.id; if (colorDistance(biome.grassCombined, color) - biomePriority > colorDistance(blockColor, color)) { return true; @@ -593,7 +594,7 @@ public class TextureUtil implements TextureHolder { if (folder.exists()) { // Get all the jar files File[] files = folder.listFiles((dir, name) -> name.endsWith(".jar")); - for (BlockType blockType : BlockTypes.values) { + for (BlockType blockType : BlockTypesCache.values) { BlockMaterial material = blockType.getMaterial(); if (!material.isSolid() || !material.isFullCube()) { continue; @@ -635,7 +636,7 @@ public class TextureUtil implements TextureHolder { Type typeToken = new TypeToken>() { }.getType(); - for (BlockType blockType : BlockTypes.values) { + for (BlockType blockType : BlockTypesCache.values) { if (!blockType.getMaterial().isFullCube()) { continue; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/task/TaskBuilder.java b/worldedit-core/src/main/java/com/boydti/fawe/util/task/TaskBuilder.java index 04d4b86be..9855bf49a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/task/TaskBuilder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/task/TaskBuilder.java @@ -2,7 +2,7 @@ package com.boydti.fawe.util.task; import com.boydti.fawe.Fawe; import com.boydti.fawe.beta.IQueueExtent; -import com.boydti.fawe.beta.implementation.QueueHandler; +import com.boydti.fawe.beta.implementation.queue.QueueHandler; import com.boydti.fawe.object.Metadatable; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.util.TaskManager; 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 4834c446a..4ebfde40a 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 @@ -1,6 +1,7 @@ package com.boydti.fawe.wrappers; import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.util.TaskManager; @@ -13,6 +14,7 @@ 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.entity.Player; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; @@ -235,8 +237,8 @@ public class WorldWrapper extends AbstractWorld { } @Override - public void sendChunk(int chunkX, int chunkZ, int bitMask) { - parent.sendChunk(chunkX, chunkZ, bitMask); + public void refreshChunk(int X, int Z) { + parent.refreshChunk(X, Z); } @Override @@ -304,4 +306,9 @@ public class WorldWrapper extends AbstractWorld { public IChunkGet get(int x, int z) { return parent.get(x, z); } + + @Override + public void sendFakeChunk(@Nullable Player player, ChunkPacket packet) { + parent.sendFakeChunk(player, packet); + } } diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTInputStream.java b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTInputStream.java index 10c79cda0..e74ca6c7d 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTInputStream.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTInputStream.java @@ -19,18 +19,18 @@ package com.sk89q.jnbt; -import com.boydti.fawe.jnbt.NBTStreamer; +import com.boydti.fawe.jnbt.streamer.StreamDelegate; +import com.boydti.fawe.jnbt.streamer.ValueReader; import java.io.Closeable; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.Function; /** * This class reads NBT, or Named Binary Tag @@ -86,19 +86,23 @@ public final class NBTInputStream implements Closeable { return readTagPayload(type, 0); } - public void readNamedTagLazy(Function getReader) throws IOException { - int type = is.readByte(); - String name = readNamedTagName(type); - BiConsumer reader = getReader.apply(name); - if (reader != null) { - reader.accept(0, readTagPaylodRaw(type, 0)); - return; + public void readNamedTagLazy(StreamDelegate scope) throws IOException { + try { + int type = is.readByte(); + if (type == NBTConstants.TYPE_END) return; + + StreamDelegate child = scope.get(is); + if (child != null) { + child.acceptRoot(this, type, 0); + } else { + readTagPaylodLazy(type, 0); + } + } catch (Throwable e) { + e.printStackTrace(); } - readTagPaylodLazy(type, 0, name, getReader); } public String readNamedTagName(int type) throws IOException { - String name; if (type != NBTConstants.TYPE_END) { int nameLength = is.readShort() & 0xFFFF; byte[] nameBytes = new byte[nameLength]; @@ -111,7 +115,7 @@ public final class NBTInputStream implements Closeable { private byte[] buf; - public void readTagPaylodLazy(int type, int depth, String node, Function getReader) throws IOException { + public void readTagPaylodLazy(int type, int depth) throws IOException { switch (type) { case NBTConstants.TYPE_END: return; @@ -134,22 +138,199 @@ public final class NBTInputStream implements Closeable { is.skipBytes(8); return; case NBTConstants.TYPE_STRING: - int length = is.readShort(); + int length = is.readShort() & 0xFFFF; is.skipBytes(length); return; case NBTConstants.TYPE_BYTE_ARRAY: - BiConsumer reader = getReader.apply(node + ".?"); + is.skipBytes(is.readInt()); + return; + case NBTConstants.TYPE_LIST: { + int childType = is.readByte(); length = is.readInt(); - if (reader != null) { - reader.accept(length, NBTConstants.TYPE_BYTE); + for (int i = 0; i < length; ++i) { + readTagPaylodLazy(childType, depth + 1); } - reader = getReader.apply(node + ".#"); - if (reader == null) { + return; + } + case NBTConstants.TYPE_COMPOUND: { + // readDataPayload + depth++; + while(true) { + int childType = is.readByte(); + if (childType == NBTConstants.TYPE_END) { + return; + } + is.skipBytes(is.readShort() & 0xFFFF); + readTagPaylodLazy(childType, depth + 1); + } + } + case NBTConstants.TYPE_INT_ARRAY: { + is.skipBytes(is.readInt() << 2); + return; + } + case NBTConstants.TYPE_LONG_ARRAY: { + is.skipBytes(is.readInt() << 3); + return; + } + default: + throw new IOException("Invalid tag type: " + type + "."); + } + } + + public void readTagPaylodLazy(int type, int depth, StreamDelegate scope) throws IOException { + switch (type) { + case NBTConstants.TYPE_END: + return; + case NBTConstants.TYPE_BYTE: { + ValueReader value = scope.getValueReader(); + if (value == null) value = scope.getElemReader(); + if (value != null) { + value.applyInt(0, is.readByte()); + } else { + is.skipBytes(1); + } + return; + } + case NBTConstants.TYPE_SHORT: { + ValueReader value = scope.getValueReader(); + if (value == null) value = scope.getElemReader(); + if (value != null) { + value.applyInt(0, is.readShort()); + } else { + is.skipBytes(2); + } + return; + } + case NBTConstants.TYPE_INT: { + ValueReader value = scope.getValueReader(); + if (value == null) value = scope.getElemReader(); + if (value != null) { + value.applyInt(0, is.readInt()); + } else { + is.skipBytes(4); + } + return; + } + case NBTConstants.TYPE_LONG: { + ValueReader value = scope.getValueReader(); + if (value == null) value = scope.getElemReader(); + if (value != null) { + value.applyLong(0, is.readLong()); + } else { + is.skipBytes(8); + } + return; + } + case NBTConstants.TYPE_FLOAT: { + ValueReader value = scope.getValueReader(); + if (value == null) value = scope.getElemReader(); + if (value != null) { + value.applyFloat(0, is.readFloat()); + } else { + is.skipBytes(4); + } + return; + } + case NBTConstants.TYPE_DOUBLE: { + ValueReader value = scope.getValueReader(); + if (value == null) value = scope.getElemReader(); + if (value != null) { + value.applyDouble(0, is.readDouble()); + } else { + is.skipBytes(8); + } + return; + } + case NBTConstants.TYPE_STRING: { + ValueReader value = scope.getValueReader(); + if (value == null) value = scope.getElemReader(); + int length = is.readShort() & 0xFFFF; + if (value != null) { + byte[] bytes = new byte[length]; + is.readFully(bytes); + value.apply(0, new String(bytes, NBTConstants.CHARSET)); + } else { is.skipBytes(length); + } + return; + } + case NBTConstants.TYPE_LIST: { + int childType = is.readByte(); + int length = is.readInt(); + StreamDelegate child; + scope.acceptInfo(length, childType); + ValueReader valueReader = scope.getValueReader(); + if (valueReader != null) { + List tagList = readListRaw(depth, childType, length); + valueReader.apply(0, tagList); return; } - if (reader instanceof NBTStreamer.ByteReader) { - NBTStreamer.ByteReader byteReader = (NBTStreamer.ByteReader) reader; + valueReader = scope.getElemReader(); + if (valueReader != null) { + for (int i = 0; i < length; ++i) { + valueReader.apply(i, readTagPayloadRaw(childType, depth + 1)); + } + return; + } + child = scope.get0(); + if (child == null) { + for (int i = 0; i < length; ++i) { + readTagPaylodLazy(childType, depth + 1); + } + } else { + for (int i = 0; i < length; ++i) { + readTagPaylodLazy(childType, depth + 1, child); + } + } + return; + } + case NBTConstants.TYPE_COMPOUND: { + // readDataPayload + scope.acceptInfo(-1, NBTConstants.TYPE_BYTE); + ValueReader valueReader = scope.getValueReader(); + if (valueReader != null) { + valueReader.apply(0, this.readTagPayloadRaw(type, depth)); + return; + } + valueReader = scope.getElemReader(); + if (valueReader != null) { + for (int i = 0; ; i++) { + int childType = is.readByte(); + if (childType == NBTConstants.TYPE_END) { + return; + } + String key = readNamedTagName(childType); + Object value = readTagPayloadRaw(childType, depth + 1); + AbstractMap.SimpleEntry entry = new AbstractMap.SimpleEntry<>(key, value); + valueReader.apply(i, entry); + } + } + while(true) { + int childType = is.readByte(); + if (childType == NBTConstants.TYPE_END) { + return; + } + StreamDelegate child = scope.get(is); + if (child == null) { + readTagPaylodLazy(childType, depth + 1); + } else { + readTagPaylodLazy(childType, depth + 1, child); + } + } + } + case NBTConstants.TYPE_BYTE_ARRAY: { + int length = is.readInt(); + scope.acceptInfo(length, NBTConstants.TYPE_BYTE); + if (scope.acceptLazy(length, this)) return; + ValueReader valueReader = scope.getValueReader(); + if (valueReader != null) { + byte[] arr = new byte[length]; + is.readFully(arr); + valueReader.apply(0, arr); + return; + } + valueReader = scope.getElemReader(); + if (valueReader != null) { int i = 0; DataInputStream dis = is; if (length > 1024) { @@ -160,109 +341,54 @@ public final class NBTInputStream implements Closeable { for (; left > 1024; left -= 1024) { dis.readFully(buf); for (byte b : buf) { - byteReader.run(i++, b & 0xFF); + valueReader.applyInt(i++, b & 0xFF); } } } for (; i < length; i++) { - byteReader.run(i, dis.read()); - } - } else if (reader instanceof NBTStreamer.LazyReader) { - reader.accept(length, is); - } else { - for (int i = 0; i < length; i++) { - reader.accept(i, is.readByte()); - } - } - return; - case NBTConstants.TYPE_LIST: - int childType = is.readByte(); - if (childType == NBTConstants.TYPE_LIST) { - childType = NBTConstants.TYPE_COMPOUND; - } - length = is.readInt(); - reader = getReader.apply(node + ".?"); - if (reader != null) { - reader.accept(length, childType); - } - node += ".#"; - reader = getReader.apply(node); - depth++; - if (reader == null) { - for (int i = 0; i < length; ++i) { - readTagPaylodLazy(childType, depth, node, getReader); + valueReader.applyInt(i, dis.read()); } return; } - for (int i = 0; i < length; ++i) { - reader.accept(i, readTagPayload(childType, depth)); - } + is.skipBytes(length); return; - case NBTConstants.TYPE_COMPOUND: - depth++; - // 3 - for (int i = 0; ; i++) { - childType = is.readByte(); - if (childType == NBTConstants.TYPE_END) { - return; - } - String name = readNamedTagName(childType); - String childNode = node + "." + name; - reader = getReader.apply(childNode); - if (reader == null) { - readTagPaylodLazy(childType, depth, childNode, getReader); - continue; - } - reader.accept(i, readTagPaylodRaw(childType, depth)); - } + } case NBTConstants.TYPE_INT_ARRAY: { - length = is.readInt(); - reader = getReader.apply(node + ".?"); - if (reader != null) { - reader.accept(length, NBTConstants.TYPE_INT); - } - reader = getReader.apply(node + ".#"); - if (reader == null) { - is.skipBytes(length << 2); + int length = is.readInt(); + scope.acceptInfo(length, NBTConstants.TYPE_INT); + if (scope.acceptLazy(length, this)) return; + ValueReader valueReader = scope.getValueReader(); + if (valueReader != null) { + valueReader.apply(0, readIntArrayRaw(length)); return; } - if (reader instanceof NBTStreamer.ByteReader) { - NBTStreamer.ByteReader byteReader = (NBTStreamer.ByteReader) reader; + valueReader = scope.getElemReader(); + if (valueReader != null) { for (int i = 0; i < length; i++) { - byteReader.run(i, is.readInt()); - } - } else if (reader instanceof NBTStreamer.LazyReader) { - reader.accept(length, is); - } else { - for (int i = 0; i < length; i++) { - reader.accept(i, is.readInt()); + valueReader.applyInt(i, is.readInt()); } + return; } + is.skipBytes(length << 2); return; } case NBTConstants.TYPE_LONG_ARRAY: { - length = is.readInt(); - reader = getReader.apply(node + ".?"); - if (reader != null) { - reader.accept(length, NBTConstants.TYPE_LONG); - } - reader = getReader.apply(node + ".#"); - if (reader == null) { - is.skipBytes(length << 3); + int length = is.readInt(); + scope.acceptInfo(length, NBTConstants.TYPE_LONG); + if (scope.acceptLazy(length, this)) return; + ValueReader valueReader = scope.getValueReader(); + if (valueReader != null) { + valueReader.apply(0, readLongArrayRaw(length)); return; } - if (reader instanceof NBTStreamer.LongReader) { - NBTStreamer.LongReader longReader = (NBTStreamer.LongReader) reader; + valueReader = scope.getElemReader(); + if (valueReader != null) { for (int i = 0; i < length; i++) { - longReader.run(i, is.readLong()); - } - } else if (reader instanceof NBTStreamer.LazyReader) { - reader.accept(length, is); - } else { - for (int i = 0; i < length; i++) { - reader.accept(i, is.readLong()); + valueReader.applyLong(i, is.readLong()); } + return; } + is.skipBytes(length << 3); return; } @@ -271,6 +397,18 @@ public final class NBTInputStream implements Closeable { } } + private List readListRaw(int depth, int childType, int length) throws IOException { + List tagList = new ArrayList<>(length); + for (int i = 0; i < length; ++i) { + Object tag = readTagPayloadRaw(childType, depth + 1); + if (tag == null) { + throw new IOException("TAG_End not permitted in a list."); + } + tagList.add(tag); + } + return tagList; + } + public static int getSize(int type) { switch (type) { default: @@ -294,7 +432,7 @@ public final class NBTInputStream implements Closeable { } } - private Object readTagPaylodRaw(int type, int depth) throws IOException { + public Object readTagPayloadRaw(int type, int depth) throws IOException { switch (type) { case NBTConstants.TYPE_END: if (depth == 0) { @@ -325,72 +463,70 @@ public final class NBTInputStream implements Closeable { bytes = new byte[length]; is.readFully(bytes); return (new String(bytes, NBTConstants.CHARSET)); - case NBTConstants.TYPE_LIST: + case NBTConstants.TYPE_LIST: { int childType = is.readByte(); - if (childType == NBTConstants.TYPE_LIST) { - childType = NBTConstants.TYPE_COMPOUND; - } length = is.readInt(); - List tagList = new ArrayList<>(); - for (int i = 0; i < length; ++i) { - Tag tag = readTagPayload(childType, depth + 1); - if (tag instanceof EndTag) { - throw new IOException("TAG_End not permitted in a list."); - } - tagList.add(tag); - } - return (tagList); - case NBTConstants.TYPE_COMPOUND: - Map tagMap = new HashMap<>(); + return readListRaw(depth, childType, length); + } + case NBTConstants.TYPE_COMPOUND: { + Map tagMap = new HashMap<>(); while (true) { - NamedTag namedTag = readNamedTag(depth + 1); - Tag tag = namedTag.getTag(); - if (tag instanceof EndTag) { - break; - } else { - tagMap.put(namedTag.getName(), tag); + int childType = is.readByte(); + if (childType == NBTConstants.TYPE_END) { + return tagMap; } + String name = readNamedTagName(childType); + Object value = readTagPayloadRaw(childType, depth + 1); + tagMap.put(name, value); } - return (tagMap); + } case NBTConstants.TYPE_INT_ARRAY: { length = is.readInt(); - int[] data = new int[length]; - if (buf == null) { - buf = new byte[1024]; - } - int index = 0; - while (length > 0) { - int toRead = Math.min(length << 2, buf.length); - is.readFully(buf, 0, toRead); - for (int i = 0; i < toRead; i += 4, index++) { - data[index] = ((buf[i] & 0xFF) << 24) + ((buf[i + 1] & 0xFF) << 16) + ((buf[i + 2] & 0xFF) << 8) + (buf[i + 3] & 0xFF); - } - length -= toRead; - } - return (data); + return readIntArrayRaw(length); } case NBTConstants.TYPE_LONG_ARRAY: { length = is.readInt(); - long[] data = new long[length]; - if (buf == null) { - buf = new byte[1024]; - } - int index = 0; - while (length > 0) { - int toRead = Math.min(length << 3, buf.length); - is.readFully(buf, 0, toRead); - for (int i = 0; i < toRead; i += 8, index++) { - data[index] = (((long) buf[i] << 56) | ((long) (buf[i + 1] & 255) << 48) | ((long) (buf[i + 2] & 255) << 40) | ((long) (buf[i + 3] & 255) << 32) | ((long) (buf[i + 4] & 255) << 24) | ((buf[i + 5] & 255) << 16) | ((buf[i + 6] & 255) << 8) | (buf[i + 7] & 255)); - } - length -= toRead; - } - return (data); + return readLongArrayRaw(length); } default: throw new IOException("Invalid tag type: " + type + "."); } } + private int[] readIntArrayRaw(int length) throws IOException { + int[] data = new int[length]; + if (buf == null) { + buf = new byte[1024]; + } + int index = 0; + while (length > 0) { + int toRead = Math.min(length << 2, buf.length); + is.readFully(buf, 0, toRead); + for (int i = 0; i < toRead; i += 4, index++) { + data[index] = ((buf[i] & 0xFF) << 24) + ((buf[i + 1] & 0xFF) << 16) + ((buf[i + 2] & 0xFF) << 8) + (buf[i + 3] & 0xFF); + } + length -= toRead; + } + return data; + } + + private long[] readLongArrayRaw(int length) throws IOException { + long[] data = new long[length]; + if (buf == null) { + buf = new byte[1024]; + } + int index = 0; + while (length > 0) { + int toRead = Math.min(length << 3, buf.length); + is.readFully(buf, 0, toRead); + for (int i = 0; i < toRead; i += 8, index++) { + data[index] = (((long) buf[i] << 56) | ((long) (buf[i + 1] & 255) << 48) | ((long) (buf[i + 2] & 255) << 40) | ((long) (buf[i + 3] & 255) << 32) | ((long) (buf[i + 4] & 255) << 24) | ((buf[i + 5] & 255) << 16) | ((buf[i + 6] & 255) << 8) | (buf[i + 7] & 255)); + } + length -= toRead; + } + return (data); + } + /** * Reads the payload of a tag given the type. * @@ -432,9 +568,6 @@ public final class NBTInputStream implements Closeable { return new StringTag(new String(bytes, NBTConstants.CHARSET)); case NBTConstants.TYPE_LIST: int childType = is.readByte(); - if (childType == NBTConstants.TYPE_LIST) { - childType = NBTConstants.TYPE_COMPOUND; - } length = is.readInt(); List tagList = new ArrayList<>(); for (int i = 0; i < length; ++i) { @@ -457,7 +590,6 @@ public final class NBTInputStream implements Closeable { tagMap.put(namedTag.getName(), tag); } } - return new CompoundTag(tagMap); case NBTConstants.TYPE_INT_ARRAY: length = is.readInt(); @@ -478,91 +610,6 @@ public final class NBTInputStream implements Closeable { } } - /* - Don't delete please - */ - public Object readDataPayload(int type, int depth) throws IOException { - switch (type) { - case NBTConstants.TYPE_END: - if (depth == 0) { - throw new IOException( - "TAG_End found without a TAG_Compound/TAG_List tag preceding it."); - } else { - return null; - } - case NBTConstants.TYPE_BYTE: - return is.readByte(); - case NBTConstants.TYPE_SHORT: - return is.readShort(); - case NBTConstants.TYPE_INT: - return is.readInt(); - case NBTConstants.TYPE_LONG: - return is.readLong(); - case NBTConstants.TYPE_FLOAT: - return is.readFloat(); - case NBTConstants.TYPE_DOUBLE: - return is.readDouble(); - case NBTConstants.TYPE_BYTE_ARRAY: - int length = is.readInt(); - byte[] bytes = new byte[length]; - is.readFully(bytes); - return bytes; - case NBTConstants.TYPE_STRING: - length = is.readShort(); - bytes = new byte[length]; - is.readFully(bytes); - return new String(bytes, NBTConstants.CHARSET); - case NBTConstants.TYPE_LIST: - int childType = is.readByte(); - if (childType == NBTConstants.TYPE_LIST) { - childType = NBTConstants.TYPE_COMPOUND; - } - length = is.readInt(); - ArrayList list = new ArrayList<>(); - for (int i = 0; i < length; ++i) { - Object obj = readDataPayload(childType, depth + 1); - if (obj == null) { - throw new IOException("TAG_End not permitted in a list."); - } - list.add(obj); - } - - return list; - case NBTConstants.TYPE_COMPOUND: - Map map = new HashMap<>(); - while (true) { - int newType = is.readByte(); - String name = readNamedTagName(newType); - Object data = readDataPayload(newType, depth + 1); - if (data == null) { - break; - } else { - map.put(name, data); - } - } - - return map; - case NBTConstants.TYPE_INT_ARRAY: { - length = is.readInt(); - int[] data = new int[length]; - for (int i = 0; i < length; i++) { - data[i] = is.readInt(); - } - return data; - } - case NBTConstants.TYPE_LONG_ARRAY: { - length = is.readInt(); - long[] data = new long[length]; - for (int i = 0; i < length; i++) { - data[i] = is.readLong(); - } - return data; - } - default: - throw new IOException("Invalid tag type: " + type + "."); - } - } - @Override public void close() throws IOException { is.close(); diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java index 6aa559a9e..107cbaae6 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java @@ -167,11 +167,12 @@ public final class NBTOutputStream extends OutputStream implements Closeable, Da } public void writeNamedTagName(String name, int type) throws IOException { - byte[] nameBytes = name.getBytes(NBTConstants.CHARSET); +// byte[] nameBytes = name.getBytes(NBTConstants.CHARSET); os.writeByte(type); - os.writeShort(nameBytes.length); - os.write(nameBytes); + os.writeUTF(name); +// os.writeShort(nameBytes.length); +// os.write(nameBytes); } public void writeLazyCompoundTag(String name, LazyWrite next) throws IOException { @@ -208,47 +209,47 @@ public final class NBTOutputStream extends OutputStream implements Closeable, Da public void writeTagPayload(Tag tag) throws IOException { int type = NBTUtils.getTypeCode(tag.getClass()); switch (type) { - case NBTConstants.TYPE_END: - writeEndTagPayload((EndTag) tag); - break; - case NBTConstants.TYPE_BYTE: - writeByteTagPayload((ByteTag) tag); - break; - case NBTConstants.TYPE_SHORT: - writeShortTagPayload((ShortTag) tag); - break; - case NBTConstants.TYPE_INT: - writeIntTagPayload((IntTag) tag); - break; - case NBTConstants.TYPE_LONG: - writeLongTagPayload((LongTag) tag); - break; - case NBTConstants.TYPE_FLOAT: - writeFloatTagPayload((FloatTag) tag); - break; - case NBTConstants.TYPE_DOUBLE: - writeDoubleTagPayload((DoubleTag) tag); - break; - case NBTConstants.TYPE_BYTE_ARRAY: - writeByteArrayTagPayload((ByteArrayTag) tag); - break; - case NBTConstants.TYPE_STRING: - writeStringTagPayload((StringTag) tag); - break; - case NBTConstants.TYPE_LIST: - writeListTagPayload((ListTag) tag); - break; - case NBTConstants.TYPE_COMPOUND: - writeCompoundTagPayload((CompoundTag) tag); - break; - case NBTConstants.TYPE_INT_ARRAY: - writeIntArrayTagPayload((IntArrayTag) tag); - break; - case NBTConstants.TYPE_LONG_ARRAY: - writeLongArrayTagPayload((LongArrayTag) tag); - break; - default: - throw new IOException("Invalid tag type: " + type + "."); + case NBTConstants.TYPE_END: + writeEndTagPayload((EndTag) tag); + break; + case NBTConstants.TYPE_BYTE: + writeByteTagPayload((ByteTag) tag); + break; + case NBTConstants.TYPE_SHORT: + writeShortTagPayload((ShortTag) tag); + break; + case NBTConstants.TYPE_INT: + writeIntTagPayload((IntTag) tag); + break; + case NBTConstants.TYPE_LONG: + writeLongTagPayload((LongTag) tag); + break; + case NBTConstants.TYPE_FLOAT: + writeFloatTagPayload((FloatTag) tag); + break; + case NBTConstants.TYPE_DOUBLE: + writeDoubleTagPayload((DoubleTag) tag); + break; + case NBTConstants.TYPE_BYTE_ARRAY: + writeByteArrayTagPayload((ByteArrayTag) tag); + break; + case NBTConstants.TYPE_STRING: + writeStringTagPayload((StringTag) tag); + break; + case NBTConstants.TYPE_LIST: + writeListTagPayload((ListTag) tag); + break; + case NBTConstants.TYPE_COMPOUND: + writeCompoundTagPayload((CompoundTag) tag); + break; + case NBTConstants.TYPE_INT_ARRAY: + writeIntArrayTagPayload((IntArrayTag) tag); + break; + case NBTConstants.TYPE_LONG_ARRAY: + writeLongArrayTagPayload((LongArrayTag) tag); + break; + default: + throw new IOException("Invalid tag type: " + type + "."); } } diff --git a/worldedit-core/src/main/java/com/sk89q/minecraft/util/commands/SimpleInjector.java b/worldedit-core/src/main/java/com/sk89q/minecraft/util/commands/SimpleInjector.java new file mode 100644 index 000000000..4ffefe26a --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/minecraft/util/commands/SimpleInjector.java @@ -0,0 +1,53 @@ +/* + * 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.minecraft.util.commands; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +public class SimpleInjector implements Injector { + + private static final Logger log = LoggerFactory.getLogger(SimpleInjector.class); + private Object[] args; + private Class[] argClasses; + + public SimpleInjector(Object... args) { + this.args = args; + argClasses = new Class[args.length]; + for (int i = 0; i < args.length; ++i) { + argClasses[i] = args[i].getClass(); + } + } + + @Override + public Object getInstance(Class clazz) { + try { + Constructor ctr = clazz.getConstructor(argClasses); + ctr.setAccessible(true); + return ctr.newInstance(args); + } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) { + log.error("Error initializing commands class " + clazz, e); + return null; + } + } +} \ No newline at end of file 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 fc70841ec..42c8f81f1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -332,6 +332,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { if (get instanceof AbstractDelegateExtent && !(get instanceof NullExtent)) { traverser.setNext(nullExtent); } + get.addProcessor(nullExtent); traverser = next; } return super.cancel(); 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 0bd327bf9..0a543d24b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -39,6 +39,7 @@ import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.TextureHolder; import com.boydti.fawe.util.TextureUtil; import com.boydti.fawe.wrappers.WorldWrapper; +import com.google.common.collect.Maps; import com.sk89q.jchronic.Chronic; import com.sk89q.jchronic.Options; import com.sk89q.jchronic.utils.Span; @@ -87,7 +88,10 @@ import java.io.FileOutputStream; import java.io.IOException; import java.time.ZoneId; import java.util.Calendar; +import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; @@ -163,6 +167,7 @@ public class LocalSession implements TextureHolder { private boolean useServerCUI = false; // Save this to not annoy players. private ItemType wandItem; private ItemType navWandItem; + private Map macros = new HashMap<>(); /** * Construct the object. @@ -292,6 +297,19 @@ public class LocalSession implements TextureHolder { } } + public Map getMacros() { + return Collections.unmodifiableMap(this.macros); + } + + public void setMacro(String key, String value) { + this.macros.put(key, value); + setDirty(); + } + + public String getMacro(String key) { + return this.macros.get(key); + } + /** * Get whether this session is "dirty" and has changes that needs to * be committed. @@ -678,7 +696,12 @@ public class LocalSession implements TextureHolder { public Region getSelection(World world) throws IncompleteRegionException { checkNotNull(world); if (selector.getIncompleteRegion().getWorld() == null || !selector.getIncompleteRegion().getWorld().equals(world)) { - throw new IncompleteRegionException(); + throw new IncompleteRegionException() { + @Override + public synchronized Throwable fillInStackTrace() { + return this; + } + }; } return selector.getRegion(); } @@ -1284,6 +1307,8 @@ public class LocalSession implements TextureHolder { public void describeCUI(Actor actor) { checkNotNull(actor); + // TODO preload + if (!hasCUISupport) { return; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index 6dbda0af8..184706dee 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -19,10 +19,6 @@ package com.sk89q.worldedit.command; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.sk89q.worldedit.command.util.Logging.LogMode.PLACEMENT; -import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; - import com.boydti.fawe.FaweAPI; import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.BBC; @@ -35,7 +31,6 @@ import com.boydti.fawe.object.clipboard.URIClipboardHolder; import com.boydti.fawe.object.clipboard.WorldCutClipboard; import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.io.FastByteArrayOutputStream; -import com.boydti.fawe.object.schematic.Schematic; import com.boydti.fawe.util.ImgurUtility; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MaskTraverser; @@ -75,6 +70,13 @@ import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.world.World; +import org.enginehub.piston.annotation.Command; +import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.Arg; +import org.enginehub.piston.annotation.param.ArgFlag; +import org.enginehub.piston.annotation.param.Switch; +import org.enginehub.piston.inject.InjectedValueAccess; + import java.io.File; import java.io.IOException; import java.io.OutputStream; @@ -86,12 +88,10 @@ import java.util.HashSet; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import org.enginehub.piston.annotation.Command; -import org.enginehub.piston.annotation.CommandContainer; -import org.enginehub.piston.annotation.param.Arg; -import org.enginehub.piston.annotation.param.ArgFlag; -import org.enginehub.piston.annotation.param.Switch; -import org.enginehub.piston.inject.InjectedValueAccess; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldedit.command.util.Logging.LogMode.PLACEMENT; +import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; /** @@ -516,8 +516,7 @@ public class ClipboardCommands { final BlockVector3 to = atOrigin ? origin : session.getPlacementPosition(actor); checkPaste(actor, editSession, to, holder, clipboard); - Schematic schem = new Schematic(clipboard); - schem.paste(editSession, to, !ignoreAirBlocks); + clipboard.paste(editSession, to, !ignoreAirBlocks); Region region = clipboard.getRegion().clone(); if (selectPasted) { @@ -538,6 +537,7 @@ public class ClipboardCommands { @Command( name = "/rotate", + aliases = {"/rt"}, desc = "Rotate the contents of the clipboard", descFooter = "Non-destructively rotate the contents of the clipboard.\n" + "Angles are provided in degrees and a positive angle will result in a clockwise rotation. " + diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java index 24ec5527e..263ddcd6e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java @@ -219,8 +219,8 @@ public class HistoryCommands { } @Command( - name = "undo", - aliases = { "/undo" }, + name = "/undo", + aliases = { "/un", "/ud", "undo" }, desc = "Undoes the last action (from history)" ) @CommandPermissions({"worldedit.history.undo", "worldedit.history.undo.self"}) @@ -266,8 +266,8 @@ public class HistoryCommands { } @Command( - name = "redo", - aliases = { "/redo" }, + name = "/redo", + aliases = { "/do", "/rd", "redo" }, desc = "Redoes the last action (from history)" ) @CommandPermissions({"worldedit.history.redo", "worldedit.history.redo.self"}) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index 149d3bdfb..41681ad89 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -31,6 +31,8 @@ import static com.sk89q.worldedit.regions.Regions.minimumBlockY; import com.boydti.fawe.FaweAPI; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.implementation.processors.ChunkSendProcessor; +import com.boydti.fawe.beta.implementation.processors.NullProcessor; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FaweLimit; import com.sk89q.jnbt.CompoundTag; @@ -76,6 +78,9 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.stream.Stream; + +import com.sk89q.worldedit.world.block.BlockTypes; import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.param.Arg; @@ -102,15 +107,29 @@ public class RegionCommands { } @Command( - name = "/set", - desc = "Sets all the blocks in the region" + name = "/air", + aliases = {"/0"}, + desc = "Sets all the blocks in the region to air" + ) + @CommandPermissions("worldedit.region.set") + @Logging(REGION) + public void air(Actor actor, EditSession editSession, + @Selection Region region, + InjectedValueAccess context) throws WorldEditException { + set(actor, editSession, region, BlockTypes.AIR, context); + } + + @Command( + name = "/set", + aliases = {"/"}, + desc = "Sets all the blocks in the region" ) @CommandPermissions("worldedit.region.set") @Logging(REGION) public void set(Actor actor, EditSession editSession, - @Selection Region region, - @Arg(desc = "The pattern of blocks to set") - Pattern pattern, InjectedValueAccess context) throws WorldEditException { + @Selection Region region, + @Arg(desc = "The pattern of blocks to set") + Pattern pattern, InjectedValueAccess context) throws WorldEditException { actor.checkConfirmationRegion(() -> { int affected = editSession.setBlocks(region, pattern); if (affected != 0) { @@ -121,6 +140,21 @@ public class RegionCommands { }, getArguments(context), region, context); } + @Command( + name = "/test", + desc = "test region" + ) + @CommandPermissions("worldedit.region.test") + @Logging(REGION) + public void test(World world, Player player, EditSession editSession, + @Selection Region region, + @Arg(desc = "The pattern of blocks to set") + Pattern pattern, InjectedValueAccess context) throws WorldEditException { + editSession.addProcessor(new ChunkSendProcessor(world, () -> Stream.of(player))); + editSession.addProcessor(NullProcessor.INSTANCE); + editSession.setBlocks(region, pattern); + } + @Command( name = "/fixlighting", desc = "Get the light at a position" @@ -267,7 +301,7 @@ public class RegionCommands { @Command( name = "/replace", - aliases = { "/re", "/rep", "/r" }, + aliases = { "/repl", "/rep" }, desc = "Replace all blocks in the selection with another" ) @CommandPermissions("worldedit.region.replace") @@ -459,6 +493,7 @@ public class RegionCommands { @Command( name = "/move", + aliases = {"/mv"}, desc = "Move the contents of the selection" ) @CommandPermissions("worldedit.region.move") diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index 74b1c7091..56f97a6bb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -584,7 +584,7 @@ public class SchematicCommands { boolean oldFirst, @Switch(name = 'n', desc = "Sort by date, newest first") boolean newFirst, - @ArgFlag(name = 'f', desc = "Restricts by format.") + @ArgFlag(name = 'f', desc = "Restricts by format.", def = "") String formatName, @Arg(name = "filter", desc = "Filter for schematics", def = "all") String filter, diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java index 73a370754..66b49f89e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java @@ -194,8 +194,8 @@ public class ToolUtilCommands { } @Command( - name = "/", - aliases = {","}, + name = "/superpickaxe", + aliases = {",", "/sp", "/pickaxe"}, desc = "Toggle the super pickaxe function" ) @CommandPermissions("worldedit.superpickaxe") diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index 5c96f55fc..2b8bedb5c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -110,6 +110,15 @@ public class UtilityCommands { this.we = we; } + @Command( + name = "/macro", + desc = "Generate or run a macro" + ) + @CommandPermissions("worldedit.macro") + public void macro(Player player, LocalSession session, String name, String argument) throws IOException { + + } + @Command( name = "/heightmapinterface", desc = "Generate the heightmap interface: https://github.com/boy0001/HeightMap" @@ -420,7 +429,7 @@ public class UtilityCommands { @Command( name = "replacenear", - aliases = { "/replacenear" }, + aliases = { "/replacenear", "/rn" }, desc = "Replace nearby blocks" ) @CommandPermissions("worldedit.replacenear") diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java index df4f26481..a9e493564 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java @@ -74,7 +74,8 @@ public class DistanceWand extends BrushTool implements DoubleActionTraceTool { private Location getTarget(Player player) { Location target; Mask mask = getTraceMask(); - if (this.range < MAX_RANGE) { + int range = getRange(); + if (range < MAX_RANGE) { target = player.getBlockTrace(getRange(), true, mask); } else { target = player.getBlockTrace(MAX_RANGE, false, mask); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/MapMetadatable.java b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/MapMetadatable.java index 5ff642a9a..efa807c7a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/MapMetadatable.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/MapMetadatable.java @@ -32,6 +32,10 @@ public interface MapMetadatable extends Metadatable { return !getRawMeta().isEmpty(); } + default Object putIfAbsent(String key, Object value) { + return getRawMeta().putIfAbsent(key, value); + } + /** * {@inheritDoc} */ diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java index d7bbdef2c..40ce59e8d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java @@ -20,12 +20,12 @@ package com.sk89q.worldedit.entity; import com.boydti.fawe.Fawe; -import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.brush.visualization.VirtualWorld; import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.util.MainUtil; +import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EmptyClipboardException; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalSession; @@ -39,9 +39,9 @@ import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.regions.RegionOperationException; import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.Location; @@ -49,11 +49,10 @@ import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.gamemode.GameMode; -import java.io.File; -import java.text.NumberFormat; + import javax.annotation.Nullable; -import org.enginehub.piston.inject.InjectedValueAccess; -import org.jetbrains.annotations.NotNull; +import java.io.File; +import java.io.IOException; /** * Represents a player @@ -385,32 +384,36 @@ public interface Player extends Entity, Actor { } default int cancel(boolean close) { -// Collection queues = SetQueue.IMP.getAllQueues(); TODO NOT IMPLEMENTED -// int cancelled = 0; -// clearActions(); -// for (IQueueExtent queue : queues) { -// Collection sessions = queue.getEditSessions(); -// for (EditSession session : sessions) { -// FawePlayer currentPlayer = session.getPlayer(); -// if (currentPlayer == this) { -// if (session.cancel()) { -// cancelled++; -// } -// } -// } -// } -// VirtualWorld world = getSession().getVirtualWorld(); -// if (world != null) { -// if (close) { -// try { -// world.close(false); -// } catch (IOException e) { -// e.printStackTrace(); -// } -// } -// else world.clear(); -// } - return 0; + int cancelled = 0; + + for (Request request : Request.getAll()) { + EditSession editSession = request.getEditSession(); + if (editSession != null) { + Player player = editSession.getPlayer(); + if (equals(player)) { + editSession.cancel(); + cancelled++; + } + } + } + VirtualWorld world = getSession().getVirtualWorld(); + if (world != null) { + if (close) { + try { + world.close(false); + } catch (IOException e) { + e.printStackTrace(); + } + } + else { + try { + world.close(false); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return cancelled; } void sendTitle(String title, String sub); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java index 8bbd89a97..12ba7e815 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java @@ -38,6 +38,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.MutableBlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.ConvexPolyhedralRegion; +import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.CylinderRegion; import com.sk89q.worldedit.regions.Polygonal2DRegion; import com.sk89q.worldedit.regions.Region; @@ -80,7 +81,15 @@ import org.jetbrains.annotations.NotNull; */ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { - private final ConcurrentHashMap meta = new ConcurrentHashMap<>(); + private final Map meta; + + public AbstractPlayerActor(Map meta) { + this.meta = meta; + } + + public AbstractPlayerActor() { + this(new ConcurrentHashMap<>()); + } @Override public Map getRawMeta() { @@ -686,12 +695,10 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { * * @return an array of allowed regions */ - @Deprecated public Region[] getCurrentRegions() { - return WEManager.IMP.getMask(this); + return getCurrentRegions(FaweMaskManager.MaskType.MEMBER); } - @Deprecated public Region[] getCurrentRegions(FaweMaskManager.MaskType type) { return WEManager.IMP.getMask(this, type); } 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 6e4497b0f..ffc90b6f8 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 @@ -26,6 +26,7 @@ import com.boydti.fawe.command.CFICommands; import com.boydti.fawe.command.CFICommandsRegistration; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.task.ThrowableSupplier; import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.TaskManager; @@ -629,6 +630,7 @@ public final class PlatformCommandManager { Command cmd = optional.get(); CommandQueuedCondition queued = cmd.getCondition().as(CommandQueuedCondition.class).orElse(null); if (queued != null && !queued.isQueued()) { + System.out.println("Not queued"); handleCommandOnCurrentThread(event); return; } @@ -664,8 +666,7 @@ public final class PlatformCommandManager { MemoizingValueAccess context = initializeInjectedValues(event::getArguments, actor); - ThrowableSupplier task = - () -> commandManager.execute(context, ImmutableList.copyOf(split)); + ThrowableSupplier task = () -> commandManager.execute(context, ImmutableList.copyOf(split)); handleCommandTask(task, context, session, event); } @@ -699,6 +700,8 @@ public final class PlatformCommandManager { } else { actor.print(e.getRichMessage()); } + } catch (FaweException e) { + actor.printError("Edit cancelled: " + e.getMessage()); } catch (UsageException e) { actor.print(TextComponent.builder("") .color(TextColor.RED) @@ -726,13 +729,12 @@ public final class PlatformCommandManager { } else { System.out.println("Invalid context " + context); } - Optional editSessionOpt = - context.injectedValue(Key.of(EditSession.class)); + Optional editSessionOpt = context.injectedValue(Key.of(EditSession.class)); if (editSessionOpt.isPresent()) { EditSession editSession = editSessionOpt.get(); - session.remember(editSession); editSession.flushQueue(); + session.remember(editSession); long time = System.currentTimeMillis() - start; if (time > 1000) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java index fb67fdc6c..22ac8e8e9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java @@ -41,6 +41,7 @@ import com.sk89q.worldedit.world.gamemode.GameMode; import javax.annotation.Nullable; import java.util.UUID; +import java.util.function.Supplier; public class PlayerProxy extends AbstractPlayerActor { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java index 2e06e658d..bb2ee2019 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java @@ -247,7 +247,7 @@ public class AbstractDelegateExtent implements Extent, LightingExtent { @Override public String toString() { - return super.toString() + ":" + extent.toString(); + return super.toString() + ":" + (extent == this ? "" : extent.toString()); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java index 2d9e24f63..a42d54d19 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java @@ -21,6 +21,8 @@ package com.sk89q.worldedit.extent; import static com.google.common.base.Preconditions.checkNotNull; +import com.boydti.fawe.beta.implementation.filter.block.ExtentFilterBlock; +import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.object.changeset.FaweChangeSet; import com.boydti.fawe.object.clipboard.WorldCopyClipboard; @@ -649,4 +651,16 @@ public interface Extent extends InputExtent, OutputExtent { default Extent disableHistory() { return this; } + + default T apply(Region region, T filter) { + return apply((Iterable) region, filter); + } + + default T apply(Iterable positions, T filter) { + ExtentFilterBlock block = new ExtentFilterBlock(this); + for (BlockVector3 pos : positions) { + filter.applyBlock(block.init(pos)); + } + return filter; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java index 97d722462..8c4b6bee3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java @@ -19,18 +19,12 @@ package com.sk89q.worldedit.extent.clipboard; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.boydti.fawe.config.Settings; -import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; -import com.boydti.fawe.object.clipboard.FaweClipboard; -import com.boydti.fawe.object.clipboard.FaweClipboard.ClipboardEntity; -import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard; +import com.boydti.fawe.object.clipboard.DelegateClipboard; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; -import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; @@ -40,37 +34,26 @@ 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.BlockTypes; + +import javax.annotation.Nullable; import java.io.Closeable; -import java.util.ArrayList; -import java.util.Collections; +import java.io.IOException; import java.util.List; import java.util.UUID; -import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; /** * Stores block data as a multi-dimensional array of {@link BlockState}s and * other data as lists or maps. */ -public class BlockArrayClipboard implements Clipboard, Closeable { +public class BlockArrayClipboard extends DelegateClipboard implements Clipboard, Closeable { private Region region; private BlockVector3 origin; - public FaweClipboard IMP; - private BlockVector3 size; - private int mx; - private int my; - private int mz; - private final List entities = new ArrayList<>(); public BlockArrayClipboard(Region region) { - checkNotNull(region); - this.region = region.clone(); - this.size = getDimensions(); - this.IMP = Settings.IMP.CLIPBOARD.USE_DISK ? new DiskOptimizedClipboard(size.getBlockX(), size.getBlockY(), size.getBlockZ()) : new MemoryOptimizedClipboard(size.getBlockX(), size.getBlockY(), size.getBlockZ()); - this.origin = region.getMinimumPoint(); - this.mx = origin.getBlockX(); - this.my = origin.getBlockY(); - this.mz = origin.getBlockZ(); + this(region, UUID.randomUUID()); } /** @@ -81,48 +64,17 @@ public class BlockArrayClipboard implements Clipboard, Closeable { * @param region the bounding region */ public BlockArrayClipboard(Region region, UUID clipboardId) { + this(region, Clipboard.create(region.getDimensions(), clipboardId)); checkNotNull(region); this.region = region.clone(); - this.size = getDimensions(); - this.IMP = Settings.IMP.CLIPBOARD.USE_DISK ? new DiskOptimizedClipboard(size.getBlockX(), size.getBlockY(), size.getBlockZ(), clipboardId) : new MemoryOptimizedClipboard(size.getBlockX(), size.getBlockY(), size.getBlockZ()); this.origin = region.getMinimumPoint(); - this.mx = origin.getBlockX(); - this.my = origin.getBlockY(); - this.mz = origin.getBlockZ(); } - public BlockArrayClipboard(Region region, FaweClipboard clipboard) { + public BlockArrayClipboard(Region region, Clipboard clipboard) { + super(clipboard); checkNotNull(region); this.region = region.clone(); - this.size = getDimensions(); this.origin = region.getMinimumPoint(); - this.IMP = clipboard; - this.mx = origin.getBlockX(); - this.my = origin.getBlockY(); - this.mz = origin.getBlockZ(); - } - - public void init(Region region, FaweClipboard fc) { - checkNotNull(region); - checkNotNull(fc); - this.region = region.clone(); - this.size = getDimensions(); - this.IMP = fc; - this.origin = region.getMinimumPoint(); - this.mx = origin.getBlockX(); - this.my = origin.getBlockY(); - this.mz = origin.getBlockZ(); - } - - @Override - protected void finalize() { - close(); - } - - @Override - public void close() { - IMP.close(); - } @Override @@ -142,12 +94,7 @@ public class BlockArrayClipboard implements Clipboard, Closeable { @Override public void setOrigin(BlockVector3 origin) { this.origin = origin; - IMP.setOrigin(origin.subtract(region.getMinimumPoint())); - } - - @Override - public BlockVector3 getDimensions() { - return region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1); + getParent().setOrigin(origin.subtract(region.getMinimumPoint())); } @Override @@ -160,35 +107,13 @@ public class BlockArrayClipboard implements Clipboard, Closeable { return region.getMaximumPoint(); } - @Override - public List getEntities(Region region) { - List filtered = new ArrayList<>(); - for (Entity entity : getEntities()) { - if (region.contains(entity.getLocation().toVector().toBlockPoint())) { - filtered.add(entity); - } - } - return Collections.unmodifiableList(filtered); - } - - @Override - public List getEntities() { - return IMP.getEntities(); - } - - @Nullable - @Override - public Entity createEntity(Location location, BaseEntity entity) { - return IMP.createEntity(location.getExtent(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch(), entity); - } - @Override public BlockState getBlock(BlockVector3 position) { if (region.contains(position)) { - int x = position.getBlockX() - mx; - int y = position.getBlockY() - my; - int z = position.getBlockZ() - mz; - return IMP.getBlock(x, y, z).toImmutableState(); + int x = position.getBlockX()- origin.getX(); + int y = position.getBlockY()- origin.getY(); + int z = position.getBlockZ()- origin.getZ(); + return getParent().getBlock(x, y, z); } return BlockTypes.AIR.getDefaultState(); @@ -197,10 +122,10 @@ public class BlockArrayClipboard implements Clipboard, Closeable { @Override public BaseBlock getFullBlock(BlockVector3 position) { if(region.contains(position)) { - int x = position.getBlockX() - mx; - int y = position.getBlockY() - my; - int z = position.getBlockZ() - mz; - return IMP.getBlock(x, y, z); + int x = position.getBlockX()- origin.getX(); + int y = position.getBlockY()- origin.getY(); + int z = position.getBlockZ()- origin.getZ(); + return getParent().getFullBlock(x, y, z); } return BlockTypes.AIR.getDefaultState().toBaseBlock(); } @@ -218,10 +143,10 @@ public class BlockArrayClipboard implements Clipboard, Closeable { @Override public boolean setTile(int x, int y, int z, CompoundTag tag) { - x -= mx; - y -= my; - z -= mz; - return IMP.setTile(x, y, z, tag); + x -= origin.getX(); + y -= origin.getY(); + z -= origin.getZ(); + return getParent().setTile(x, y, z, tag); } public boolean setTile(BlockVector3 position, CompoundTag tag) { @@ -230,38 +155,149 @@ public class BlockArrayClipboard implements Clipboard, Closeable { @Override public > boolean setBlock(int x, int y, int z, B block) throws WorldEditException { - x -= mx; - y -= my; - z -= mz; - return IMP.setBlock(x, y, z, block); + x -= origin.getX(); + y -= origin.getY(); + z -= origin.getZ(); + return getParent().setBlock(x, y, z, block); } @Override public boolean hasBiomes() { - return IMP.hasBiomes(); + return getParent().hasBiomes(); } @Override public BiomeType getBiome(BlockVector2 position) { BlockVector2 v = position.subtract(region.getMinimumPoint().toBlockVector2()); - return IMP.getBiome(v.getX(), v.getZ()); + return getParent().getBiomeType(v.getX(), v.getZ()); } @Override public boolean setBiome(BlockVector2 position, BiomeType biome) { - int x = position.getBlockX() - mx; - int z = position.getBlockZ() - mz; - return IMP.setBiome(x, z, biome); + int x = position.getBlockX() - origin.getX(); + int z = position.getBlockZ() - origin.getZ(); + return getParent().setBiome(x, 0, z, biome); } @Override public boolean setBiome(int x, int y, int z, BiomeType biome) { - return IMP.setBiome(x, z, biome); + x -= origin.getX(); + y -= origin.getY(); + z -= origin.getZ(); + return getParent().setBiome(x, y, z, biome); } - @Nullable @Override - public Operation commit() { - return null; + public List getEntities(Region region) { + region = region.clone(); + region.shift(origin); + return getParent().getEntities(region); + } + + @Override + @Nullable + public Entity createEntity(Location location, BaseEntity entity) { + return getParent().createEntity(location, entity); + } + + @Override + @Nullable + public void removeEntity(int x, int y, int z, UUID uuid) { + x -= origin.getX(); + y -= origin.getY(); + z -= origin.getZ(); + getParent().removeEntity(x, y, z, uuid); + } + + @Override + public BlockState getBlock(int x, int y, int z) { + x -= origin.getX(); + y -= origin.getY(); + z -= origin.getZ(); + return getParent().getBlock(x, y, z); + } + + @Override + public BaseBlock getFullBlock(int x, int y, int z) { + x -= origin.getX(); + y -= origin.getY(); + z -= origin.getZ(); + return getParent().getFullBlock(x, y, z); + } + + @Override + public BiomeType getBiomeType(int x, int z) { + x -= origin.getX(); + z -= origin.getZ(); + return getParent().getBiomeType(x, z); + } + + /** + * Stores entity data. + */ + public static class ClipboardEntity implements Entity { + private final BaseEntity entity; + private final Clipboard clipboard; + private final double x, y, z; + private final float yaw, pitch; + + public ClipboardEntity(Location loc, BaseEntity entity) { + this((Clipboard) loc.getExtent(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), loc.getYaw(), loc.getPitch(), entity); + } + + public ClipboardEntity(Clipboard clipboard, double x, double y, double z, float yaw, float pitch, BaseEntity entity) { + checkNotNull(entity); + checkNotNull(clipboard); + this.clipboard = clipboard; + this.x = x; + this.y = y; + this.z = z; + this.yaw = yaw; + this.pitch = pitch; + this.entity = new BaseEntity(entity); + } + + @Override + public boolean remove() { + clipboard.removeEntity(this); + return true; + } + + @Nullable + @Override + public T getFacet(Class cls) { + return null; + } + + /** + * Get the entity state. This is not a copy. + * + * @return the entity + */ + BaseEntity getEntity() { + return entity; + } + + @Override + public BaseEntity getState() { + return new BaseEntity(entity); + } + + @Override + public Location getLocation() { + return new Location(clipboard, x, y, z, yaw, pitch); + } + + @Override + public Extent getExtent() { + return clipboard; + } + + @Override + public boolean setLocation(Location loc) { + clipboard.removeEntity(this); + Entity result = clipboard.createEntity(loc, entity); + return result != null; + } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java index 6994442fe..5d8da7798 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java @@ -19,15 +19,73 @@ package com.sk89q.worldedit.extent.clipboard; +import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.clipboard.CPUOptimizedClipboard; +import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; +import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard; +import com.boydti.fawe.object.clipboard.ReadOnlyClipboard; +import com.boydti.fawe.util.EditSessionBuilder; +import com.boydti.fawe.util.MaskTraverser; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; +import com.sk89q.worldedit.extent.transform.BlockTransformExtent; +import com.sk89q.worldedit.function.RegionFunction; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.operation.ForwardExtentCopy; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.function.visitor.RegionVisitor; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.MutableBlockVector2; +import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.Regions; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.block.BlockState; + +import javax.annotation.Nullable; +import java.io.Closeable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.util.Iterator; +import java.util.UUID; + +import static com.google.common.base.Preconditions.checkNotNull; /** * Specifies an object that implements something suitable as a "clipboard." */ -public interface Clipboard extends Extent { +public interface Clipboard extends Extent, Iterable, Closeable { + public static Clipboard create(Region region) { + checkNotNull(region); + checkNotNull(region.getWorld(), + "World cannot be null (use the other constructor for the region)"); + EditSession session = new EditSessionBuilder(region.getWorld()).allowedRegionsEverywhere() + .autoQueue(false).build(); + return ReadOnlyClipboard.of(session, region); + } + + public static Clipboard create(BlockVector3 size, UUID uuid) { + if (Settings.IMP.CLIPBOARD.USE_DISK) { + return new DiskOptimizedClipboard(size, uuid); + } else if (Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL == 0) { + return new CPUOptimizedClipboard(size); + } else { + return new MemoryOptimizedClipboard(size); + } + } /** * Get the bounding region of this extent. @@ -70,4 +128,260 @@ public interface Clipboard extends Extent { default boolean hasBiomes() { return false; } + + /** + * Remove entity from clipboard + * @param entity + */ + void removeEntity(Entity entity); + + default int getWidth() { + return getDimensions().getBlockX(); + } + + default int getHeight() { + return getDimensions().getBlockY(); + } + + default int getLength() { + return getDimensions().getBlockZ(); + } + + default int getArea() { + return getWidth() * getLength(); + } + + default int getVolume() { + return getWidth() * getHeight() * getLength(); + } + + default Iterator iterator() { + return getRegion().iterator(); + } + + default Iterator iterator2d() { + return Regions.asFlatRegion(getRegion()).asFlatRegion().iterator(); + } + + default URI getURI() { + return null; + } + +// default void paste(Extent other, BlockVector3 to) { +// TODO FIXME +// } + + @Override + default T apply(Region region, T filter) { + if (region.equals(getRegion())) { + return apply(this, filter); + } else { + return apply((Iterable) region, filter); + } + } + + @Override + default void close() { + + } + + + /* + Utility methods + */ + + /** + * Forwards to paste(world, to, true, true, null) + * + * @param world + * @param to + * @return + */ + default EditSession paste(World world, BlockVector3 to) { + return paste(world, to, true, true, null); + } + + default void save(File file, ClipboardFormat format) throws IOException { + checkNotNull(file); + checkNotNull(format); + if (!file.exists()) { + File parent = file.getParentFile(); + if (parent != null) { + parent.mkdirs(); + } + file.createNewFile(); + } + save(new FileOutputStream(file), format); + } + + /** + * Save this schematic to a stream + * + * @param stream + * @param format + * @throws IOException + */ + default void save(OutputStream stream, ClipboardFormat format) throws IOException { + checkNotNull(stream); + checkNotNull(format); + try (ClipboardWriter writer = format.getWriter(stream)) { + writer.write(this); + } + } + + default EditSession paste(World world, BlockVector3 to, boolean allowUndo, boolean pasteAir, + @Nullable Transform transform) { + return paste(world, to, allowUndo, pasteAir, true, transform); + } + + /** + * Paste this schematic in a world + * + * @param world + * @param to + * @param allowUndo + * @param pasteAir + * @param transform + * @return + */ + default EditSession paste(World world, BlockVector3 to, boolean allowUndo, boolean pasteAir, + boolean copyEntities, @Nullable Transform transform) { + checkNotNull(world); + checkNotNull(to); + EditSession editSession; + if (world instanceof EditSession) { + editSession = (EditSession) world; + } else { + EditSessionBuilder builder = new EditSessionBuilder(world).autoQueue(true) + .checkMemory(false).allowedRegionsEverywhere().limitUnlimited(); + if (allowUndo) { + editSession = builder.build(); + } else { + editSession = builder.changeSetNull().fastmode(true).build(); + } + } + Extent extent = this; + Mask sourceMask = editSession.getSourceMask(); + if (transform != null && !transform.isIdentity()) { + extent = new BlockTransformExtent(this, transform); + } else if (sourceMask == null) { + paste(editSession, to, pasteAir); + editSession.flushQueue(); + return editSession; + } + ForwardExtentCopy copy = new ForwardExtentCopy(extent, this.getRegion(), + this.getOrigin(), editSession, to); + if (transform != null && !transform.isIdentity()) { + copy.setTransform(transform); + } + copy.setCopyingEntities(copyEntities); + if (sourceMask != null) { + new MaskTraverser(sourceMask).reset(extent); + copy.setSourceMask(sourceMask); + editSession.setSourceMask(null); + } + if (!pasteAir) { + copy.setSourceMask(new ExistingBlockMask(this)); + } + try { + Operations.completeLegacy(copy); + } catch (MaxChangedBlocksException e) { + e.printStackTrace(); + } + editSession.flushQueue(); + return editSession; + } + + default void paste(Extent extent, BlockVector3 to, boolean pasteAir, @Nullable Transform transform) { + Extent source = this; + if (transform != null && !transform.isIdentity()) { + source = new BlockTransformExtent(this, transform); + } + ForwardExtentCopy copy = new ForwardExtentCopy(source, this.getRegion(), this.getOrigin(), extent, to); + if (transform != null) { + copy.setTransform(transform); + } + copy.setCopyingBiomes(this.hasBiomes()); + if (extent instanceof EditSession) { + EditSession editSession = (EditSession) extent; + Mask sourceMask = editSession.getSourceMask(); + if (sourceMask != null) { + new MaskTraverser(sourceMask).reset(extent); + copy.setSourceMask(sourceMask); + editSession.setSourceMask(null); + } + } + if (!pasteAir) { + copy.setSourceMask(new ExistingBlockMask(this)); + } + Operations.completeBlindly(copy); + } + + default void paste(Extent extent, BlockVector3 to, boolean pasteAir) { + Region region = this.getRegion().clone(); + final BlockVector3 origin = this.getOrigin(); + + final boolean copyBiomes = this.hasBiomes(); + // To must be relative to the clipboard origin ( player location - clipboard origin ) (as the locations supplied are relative to the world origin) + final int relx = to.getBlockX() - origin.getBlockX(); + final int rely = to.getBlockY() - origin.getBlockY(); + final int relz = to.getBlockZ() - origin.getBlockZ(); + +// this.apply(this, new Filter() { +// @Override +// public void applyBlock(FilterBlock block) { +// +// } +// }); + + System.out.println("Rel " + relx + "," + rely + "," + relz + " | " + to + " | " + origin); + + System.out.println("TODO optimize paste using above apply"); + + Operation visitor = new RegionVisitor(region, new RegionFunction() { + // MutableBlockVector2 mpos2d_2 = new MutableBlockVector2(); + MutableBlockVector2 mpos2d = new MutableBlockVector2(); + + { + mpos2d.setComponents(Integer.MIN_VALUE, Integer.MIN_VALUE); + } + + @Override + public boolean apply(BlockVector3 mutable) throws WorldEditException { + BlockState block = getBlock(mutable); + System.out.println("Pos " + mutable); + int xx = mutable.getBlockX() + relx; + int zz = mutable.getBlockZ() + relz; + if (copyBiomes && xx != mpos2d.getBlockX() && zz != mpos2d.getBlockZ()) { + mpos2d.setComponents(xx, zz); +// extent.setBiome(mpos2d, clipboard.getBiome(mpos2d_2.setComponents(mutable.getBlockX(), mutable.getBlockZ()))); + extent.setBiome(mpos2d, Clipboard.this + .getBiome(BlockVector2.at(mutable.getBlockX(), mutable.getBlockZ()))); + } + if (!pasteAir && block.getBlockType().getMaterial().isAir()) { + return false; + } + extent.setBlock(xx, mutable.getBlockY() + rely, zz, block); + return false; + } + }); + Operations.completeBlindly(visitor); + // Entity offset is the paste location subtract the clipboard origin (entity's location is already relative to the world origin) + final int entityOffsetX = to.getBlockX() - origin.getBlockX(); + final int entityOffsetY = to.getBlockY() - origin.getBlockY(); + final int entityOffsetZ = to.getBlockZ() - origin.getBlockZ(); + // entities + for (Entity entity : this.getEntities()) { + // skip players on pasting schematic + if (entity.getState() != null && entity.getState().getType().getId() + .equals("minecraft:player")) { + continue; + } + Location pos = entity.getLocation(); + Location newPos = new Location(pos.getExtent(), pos.getX() + entityOffsetX, + pos.getY() + entityOffsetY, pos.getZ() + entityOffsetZ, pos.getYaw(), + pos.getPitch()); + extent.createEntity(newPos, entity.getState()); + } + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java index 37f9ebec9..25e0ff743 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java @@ -25,7 +25,6 @@ import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.clipboard.URIClipboardHolder; import com.boydti.fawe.object.io.PGZIPOutputStream; -import com.boydti.fawe.object.schematic.Schematic; import com.boydti.fawe.util.MainUtil; import com.google.gson.Gson; import com.sk89q.worldedit.LocalSession; @@ -127,12 +126,12 @@ public interface ClipboardFormat { return holder; } - default Schematic load(File file) throws IOException { + default Clipboard load(File file) throws IOException { return load(new FileInputStream(file)); } - default Schematic load(InputStream stream) throws IOException { - return new Schematic(getReader(stream).read()); + default Clipboard load(InputStream stream) throws IOException { + return getReader(stream).read(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardReader.java index 07ff96593..a1b893890 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardReader.java @@ -19,11 +19,16 @@ package com.sk89q.worldedit.extent.clipboard.io; +import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.math.BlockVector3; import java.io.Closeable; import java.io.IOException; import java.util.UUID; +import java.util.function.Function; /** * Reads {@code Clipboard}s. @@ -38,9 +43,20 @@ public interface ClipboardReader extends Closeable { * @return the read clipboard * @throws IOException thrown on I/O error */ - Clipboard read() throws IOException; + default Clipboard read() throws IOException { + return read(UUID.randomUUID()); + } default Clipboard read(UUID uuid) throws IOException { + return read(uuid, new Function() { + @Override + public Clipboard apply(BlockVector3 dimensions) { + return new DiskOptimizedClipboard(dimensions); + } + }); + } + + default Clipboard read(UUID uuid, Function createOutput) throws IOException { return read(); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FaweFormat.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FaweFormat.java new file mode 100644 index 000000000..0daf069f9 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FaweFormat.java @@ -0,0 +1,4 @@ +package com.sk89q.worldedit.extent.clipboard.io; + +public class FaweFormat { +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java index a95844c29..421a48874 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java @@ -20,25 +20,95 @@ package com.sk89q.worldedit.extent.clipboard.io; import com.boydti.fawe.Fawe; -import com.boydti.fawe.jnbt.CorruptSchematicStreamer; -import com.boydti.fawe.jnbt.SchematicStreamer; - -import static com.google.common.base.Preconditions.checkNotNull; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.jnbt.streamer.InfoReader; +import com.boydti.fawe.jnbt.streamer.IntValueReader; +import com.boydti.fawe.jnbt.streamer.StreamDelegate; +import com.boydti.fawe.jnbt.streamer.ValueReader; +import com.boydti.fawe.object.FaweInputStream; +import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.object.clipboard.LinearClipboard; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; +import com.boydti.fawe.object.io.FastByteArraysInputStream; +import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.EntityNBTCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.FlowerPotCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NBTCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NoteBlockCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.Pre13HangingCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.SignCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.SkullBlockCompatibilityHandler; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.registry.state.PropertyKey; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockCategories; +import com.sk89q.worldedit.world.block.BlockID; +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.block.BlockTypeSwitch; +import com.sk89q.worldedit.world.block.BlockTypeSwitchBuilder; +import com.sk89q.worldedit.world.entity.EntityType; +import com.sk89q.worldedit.world.entity.EntityTypes; +import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.world.registry.LegacyMapper; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.function.Function; + +import static com.google.common.base.Preconditions.checkNotNull; /** * Reads schematic files based that are compatible with MCEdit and other editors. */ public class SchematicReader implements ClipboardReader { + private static final NBTCompatibilityHandler[] COMPATIBILITY_HANDLERS = { + new SignCompatibilityHandler(), + new FlowerPotCompatibilityHandler(), + new NoteBlockCompatibilityHandler(), + new SkullBlockCompatibilityHandler() + }; + private static final EntityNBTCompatibilityHandler[] ENTITY_COMPATIBILITY_HANDLERS = { + new Pre13HangingCompatibilityHandler() + }; + private NBTInputStream inputStream; private InputStream rootStream; +// private final DataFixer fixer; TODO + + private FastByteArrayOutputStream idOut = new FastByteArrayOutputStream(); + private FastByteArrayOutputStream dataOut = new FastByteArrayOutputStream(); + private FastByteArrayOutputStream addOut; + private FastByteArrayOutputStream biomesOut; + + private FaweOutputStream ids; + private FaweOutputStream datas; + private FaweOutputStream adds; + private FaweOutputStream biomes; + + private List> tiles; + private List> entities; + + private int width, height, length; + private int offsetX, offsetY, offsetZ; + private int originX, originY, originZ; + /** * Create a new instance. * @@ -53,22 +123,394 @@ public class SchematicReader implements ClipboardReader { this.rootStream = in; } - @Override - public Clipboard read() throws IOException { - return read(UUID.randomUUID()); + public StreamDelegate createDelegate() { + StreamDelegate root = new StreamDelegate(); + StreamDelegate schematic = root.add("Schematic"); + schematic.add("Width").withInt((i, v) -> width = v); + schematic.add("Height").withInt((i, v) -> height = v); + schematic.add("Length").withInt((i, v) -> length = v); + + schematic.add("WEOriginX").withInt((i, v) -> originX = v); + schematic.add("WEOriginY").withInt((i, v) -> originY = v); + schematic.add("WEOriginZ").withInt((i, v) -> originZ = v); + + StreamDelegate metadata = schematic.add("Metadata"); + metadata.add("WEOffsetX").withInt((i, v) -> offsetX = v); + metadata.add("WEOffsetY").withInt((i, v) -> offsetY = v); + metadata.add("WEOffsetZ").withInt((i, v) -> offsetZ = v); + + StreamDelegate blocksDelegate = schematic.add("Blocks"); + blocksDelegate.withInfo((length, type) -> ids = new FaweOutputStream(new LZ4BlockOutputStream(idOut))); + blocksDelegate.withInt((index, value) -> ids.write(value)); + + StreamDelegate dataDelegate = schematic.add("Data"); + dataDelegate.withInfo((length, type) -> datas = new FaweOutputStream(new LZ4BlockOutputStream(dataOut))); + dataDelegate.withInt((index, value) -> datas.write(value)); + + StreamDelegate addDelegate = schematic.add("AddBlocks"); + addDelegate.withInfo((length, type) -> { + addOut = new FastByteArrayOutputStream(); + adds = new FaweOutputStream(new LZ4BlockOutputStream(addOut)); + }); + addDelegate.withInt((index, value) -> { + if (value != 0) { + int first = value & 0x0F; + int second = (value & 0xF0) >> 4; + adds.write(first); + adds.write(second); + } else { + adds.write(0); + adds.write(0); + } + }); + + StreamDelegate biomesDelegate = schematic.add("Biomes"); + StreamDelegate aweBiomesDelegate = schematic.add("AWEBiomes"); + + InfoReader biomesInfo = (l, t) -> { + biomesOut = new FastByteArrayOutputStream(); + biomes = new FaweOutputStream(new LZ4BlockOutputStream(biomesOut)); + }; + biomesDelegate.withInfo(biomesInfo); + aweBiomesDelegate.withInfo(biomesInfo); + + IntValueReader biomeReader = (index, value) -> biomes.write(value); + biomesDelegate.withInt(biomeReader); + + + StreamDelegate tilesDelegate = schematic.add("TileEntities"); + tilesDelegate.withInfo((length, type) -> tiles = new ArrayList<>(length)); + tilesDelegate.withElem(new ValueReader>() { + @Override + public void apply(int index, Map tile) { + tiles.add(tile); + } + }); + + StreamDelegate entitiesDelegate = schematic.add("Entities"); + entitiesDelegate.withInfo((length, type) -> entities = new ArrayList<>(length)); + entitiesDelegate.withElem(new ValueReader>() { + @Override + public void apply(int index, Map entity) { + entities.add(entity); + } + }); + return root; } - public Clipboard read(final UUID clipboardId) throws IOException { - try { - return new SchematicStreamer(inputStream, clipboardId).getClipboard(); - } catch (Exception e) { - Fawe.debug("Input is corrupt!"); - e.printStackTrace(); - return new CorruptSchematicStreamer(rootStream, clipboardId).recover(); + private int readCombined(InputStream idIn, InputStream dataIn) throws IOException { + return ((idIn.read() & 0xFF) << 4) + (dataIn.read() & 0xF); + } + + private int readCombined(InputStream idIn, InputStream dataIn, InputStream addIn) throws IOException { + return ((addIn.read() & 0xFF) << 8) + readCombined(idIn, dataIn); + } + + private BlockState getBlock(int combined) { + BlockState state = LegacyMapper.getInstance().getBlockFromLegacyCombinedId(combined); + return state; + } + + private void write(int index, BlockState block, LinearClipboard clipboard) { + clipboard.setBlock(index, block); + } + + private void write(int x, int y, int z, BlockState block, Clipboard clipboard) { + clipboard.setBlock(x, y, z, block); + } + + private void readwrite(int index, InputStream idIn, InputStream dataIn, LinearClipboard out) throws IOException { + readwrite(index, readCombined(idIn, dataIn), out); + } + + private void readwrite(int x, int y, int z, InputStream idIn, InputStream dataIn, Clipboard out) throws IOException { + readwrite(x, y, z, readCombined(idIn, dataIn), out); + } + + private void readwrite(int index, InputStream idIn, InputStream dataIn, InputStream addIn, LinearClipboard out) throws IOException { + readwrite(index, readCombined(idIn, dataIn, addIn), out); + } + + private void readwrite(int x, int y, int z, InputStream idIn, InputStream dataIn, InputStream addIn, Clipboard out) throws IOException { + readwrite(x, y, z, readCombined(idIn, dataIn, addIn), out); + } + + private void readwrite(int index, int combined, LinearClipboard out) throws IOException { + write(index, getBlock(combined), out); + } + + private void readwrite(int x, int y, int z, int combined, Clipboard out) throws IOException { + write(x, y, z, getBlock(combined), out); + } + + @Override + public Clipboard read(UUID uuid, Function createOutput) throws IOException { + StreamDelegate root = createDelegate(); + inputStream.readNamedTagLazy(root); + + if (ids != null) ids.close(); + if (datas != null) datas.close(); + if (adds != null) adds.close(); + if (biomes != null) biomes.close(); + ids = null; + datas = null; + adds = null; + biomes = null; + + BlockVector3 dimensions = BlockVector3.at(width, height, length); + BlockVector3 origin = BlockVector3.at(originX, originY, originZ); + if (offsetX != Integer.MIN_VALUE && offsetY != Integer.MIN_VALUE && offsetZ != Integer.MIN_VALUE) { + origin = origin.subtract(BlockVector3.at(offsetX, offsetY, offsetZ)); + } + + Clipboard clipboard = createOutput.apply(dimensions); + try (InputStream dataIn = new LZ4BlockInputStream(new FastByteArraysInputStream(dataOut.toByteArrays()));InputStream idIn = new LZ4BlockInputStream(new FastByteArraysInputStream(idOut.toByteArrays()))) { + if (addOut != null) { + try (FaweInputStream addIn = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(addOut.toByteArrays())))) { + if (clipboard instanceof LinearClipboard) { + LinearClipboard linear = (LinearClipboard) clipboard; + for (int y = 0, index = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++, index++) { + readwrite(index, idIn, dataIn, addIn, linear); + } + } + } + } else { + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + readwrite(x, y, z, idIn, dataIn, addIn, clipboard); + } + } + } + } + } + } else { + if (clipboard instanceof LinearClipboard) { + LinearClipboard linear = (LinearClipboard) clipboard; + for (int y = 0, index = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++, index++) { + readwrite(index, idIn, dataIn, linear); + } + } + } + } else { + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + readwrite(x, y, z, idIn, dataIn, clipboard); + } + } + } + } + } + } + + if (biomes != null) { + try (InputStream biomesIn = new LZ4BlockInputStream(new FastByteArraysInputStream(biomesOut.toByteArrays()))) { + if (clipboard instanceof LinearClipboard) { + LinearClipboard linear = (LinearClipboard) clipboard; + int volume = width * length; + for (int index = 0; index < volume; index++) { + BiomeType biome = BiomeTypes.getLegacy(biomesIn.read()); + if (biome != null) linear.setBiome(index, biome); + } + } else { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + BiomeType biome = BiomeTypes.getLegacy(biomesIn.read()); + if (biome != null) clipboard.setBiome(x, 0, z, biome); + } + } + } + } + } + + // tiles + if (tiles != null && !tiles.isEmpty()) { + outer: + for (Map tileRaw : tiles) { + CompoundTag tile = FaweCache.IMP.asTag(tileRaw); + int x = (int) tileRaw.get("x"); + int y = (int) tileRaw.get("y"); + int z = (int) tileRaw.get("z"); + + BlockState block = clipboard.getBlock(x, y, z); + for (NBTCompatibilityHandler compat : COMPATIBILITY_HANDLERS) { + if (compat.isAffectedBlock(block)) { + block = compat.updateNBT(block, tile.getValue()); + BaseBlock baseBlock = block.toBaseBlock(tile); + clipboard.setBlock(x, y, z, baseBlock); + continue outer; + } + } + clipboard.setTile(x, y, z, tile); + } + } + + // entities + if (entities != null && !entities.isEmpty()) { + for (Map entRaw : entities) { + String id = (String) entRaw.get("id"); + if (id == null) { + continue; + } + entRaw.put("Id", id); + EntityType type = EntityTypes.parse(id); + if (type != null) { + CompoundTag ent = FaweCache.IMP.asTag(entRaw); + for (EntityNBTCompatibilityHandler compat : ENTITY_COMPATIBILITY_HANDLERS) { + if (compat.isAffectedEntity(type, ent)) { + ent = compat.updateNBT(type, ent); + } + } + BaseEntity state = new BaseEntity(type, ent); + Location loc = ent.getEntityLocation(clipboard); + clipboard.createEntity(loc, state); + } else { + Fawe.debug("Invalid entity: " + id); + } + } + } + fixStates(clipboard); + clipboard.setOrigin(origin); + return clipboard; + } + + private void fixStates(Clipboard fc) { + for (BlockVector3 pos : fc) { + BlockState block = pos.getBlock(fc); + if (block.getMaterial().isAir()) continue; + + int x = pos.getX(); + int y = pos.getY(); + int z = pos.getZ(); + + BlockType type = block.getBlockType(); + if (BlockCategories.STAIRS.contains(type)) { + Direction facing = block.getState(PropertyKey.FACING); + + BlockVector3 forward = facing.toBlockVector(); + Direction left = facing.getLeft(); + Direction right = facing.getRight(); + + BlockStateHolder forwardBlock = fc.getBlock(x + forward.getBlockX(), y + forward.getBlockY(), z + forward.getBlockZ()); + BlockType forwardType = forwardBlock.getBlockType(); + if (forwardType.hasProperty(PropertyKey.SHAPE) && forwardType.hasProperty(PropertyKey.FACING)) { + Direction forwardFacing = (Direction) forwardBlock.getState(PropertyKey.FACING); + if (forwardFacing == left) { + BlockState rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ()); + BlockType rightType = rightBlock.getBlockType(); + if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) { + pos.setBlock(fc, block.with(PropertyKey.SHAPE, "inner_left")); + } + return; + } else if (forwardFacing == right) { + BlockState leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ()); + BlockType leftType = leftBlock.getBlockType(); + if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) { + fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "inner_right")); + } + return; + } + } + + BlockState backwardsBlock = fc.getBlock(x - forward.getBlockX(), y - forward.getBlockY(), z - forward.getBlockZ()); + BlockType backwardsType = backwardsBlock.getBlockType(); + if (backwardsType.hasProperty(PropertyKey.SHAPE) && backwardsType.hasProperty(PropertyKey.FACING)) { + Direction backwardsFacing = (Direction) backwardsBlock.getState(PropertyKey.FACING); + if (backwardsFacing == left) { + BlockState rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ()); + BlockType rightType = rightBlock.getBlockType(); + if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) { + pos.setBlock(fc, block.with(PropertyKey.SHAPE, "outer_left")); + } + return; + } else if (backwardsFacing == right) { + BlockState leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ()); + BlockType leftType = leftBlock.getBlockType(); + if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) { + pos.setBlock(fc, block.with(PropertyKey.SHAPE, "outer_right")); + } + return; + } + } + } else { + int group = group(type); + if (group == -1) return; + BlockState set = block; + + if (set.getState(PropertyKey.NORTH) == Boolean.FALSE && merge(fc, group, x, y, z - 1)) set = set.with(PropertyKey.NORTH, true); + if (set.getState(PropertyKey.EAST) == Boolean.FALSE && merge(fc, group, x + 1, y, z)) set = set.with(PropertyKey.EAST, true); + if (set.getState(PropertyKey.SOUTH) == Boolean.FALSE && merge(fc, group, x, y, z + 1)) set = set.with(PropertyKey.SOUTH, true); + if (set.getState(PropertyKey.WEST) == Boolean.FALSE && merge(fc, group, x - 1, y, z)) set = set.with(PropertyKey.WEST, true); + + if (group == 2) { + int ns = ((Boolean) set.getState(PropertyKey.NORTH) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.SOUTH) ? 1 : 0); + int ew = ((Boolean) set.getState(PropertyKey.EAST) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.WEST) ? 1 : 0); + if (Math.abs(ns - ew) != 2 || fc.getBlock(x, y + 1, z).getBlockType().getMaterial().isSolid()) { + set = set.with(PropertyKey.UP, true); + } + } + + if (set != block) pos.setBlock(fc, set); + } } } + private BlockTypeSwitch fullCube = new BlockTypeSwitchBuilder<>(false).add(type -> { + BlockMaterial mat = type.getMaterial(); + return (mat.isFullCube() && !mat.isFragileWhenPushed() && mat.getLightValue() == 0 && mat.isOpaque() && mat.isSolid() && !mat.isTranslucent()); + }, true).build(); + + private boolean merge(Clipboard fc, int group, int x, int y, int z) { + BlockState block = fc.getBlock(x, y, z); + BlockType type = block.getBlockType(); + return group(type) == group || fullCube.apply(type); + } + + private int group(BlockType type) { + switch (type.getInternalId()) { + case BlockID.ACACIA_FENCE: + case BlockID.BIRCH_FENCE: + case BlockID.DARK_OAK_FENCE: + case BlockID.JUNGLE_FENCE: + case BlockID.OAK_FENCE: + case BlockID.SPRUCE_FENCE: + return 0; + case BlockID.NETHER_BRICK_FENCE: + return 1; + case BlockID.COBBLESTONE_WALL: + case BlockID.MOSSY_COBBLESTONE_WALL: + return 2; + case BlockID.IRON_BARS: + case BlockID.BLACK_STAINED_GLASS_PANE: + case BlockID.BLUE_STAINED_GLASS_PANE: + case BlockID.BROWN_MUSHROOM_BLOCK: + case BlockID.BROWN_STAINED_GLASS_PANE: + case BlockID.CYAN_STAINED_GLASS_PANE: + case BlockID.GLASS_PANE: + case BlockID.GRAY_STAINED_GLASS_PANE: + case BlockID.GREEN_STAINED_GLASS_PANE: + case BlockID.LIGHT_BLUE_STAINED_GLASS_PANE: + case BlockID.LIGHT_GRAY_STAINED_GLASS_PANE: + case BlockID.LIME_STAINED_GLASS_PANE: + case BlockID.MAGENTA_STAINED_GLASS_PANE: + case BlockID.ORANGE_STAINED_GLASS_PANE: + case BlockID.PINK_STAINED_GLASS_PANE: + case BlockID.PURPLE_STAINED_GLASS_PANE: + case BlockID.RED_STAINED_GLASS_PANE: + case BlockID.WHITE_STAINED_GLASS_PANE: + case BlockID.YELLOW_STAINED_GLASS_PANE: + return 3; + default: + return -1; + } + } + @Override public void close() throws IOException { inputStream.close(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java index 64085e274..a5a7a263b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java @@ -19,25 +19,24 @@ package com.sk89q.worldedit.extent.clipboard.io; -import static com.google.common.base.Preconditions.checkNotNull; - import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.Settings; -import com.boydti.fawe.jnbt.NBTStreamer; -import com.boydti.fawe.jnbt.NBTStreamer.LazyReader; +import com.boydti.fawe.jnbt.streamer.InfoReader; +import com.boydti.fawe.jnbt.streamer.IntValueReader; +import com.boydti.fawe.jnbt.streamer.StreamDelegate; +import com.boydti.fawe.jnbt.streamer.ValueReader; import com.boydti.fawe.object.FaweInputStream; import com.boydti.fawe.object.FaweOutputStream; import com.boydti.fawe.object.clipboard.CPUOptimizedClipboard; import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; -import com.boydti.fawe.object.clipboard.FaweClipboard; +import com.boydti.fawe.object.clipboard.LinearClipboard; import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard; import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.object.io.FastByteArraysInputStream; -import com.boydti.fawe.util.IOUtil; import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.IntTag; -import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; @@ -54,20 +53,25 @@ 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.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.entity.EntityTypes; -import com.sk89q.worldedit.world.storage.NBTConversions; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.DataInputStream; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.UUID; -import java.util.function.BiConsumer; -import net.jpountz.lz4.LZ4BlockInputStream; -import net.jpountz.lz4.LZ4BlockOutputStream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.util.function.Function; + +import static com.google.common.base.Preconditions.checkNotNull; /** * Reads schematic files using the Sponge Schematic Specification. @@ -79,6 +83,21 @@ public class SpongeSchematicReader extends NBTSchematicReader { private DataFixer fixer = null; private int dataVersion = -1; + private FastByteArrayOutputStream blocksOut; + private FaweOutputStream blocks; + + private FastByteArrayOutputStream biomesOut; + private FaweOutputStream biomes; + + private List> tiles; + private List> entities; + + private int width, height, length; + private int offsetX, offsetY, offsetZ; + private char[] palette, biomePalette; + private BlockVector3 min; + + /** * Create a new instance. * @@ -89,38 +108,6 @@ public class SpongeSchematicReader extends NBTSchematicReader { this.inputStream = inputStream; } - @Override - public Clipboard read() throws IOException { - return read(UUID.randomUUID()); - } - - @Override - public Clipboard read(UUID uuid) throws IOException { - return reader(uuid); - } - - private int width, height, length; - private int offsetX, offsetY, offsetZ; - private char[] palette; - private BlockVector3 min; - private FaweClipboard fc; - - private FaweClipboard setupClipboard(int size, UUID uuid) { - if (fc != null) { - if (fc.getDimensions().getX() == 0) { - fc.setDimensions(BlockVector3.at(size, 1, 1)); - } - return fc; - } - if (Settings.IMP.CLIPBOARD.USE_DISK) { - return fc = new DiskOptimizedClipboard(size, 1, 1, uuid); - } else if (Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL == 0) { - return fc = new CPUOptimizedClipboard(size, 1, 1); - } else { - return fc = new MemoryOptimizedClipboard(size, 1, 1); - } - } - private String fix(String palettePart) { if (fixer == null || dataVersion == -1) return palettePart; return fixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, palettePart, dataVersion); @@ -136,26 +123,24 @@ public class SpongeSchematicReader extends NBTSchematicReader { return fixer.fixUp(DataFixer.FixTypes.ENTITY, tag, dataVersion); } - private Clipboard reader(UUID uuid) throws IOException { - width = height = length = offsetX = offsetY = offsetZ = Integer.MIN_VALUE; + public StreamDelegate createDelegate() { + StreamDelegate root = new StreamDelegate(); + StreamDelegate schematic = root.add("Schematic"); + schematic.add("DataVersion").withInt((i, v) -> dataVersion = v); + schematic.add("Width").withInt((i, v) -> width = v); + schematic.add("Height").withInt((i, v) -> height = v); + schematic.add("Length").withInt((i, v) -> length = v); + schematic.add("Offset").withValue((ValueReader) (index, v) -> min = BlockVector3.at(v[0], v[1], v[2])); - final BlockArrayClipboard clipboard = new BlockArrayClipboard(new CuboidRegion(BlockVector3.at(0, 0, 0), BlockVector3.at(0, 0, 0)), fc); - FastByteArrayOutputStream blocksOut = new FastByteArrayOutputStream(); - FastByteArrayOutputStream biomesOut = new FastByteArrayOutputStream(); + StreamDelegate metadata = schematic.add("Metadata"); + metadata.add("WEOffsetX").withInt((i, v) -> offsetX = v); + metadata.add("WEOffsetY").withInt((i, v) -> offsetY = v); + metadata.add("WEOffsetZ").withInt((i, v) -> offsetZ = v); - - NBTStreamer streamer = new NBTStreamer(inputStream); - streamer.addReader("Schematic.DataVersion", (BiConsumer) (i, v) -> dataVersion = v); - streamer.addReader("Schematic.Width", (BiConsumer) (i, v) -> width = v); - streamer.addReader("Schematic.Height", (BiConsumer) (i, v) -> height = v); - streamer.addReader("Schematic.Length", (BiConsumer) (i, v) -> length = v); - streamer.addReader("Schematic.Offset", (BiConsumer) (i, v) -> min = BlockVector3.at(v[0], v[1], v[2])); - streamer.addReader("Schematic.Metadata.WEOffsetX", (BiConsumer) (i, v) -> offsetX = v); - streamer.addReader("Schematic.Metadata.WEOffsetY", (BiConsumer) (i, v) -> offsetY = v); - streamer.addReader("Schematic.Metadata.WEOffsetZ", (BiConsumer) (i, v) -> offsetZ = v); - streamer.addReader("Schematic.Palette", (BiConsumer>) (i, v) -> { + StreamDelegate paletteDelegate = schematic.add("Palette"); + paletteDelegate.withValue((ValueReader>) (ignore, v) -> { palette = new char[v.size()]; - for (Map.Entry entry : v.entrySet()) { + for (Entry entry : v.entrySet()) { BlockState state = null; try { String palettePart = fix(entry.getKey()); @@ -163,227 +148,202 @@ public class SpongeSchematicReader extends NBTSchematicReader { } catch (InputParseException e) { e.printStackTrace(); } - int index = ((IntTag) entry.getValue()).getValue(); + int index = (int) entry.getValue(); palette[index] = (char) state.getOrdinal(); } }); + StreamDelegate blockData = schematic.add("BlockData"); + blockData.withInfo((length, type) -> { + blocksOut = new FastByteArrayOutputStream(); + blocks = new FaweOutputStream(new LZ4BlockOutputStream(blocksOut)); + }); + blockData.withInt((index, value) -> blocks.writeVarInt(value)); + StreamDelegate tilesDelegate = schematic.add("TileEntities"); + tilesDelegate.withInfo((length, type) -> tiles = new ArrayList<>(length)); + tilesDelegate.withElem((ValueReader>) (index, tile) -> tiles.add(tile)); - /// readBiomes - - streamer.addReader("Schematic.BlockData.#", (LazyReader) (arrayLen, dis) -> { - try (FaweOutputStream blocks = new FaweOutputStream(new LZ4BlockOutputStream(blocksOut))) { - IOUtil.copy(dis, blocks, arrayLen); + StreamDelegate entitiesDelegate = schematic.add("Entities"); + entitiesDelegate.withInfo((length, type) -> entities = new ArrayList<>(length)); + entitiesDelegate.withElem((ValueReader>) (index, entity) -> entities.add(entity)); + StreamDelegate biomeData = schematic.add("BiomeData"); + biomeData.withInfo((length, type) -> { + biomesOut = new FastByteArrayOutputStream(); + biomes = new FaweOutputStream(new LZ4BlockOutputStream(blocksOut)); + }); + biomeData.withElem((IntValueReader) (index, value) -> { + try { + biomes.write(value); // byte of varInt } catch (IOException e) { e.printStackTrace(); } }); - streamer.addReader("Schematic.Biomes.#", (LazyReader) (arrayLen, dis) -> { - try (FaweOutputStream biomes = new FaweOutputStream(new LZ4BlockOutputStream(biomesOut))) { - IOUtil.copy(dis, biomes, arrayLen); - } catch (IOException e) { - e.printStackTrace(); + StreamDelegate biomePaletteDelegate = schematic.add("BiomePalette"); + biomePaletteDelegate.withInfo((length, type) -> biomePalette = new char[length]); + biomePaletteDelegate.withElem(new ValueReader>() { + @Override + public void apply(int index, Map.Entry palettePart) { + String key = palettePart.getKey(); + if (fixer != null) { + key = fixer.fixUp(DataFixer.FixTypes.BIOME, key, dataVersion); + } + BiomeType biome = BiomeTypes.get(key); + if (biome == null) { + System. out.println("Unknown biome " + key); + biome = BiomeTypes.FOREST; + } + int paletteIndex = palettePart.getValue().intValue(); + biomePalette[paletteIndex] = (char) biome.getInternalId(); } }); - streamer.addReader("Schematic.TileEntities.#", (BiConsumer) (index, value) -> { - if (fc == null) { - setupClipboard(0, uuid); - } - int[] pos = value.getIntArray("Pos"); - int x,y,z; - if (pos.length != 3) { - System.out.println("Invalid tile " + value); - if (!value.containsKey("x") || !value.containsKey("y") || !value.containsKey("z")) { - return; - } - x = value.getInt("x"); - y = value.getInt("y"); - z = value.getInt("z"); - } else { - x = pos[0]; - y = pos[1]; - z = pos[2]; - } - Map values = value.getValue(); - Tag id = values.get("Id"); - if (id != null) { - values.put("x", new IntTag(x)); - values.put("y", new IntTag(y)); - values.put("z", new IntTag(z)); - values.put("id", id); - } - values.remove("Id"); - values.remove("Pos"); - value = fixBlockEntity(value); - fc.setTile(x, y, z, value); - }); - streamer.addReader("Schematic.Entities.#", (BiConsumer) (index, compound) -> { - if (fc == null) { - setupClipboard(0, uuid); - } - Map value = compound.getValue(); - StringTag id = (StringTag) value.get("Id"); - if (id == null) { - id = (StringTag) value.get("id"); - if (id == null) { - return; - } - } else { - value.put("id", id); - value.remove("Id"); - } + return root; + } - ListTag positionTag = compound.getListTag("Pos"); - ListTag directionTag = compound.getListTag("Rotation"); - EntityType type = EntityTypes.parse(id.getValue()); - if (type != null) { - compound = fixEntity(compound); - BaseEntity state = new BaseEntity(type, compound); - fc.createEntity(clipboard, positionTag.asDouble(0), positionTag.asDouble(1), positionTag.asDouble(2), (float) directionTag.asDouble(0), (float) directionTag.asDouble(1), state); - } else { - Fawe.debug("Invalid entity: " + id); - } - }); - streamer.readFully(); - if (fc == null) setupClipboard(length * width * height, uuid); - fc.setDimensions(BlockVector3.at(width, height, length)); + private BlockState getBlockState(int id) { + return BlockTypesCache.states[palette[id]]; + } + + private BiomeType getBiomeType(FaweInputStream fis) throws IOException { + char biomeId = biomePalette[fis.readVarInt()]; + BiomeType biome = BiomeTypes.get(biomeId); + return biome; + } + + @Override + public Clipboard read(UUID uuid, Function createOutput) throws IOException { + StreamDelegate root = createDelegate(); + inputStream.readNamedTagLazy(root); + if (blocks != null) blocks.close(); + if (biomes != null) biomes.close(); + blocks = null; + biomes = null; + + BlockVector3 dimensions = BlockVector3.at(width, height, length); BlockVector3 origin = min; - CuboidRegion region; if (offsetX != Integer.MIN_VALUE && offsetY != Integer.MIN_VALUE && offsetZ != Integer.MIN_VALUE) { origin = origin.subtract(BlockVector3.at(offsetX, offsetY, offsetZ)); } - region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE)); - if (blocksOut.getSize() != 0) { + + Clipboard clipboard = createOutput.apply(dimensions); + + if (blocksOut != null && blocksOut.getSize() != 0) { try (FaweInputStream fis = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(blocksOut.toByteArrays())))) { - int volume = width * height * length; - if (palette.length < 128) { - for (int index = 0; index < volume; index++) { - BlockState state = BlockTypes.states[palette[fis.read()]]; - fc.setBlock(index, state); + if (clipboard instanceof LinearClipboard) { + LinearClipboard linear = (LinearClipboard) clipboard; + int volume = width * height * length; + if (palette.length < 128) { + for (int index = 0; index < volume; index++) { + linear.setBlock(index, getBlockState(fis.read())); + } + } else { + for (int index = 0; index < volume; index++) { + linear.setBlock(index, getBlockState(fis.readVarInt())); + } } } else { - for (int index = 0; index < volume; index++) { - BlockState state = BlockTypes.states[palette[fis.readVarInt()]]; - fc.setBlock(index, state); + if (palette.length < 128) { + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + clipboard.setBlock(x, y, z, getBlockState(fis.read())); + } + } + } + } else { + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + clipboard.setBlock(x, y, z, getBlockState(fis.readVarInt())); + } + } + } } } } } - if (biomesOut.getSize() != 0) { + if (biomesOut != null && biomesOut.getSize() != 0) { try (FaweInputStream fis = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(biomesOut.toByteArrays())))) { - int volume = width * length; - for (int index = 0; index < volume; index++) { - fc.setBiome(index, BiomeTypes.get(fis.read())); + if (clipboard instanceof LinearClipboard) { + LinearClipboard linear = (LinearClipboard) clipboard; + int volume = width * length; + for (int index = 0; index < volume; index++) { + linear.setBiome(index, getBiomeType(fis)); + } + } else { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + clipboard.setBiome(x, 0, z, getBiomeType(fis)); + } + } } } } - clipboard.init(region, fc); + // tiles + if (tiles != null && !tiles.isEmpty()) { + for (Map tileRaw : tiles) { + CompoundTag tile = FaweCache.IMP.asTag(tileRaw); + + int[] pos = tile.getIntArray("Pos"); + int x,y,z; + if (pos.length != 3) { + if (!tile.containsKey("x") || !tile.containsKey("y") || !tile.containsKey("z")) { + return null; + } + x = tile.getInt("x"); + y = tile.getInt("y"); + z = tile.getInt("z"); + } else { + x = pos[0]; + y = pos[1]; + z = pos[2]; + } + Map values = tile.getValue(); + Tag id = values.get("Id"); + if (id != null) { + values.put("x", new IntTag(x)); + values.put("y", new IntTag(y)); + values.put("z", new IntTag(z)); + values.put("id", id); + } + values.remove("Id"); + values.remove("Pos"); + + tile = fixBlockEntity(tile); + clipboard.setTile(x, y, z, tile); + } + } + + // entities + if (entities != null && !entities.isEmpty()) { + for (Map entRaw : entities) { + CompoundTag ent = FaweCache.IMP.asTag(entRaw); + + Map value = ent.getValue(); + StringTag id = (StringTag) value.get("Id"); + if (id == null) { + id = (StringTag) value.get("id"); + if (id == null) { + return null; + } + } + value.put("id", id); + value.remove("Id"); + + EntityType type = EntityTypes.parse(id.getValue()); + if (type != null) { + ent = fixEntity(ent); + BaseEntity state = new BaseEntity(type, ent); + Location loc = ent.getEntityLocation(clipboard); + clipboard.createEntity(loc, state); + } else { + Fawe.debug("Invalid entity: " + id); + } + } + } + clipboard.setOrigin(origin); return clipboard; } - /* - private Clipboard readVersion2(BlockArrayClipboard version1, CompoundTag schematicTag) throws IOException { - Map schematic = schematicTag.getValue(); - if (schematic.containsKey("BiomeData")) { - readBiomes(version1, schematic); - } - if (schematic.containsKey("Entities")) { - readEntities(version1, schematic); - } - return version1; - } - */ - - private void readBiomes(BlockArrayClipboard clipboard, Map schematic) throws IOException { - ByteArrayTag dataTag = requireTag(schematic, "BiomeData", ByteArrayTag.class); - IntTag maxTag = requireTag(schematic, "BiomePaletteMax", IntTag.class); - CompoundTag paletteTag = requireTag(schematic, "BiomePalette", CompoundTag.class); - - Map palette = new HashMap<>(); - if (maxTag.getValue() != paletteTag.getValue().size()) { - throw new IOException("Biome palette size does not match expected size."); - } - - for (Entry palettePart : paletteTag.getValue().entrySet()) { - String key = palettePart.getKey(); - if (fixer != null) { - key = fixer.fixUp(DataFixer.FixTypes.BIOME, key, dataVersion); - } - BiomeType biome = BiomeTypes.get(key); - if (biome == null) { - log.warn("Unknown biome type :" + key + - " in palette. Are you missing a mod or using a schematic made in a newer version of Minecraft?"); - } - Tag idTag = palettePart.getValue(); - if (!(idTag instanceof IntTag)) { - throw new IOException("Biome mapped to non-Int tag."); - } - palette.put(((IntTag) idTag).getValue(), biome); - } - - int width = clipboard.getDimensions().getX(); - - byte[] biomes = dataTag.getValue(); - int biomeIndex = 0; - int biomeJ = 0; - int bVal; - int varIntLength; - BlockVector2 min = clipboard.getMinimumPoint().toBlockVector2(); - while (biomeJ < biomes.length) { - bVal = 0; - varIntLength = 0; - - while (true) { - bVal |= (biomes[biomeJ] & 127) << (varIntLength++ * 7); - if (varIntLength > 5) { - throw new IOException("VarInt too big (probably corrupted data)"); - } - if (((biomes[biomeJ] & 128) != 128)) { - biomeJ++; - break; - } - biomeJ++; - } - int z = biomeIndex / width; - int x = biomeIndex % width; - BiomeType type = palette.get(bVal); - clipboard.setBiome(min.add(x, z), type); - biomeIndex++; - } - } - - /* - private void readEntities(BlockArrayClipboard clipboard, Map schematic) throws IOException { - List entList = requireTag(schematic, "Entities", ListTag.class).getValue(); - if (entList.isEmpty()) { - return; - } - for (Tag et : entList) { - if (!(et instanceof CompoundTag)) { - continue; - } - CompoundTag entityTag = (CompoundTag) et; - Map tags = entityTag.getValue(); - String id = requireTag(tags, "Id", StringTag.class).getValue(); - entityTag = entityTag.createBuilder().putString("id", id).remove("Id").build(); - - if (fixer != null) { - entityTag = fixer.fixUp(DataFixer.FixTypes.ENTITY, entityTag, dataVersion); - } - - EntityType entityType = EntityTypes.get(id); - if (entityType != null) { - Location location = NBTConversions.toLocation(clipboard, - requireTag(tags, "Pos", ListTag.class), - requireTag(tags, "Rotation", ListTag.class)); - BaseEntity state = new BaseEntity(entityType, entityTag); - clipboard.createEntity(location, state); - } else { - log.warn("Unknown entity when pasting schematic: " + id); - } - } - } - */ @Override public void close() throws IOException { inputStream.close(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java index 67bc219d6..34f0bd1ec 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java @@ -19,10 +19,7 @@ package com.sk89q.worldedit.extent.clipboard.io; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.boydti.fawe.jnbt.NBTStreamer; -import com.boydti.fawe.object.clipboard.FaweClipboard; +import com.boydti.fawe.jnbt.streamer.IntValueReader; import com.boydti.fawe.util.IOUtil; import com.google.common.collect.Maps; import com.sk89q.jnbt.CompoundTag; @@ -36,7 +33,6 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extension.platform.Capability; -import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; @@ -45,8 +41,11 @@ import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; 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.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutput; @@ -59,8 +58,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; -import net.jpountz.lz4.LZ4BlockInputStream; -import net.jpountz.lz4.LZ4BlockOutputStream; + +import static com.google.common.base.Preconditions.checkNotNull; /** * Writes schematic files using the Sponge schematic format. @@ -140,61 +139,32 @@ public class SpongeSchematicWriter implements ClipboardWriter { NBTOutputStream tilesOut = new NBTOutputStream(new LZ4BlockOutputStream(tilesCompressed)); List paletteList = new ArrayList<>(); - char[] palette = new char[BlockTypes.states.length]; + char[] palette = new char[BlockTypesCache.states.length]; Arrays.fill(palette, Character.MAX_VALUE); int[] paletteMax = {0}; + int numTiles = 0; + for (BlockVector3 pos : clipboard) { + BaseBlock block = pos.getFullBlock(clipboard); + CompoundTag nbt = block.getNbtData(); + if (nbt != null) { + Map values = nbt.getValue(); + values.remove("id"); // Remove 'id' if it exists. We want 'Id' - int[] numTiles = {0}; - FaweClipboard.BlockReader reader = new FaweClipboard.BlockReader() { - @Override - public > void run(int x, int y, int z, B block) { - try { - if (block.hasNbtData()) { - CompoundTag nbt = block.getNbtData(); - if (nbt != null) { - Map values = nbt.getValue(); - - values.remove("id"); // Remove 'id' if it exists. We want 'Id' - - // Positions are kept in NBT, we don't want that. - values.remove("x"); - values.remove("y"); - values.remove("z"); - if (!values.containsKey("Id")) { - values.put("Id", new StringTag(block.getNbtId())); - } - values.put("Pos", new IntArrayTag(new int[]{ - x, - y, - z - })); - numTiles[0]++; - tilesOut.writeTagPayload(block.getNbtData()); - } - } - int ordinal = block.getOrdinal(); - char value = palette[ordinal]; - if (value == Character.MAX_VALUE) { - int size = paletteMax[0]++; - palette[ordinal] = value = (char) size; - paletteList.add(ordinal); - } - IOUtil.writeVarInt(blocksOut, value); - } catch (IOException e) { - throw new RuntimeException(e); + // Positions are kept in NBT, we don't want that. + values.remove("x"); + values.remove("y"); + values.remove("z"); + if (!values.containsKey("Id")) { + values.put("Id", new StringTag(block.getNbtId())); } - } - }; - if (clipboard instanceof BlockArrayClipboard) { - ((BlockArrayClipboard) clipboard).IMP.forEach(reader, true); - } else { - for (BlockVector3 pt : region) { - BaseBlock block = clipboard.getFullBlock(pt); - int x = pt.getBlockX() - min.getBlockX(); - int y = pt.getBlockY() - min.getBlockY(); - int z = pt.getBlockZ() - min.getBlockY(); - reader.run(x, y, z, block); + values.put("Pos", new IntArrayTag(new int[]{ + pos.getX(), + pos.getY(), + pos.getZ() + })); + numTiles++; + tilesOut.writeTagPayload(block.getNbtData()); } } // close @@ -206,7 +176,7 @@ public class SpongeSchematicWriter implements ClipboardWriter { out.writeLazyCompoundTag("Palette", out12 -> { for (int i = 0; i < paletteList.size(); i++) { int stateOrdinal = paletteList.get(i); - BlockState state = BlockTypes.states[stateOrdinal]; + BlockState state = BlockTypesCache.states[stateOrdinal]; out12.writeNamedTag(state.getAsString(), i); } }); @@ -217,10 +187,10 @@ public class SpongeSchematicWriter implements ClipboardWriter { IOUtil.copy(in, rawStream); } - if (numTiles[0] != 0) { + if (numTiles != 0) { out.writeNamedTagName("TileEntities", NBTConstants.TYPE_LIST); rawStream.write(NBTConstants.TYPE_COMPOUND); - rawStream.writeInt(numTiles[0]); + rawStream.writeInt(numTiles); try (LZ4BlockInputStream in = new LZ4BlockInputStream(new ByteArrayInputStream(tilesCompressed.toByteArray()))) { IOUtil.copy(in, rawStream); } @@ -271,9 +241,9 @@ public class SpongeSchematicWriter implements ClipboardWriter { int[] palette = new int[BiomeTypes.getMaxId() + 1]; Arrays.fill(palette, Integer.MAX_VALUE); int[] paletteMax = {0}; - NBTStreamer.ByteReader task = new NBTStreamer.ByteReader() { + IntValueReader task = new IntValueReader() { @Override - public void run(int index, int ordinal) { + public void applyInt(int index, int ordinal) { try { int value = palette[ordinal]; if (value == Integer.MAX_VALUE) { @@ -287,20 +257,17 @@ public class SpongeSchematicWriter implements ClipboardWriter { } } }; - if (clipboard instanceof BlockArrayClipboard) { - ((BlockArrayClipboard) clipboard).IMP.streamBiomes(task); - } else { - BlockVector3 min = clipboard.getMinimumPoint(); - int width = clipboard.getRegion().getWidth(); - int length = clipboard.getRegion().getLength(); - for (int z = 0, i = 0; z < length; z++) { - int z0 = min.getBlockZ() + z; - for (int x = 0; x < width; x++, i++) { - int x0 = min.getBlockX() + x; - BlockVector2 pt = BlockVector2.at(x0, z0); - BiomeType biome = clipboard.getBiome(pt); - task.run(i, biome.getInternalId()); - } + System.out.println("TODO Optimize biome write"); + BlockVector3 min = clipboard.getMinimumPoint(); + int width = clipboard.getRegion().getWidth(); + int length = clipboard.getRegion().getLength(); + for (int z = 0, i = 0; z < length; z++) { + int z0 = min.getBlockZ() + z; + for (int x = 0; x < width; x++, i++) { + int x0 = min.getBlockX() + x; + BlockVector2 pt = BlockVector2.at(x0, z0); + BiomeType biome = clipboard.getBiome(pt); + task.applyInt(i, biome.getInternalId()); } } biomesOut.close(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java index e79c0ade6..739b19da9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java @@ -67,6 +67,8 @@ import java.util.List; import java.util.Locale; import java.util.Map; import javax.annotation.Nullable; + +import com.sk89q.worldedit.world.block.BlockTypesCache; import org.jetbrains.annotations.NotNull; /** @@ -468,14 +470,14 @@ public class BlockTransformExtent extends ResettableExtent { int internalId = state.getInternalId(); int maskedId = internalId & mask; - int newMaskedId = arr[maskedId >> BlockTypes.BIT_OFFSET]; + int newMaskedId = arr[maskedId >> BlockTypesCache.BIT_OFFSET]; if (newMaskedId != -1) { return BlockState.getFromInternalId(newMaskedId | (internalId & (~mask))); } newMaskedId = transformState(state, transform); - arr[maskedId >> BlockTypes.BIT_OFFSET] = newMaskedId & mask; + arr[maskedId >> BlockTypesCache.BIT_OFFSET] = newMaskedId & mask; return BlockState.getFromInternalId(newMaskedId); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/RegionFunction.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/RegionFunction.java index 332565e35..a5b448aea 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/RegionFunction.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/RegionFunction.java @@ -19,13 +19,15 @@ package com.sk89q.worldedit.function; +import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.implementation.filter.block.FilterBlock; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.math.BlockVector3; /** * Performs a function on points in a region. */ -public interface RegionFunction { +public interface RegionFunction extends Filter { /** * Apply the function to the given position. @@ -36,4 +38,9 @@ public interface RegionFunction { */ boolean apply(BlockVector3 position) throws WorldEditException; + + @Override + default void applyBlock(FilterBlock block) { + apply(block); + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/SchemGen.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/SchemGen.java index 018a907e5..3ff43d429 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/SchemGen.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/SchemGen.java @@ -1,6 +1,5 @@ package com.sk89q.worldedit.function.generator; -import com.boydti.fawe.object.schematic.Schematic; import com.sk89q.worldedit.math.MutableBlockVector3; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; @@ -45,12 +44,11 @@ public class SchemGen implements Resource { holder.setTransform(new AffineTransform().rotateY(ThreadLocalRandom.current().nextInt(4) * 90)); } Clipboard clipboard = holder.getClipboard(); - Schematic schematic = new Schematic(clipboard); Transform transform = holder.getTransform(); if (transform.isIdentity()) { - schematic.paste(extent, mutable, false); + clipboard.paste(extent, mutable, false); } else { - schematic.paste(extent, mutable, false, transform); + clipboard.paste(extent, mutable, false, transform); } mutable.mutY(y); return true; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ABlockMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ABlockMask.java index 0dcc944b4..9b5ea1aca 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ABlockMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ABlockMask.java @@ -5,6 +5,7 @@ import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import java.util.ArrayList; import java.util.List; @@ -19,7 +20,7 @@ public abstract class ABlockMask extends AbstractExtentMask { @Override public String toString() { List strings = new ArrayList<>(); - for (BlockType type : BlockTypes.values) { + for (BlockType type : BlockTypesCache.values) { if (type != null) { boolean hasAll; List all = type.getAllStates(); @@ -43,7 +44,7 @@ public abstract class ABlockMask extends AbstractExtentMask { if (mask instanceof ABlockMask) { ABlockMask other = (ABlockMask) mask; BlockMask newMask = new BlockMask(getExtent()); - for (BlockState state : BlockTypes.states) { + for (BlockState state : BlockTypesCache.states) { if (state != null) { if (test(state) && other.test(state)) { newMask.add(state); @@ -62,7 +63,7 @@ public abstract class ABlockMask extends AbstractExtentMask { if (mask instanceof ABlockMask) { ABlockMask other = (ABlockMask) mask; BlockMask newMask = new BlockMask(getExtent()); - for (BlockState state : BlockTypes.states) { + for (BlockState state : BlockTypesCache.states) { if (state != null) { if (test(state) || other.test(state)) { newMask.add(state); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java index 3920fd644..c36c5fb82 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java @@ -28,6 +28,7 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import java.util.Arrays; import java.util.Collection; @@ -51,7 +52,7 @@ public class BlockMask extends ABlockMask { } public BlockMask(Extent extent) { - this(extent, new boolean[BlockTypes.states.length]); + this(extent, new boolean[BlockTypesCache.states.length]); } public BlockMask(Extent extent, boolean[] ordinals) { @@ -86,7 +87,7 @@ public class BlockMask extends ABlockMask { public BlockMask add(Predicate predicate) { for (int i = 0; i < ordinals.length; i++) { if (!ordinals[i]) { - BlockState state = BlockTypes.states[i]; + BlockState state = BlockTypesCache.states[i]; if (state != null) { ordinals[i] = predicate.test(state); } @@ -221,7 +222,7 @@ public class BlockMask extends ABlockMask { BlockType unsetType = null; int totalTypes = 0; - for (BlockType type : BlockTypes.values) { + for (BlockType type : BlockTypesCache.values) { if (type != null) { totalTypes++; boolean hasAll = true; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMaskBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMaskBuilder.java index 3fd433e89..c84af2122 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMaskBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMaskBuilder.java @@ -14,6 +14,7 @@ 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.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import java.util.*; import java.util.function.BiPredicate; @@ -102,7 +103,7 @@ public class BlockMaskBuilder { } else { String regex = charSequence.toString(); blockTypeList = new ArrayList<>(); - for (BlockType myType : BlockTypes.values) { + for (BlockType myType : BlockTypesCache.values) { if (myType.getId().matches(regex)) { blockTypeList.add(myType); add(myType); @@ -209,7 +210,7 @@ public class BlockMaskBuilder { if (StringMan.isAlphanumericUnd(input)) { add(BlockTypes.parse(input)); } else { - for (BlockType myType : BlockTypes.values) { + for (BlockType myType : BlockTypesCache.values) { if (myType.getId().matches(input)) { add(myType); } @@ -223,7 +224,7 @@ public class BlockMaskBuilder { AbstractProperty prop = (AbstractProperty) property; long[] states = bitSets[type.getInternalId()]; if (states == null) return false; - int localI = index << prop.getBitOffset() >> BlockTypes.BIT_OFFSET; + int localI = index << prop.getBitOffset() >> BlockTypesCache.BIT_OFFSET; return (states == ALL || FastBitSet.get(states, localI)); } @@ -357,7 +358,7 @@ public class BlockMaskBuilder { for (AbstractProperty prop : properties) { List values = prop.getValues(); for (int j = 0; j < values.size(); j++) { - int localI = j << prop.getBitOffset() >> BlockTypes.BIT_OFFSET; + int localI = j << prop.getBitOffset() >> BlockTypesCache.BIT_OFFSET; if (states == ALL || FastBitSet.get(states, localI)) { if (!allowed.test(type, new AbstractMap.SimpleEntry(prop, values.get(j)))) { if (states == ALL) { @@ -435,7 +436,7 @@ public class BlockMaskBuilder { for (AbstractProperty prop : (List>) type.getProperties()) { List values = prop.getValues(); for (int j = 0; j < values.size(); j++) { - int localI = j << prop.getBitOffset() >> BlockTypes.BIT_OFFSET; + int localI = j << prop.getBitOffset() >> BlockTypesCache.BIT_OFFSET; if (states == null || !FastBitSet.get(states, localI)) { if (propPredicate.test(type, new AbstractMap.SimpleEntry(prop, values.get(j)))) { if (states == null) { @@ -457,7 +458,7 @@ public class BlockMaskBuilder { if (states == ALL) return this; List values = property.getValues(); - int localI = index << prop.getBitOffset() >> BlockTypes.BIT_OFFSET; + int localI = index << prop.getBitOffset() >> BlockTypesCache.BIT_OFFSET; if (states == null || !FastBitSet.get(states, localI)) { if (states == null) { bitSets[type.getInternalId()] = states = FastBitSet.create(type.getMaxStateId() + 1); @@ -473,7 +474,7 @@ public class BlockMaskBuilder { long[] states = bitSets[type.getInternalId()]; if (states == null) return this; List values = property.getValues(); - int localI = index << prop.getBitOffset() >> BlockTypes.BIT_OFFSET; + int localI = index << prop.getBitOffset() >> BlockTypesCache.BIT_OFFSET; if (states == ALL || FastBitSet.get(states, localI)) { if (states == ALL) { bitSets[type.getInternalId()] = states = FastBitSet.create(type.getMaxStateId() + 1); @@ -495,7 +496,7 @@ public class BlockMaskBuilder { } } else { for (int i = 0; i < values.size(); i++) { - int index = current.modifyIndex(state, i) >> BlockTypes.BIT_OFFSET; + int index = current.modifyIndex(state, i) >> BlockTypesCache.BIT_OFFSET; if (set) FastBitSet.set(states, index); else FastBitSet.clear(states, index); } @@ -543,7 +544,7 @@ public class BlockMaskBuilder { for (AbstractProperty prop : (List>) type.getProperties()) { List values = prop.getValues(); for (int j = 0; j < values.size(); j++) { - int localI = j << prop.getBitOffset() >> BlockTypes.BIT_OFFSET; + int localI = j << prop.getBitOffset() >> BlockTypesCache.BIT_OFFSET; if (FastBitSet.get(bitSet, localI)) set++; else clear++; } @@ -558,11 +559,11 @@ public class BlockMaskBuilder { private boolean[] getOrdinals() { if (ordinals == null) { - ordinals = new boolean[BlockTypes.states.length]; - for (int i = 0; i < BlockTypes.values.length; i++) { + ordinals = new boolean[BlockTypesCache.states.length]; + for (int i = 0; i < BlockTypesCache.values.length; i++) { long[] bitSet = bitSets[i]; if (bitSet == null) continue; - BlockType type = BlockTypes.values[i]; + BlockType type = BlockTypesCache.values[i]; if (bitSet == ALL) { for (BlockState state : type.getAllStates()) { ordinals[state.getOrdinal()] = true; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseMask.java index 4a22ef5fd..3999dfc44 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseMask.java @@ -2,7 +2,6 @@ package com.sk89q.worldedit.function.mask; import javax.annotation.Nullable; -import com.boydti.fawe.beta.FilterBlock; import com.sk89q.worldedit.math.BlockVector3; public class InverseMask extends AbstractMask { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockStateMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockStateMask.java index 024a61812..2ea0b3027 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockStateMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockStateMask.java @@ -1,13 +1,9 @@ package com.sk89q.worldedit.function.mask; -import com.boydti.fawe.beta.FilterBlock; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockTypes; - -import java.util.Arrays; public class InverseSingleBlockStateMask extends ABlockMask { private final char ordinal; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockTypeMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockTypeMask.java index c52de0587..ba327b84a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockTypeMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockTypeMask.java @@ -5,6 +5,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; public class InverseSingleBlockTypeMask extends ABlockMask { private final int internalId; @@ -26,7 +27,7 @@ public class InverseSingleBlockTypeMask extends ABlockMask { @Override public Mask inverse() { - return new SingleBlockTypeMask(getExtent(), BlockTypes.values[internalId]); + return new SingleBlockTypeMask(getExtent(), BlockTypesCache.values[internalId]); } public BlockType getBlockType() { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java index 43bb43346..c4dad4dc1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java @@ -20,7 +20,7 @@ package com.sk89q.worldedit.function.mask; import com.boydti.fawe.beta.Filter; -import com.boydti.fawe.beta.FilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.FilterBlock; import com.sk89q.worldedit.math.BlockVector3; import javax.annotation.Nullable; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskFilter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskFilter.java index 5e61405d5..6632ae17e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskFilter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskFilter.java @@ -1,8 +1,8 @@ package com.sk89q.worldedit.function.mask; -import com.boydti.fawe.beta.DelegateFilter; +import com.boydti.fawe.beta.implementation.filter.block.DelegateFilter; import com.boydti.fawe.beta.Filter; -import com.boydti.fawe.beta.FilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.FilterBlock; import java.util.function.Supplier; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockStateMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockStateMask.java index fa3655ccc..1ec097fcd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockStateMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockStateMask.java @@ -1,13 +1,9 @@ package com.sk89q.worldedit.function.mask; -import com.boydti.fawe.beta.FilterBlock; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockTypes; - -import java.util.Arrays; public class SingleBlockStateMask extends ABlockMask { private final char ordinal; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java index d493960bf..2ab4f2add 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java @@ -5,6 +5,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; public class SingleBlockTypeMask extends ABlockMask { private final int internalId; @@ -26,7 +27,7 @@ public class SingleBlockTypeMask extends ABlockMask { @Override public Mask inverse() { - return new InverseSingleBlockTypeMask(getExtent(), BlockTypes.values[internalId]); + return new InverseSingleBlockTypeMask(getExtent(), BlockTypesCache.values[internalId]); } public BlockType getBlockType() { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java index 60f6e0430..767820c31 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java @@ -34,6 +34,7 @@ import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.metadata.EntityProperties; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.CombinedRegionFunction; import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.RegionMaskTestFunction; @@ -300,7 +301,7 @@ public class ForwardExtentCopy implements Operation { new MaskTraverser(sourceMask).reset(transExt); copy = new RegionMaskingFilter(sourceMask, copy); } - if (copyingBiomes && (!(source instanceof BlockArrayClipboard) || ((BlockArrayClipboard) source).IMP.hasBiomes())) { + if (copyingBiomes && source.isWorld() || (source instanceof Clipboard && ((Clipboard) source).hasBiomes())) { copy = CombinedRegionFunction.combine(copy, new BiomeCopy(source, finalDest)); } blockCopy = new BackwardsExtentBlockCopy(region, from, transform, copy); @@ -354,7 +355,7 @@ public class ForwardExtentCopy implements Operation { if (maskFunc != null) copy = new RegionMaskTestFunction(sourceMask, copy, maskFunc); else copy = new RegionMaskingFilter(sourceMask, copy); } - if (copyingBiomes && (!(source instanceof BlockArrayClipboard) || ((BlockArrayClipboard) source).IMP.hasBiomes())) { + if (copyingBiomes && source.isWorld() || (source instanceof Clipboard && ((Clipboard) source).hasBiomes())) { copy = CombinedRegionFunction.combine(copy, new BiomeCopy(source, finalDest)); } blockCopy = new RegionVisitor(region, copy); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/Pattern.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/Pattern.java index 98e6bd271..8cb2800f0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/Pattern.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/Pattern.java @@ -20,7 +20,7 @@ package com.sk89q.worldedit.function.pattern; import com.boydti.fawe.beta.Filter; -import com.boydti.fawe.beta.FilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.FilterBlock; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; 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 b9f2bc2ba..d5ecc2b44 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 @@ -19,7 +19,6 @@ package com.sk89q.worldedit.function.pattern; -import com.boydti.fawe.beta.FilterBlock; import com.boydti.fawe.object.collection.RandomCollection; import com.boydti.fawe.object.random.SimpleRandom; import com.boydti.fawe.object.random.TrueRandom; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/WaterloggedRemover.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/WaterloggedRemover.java index 3ff8c65ac..886706f08 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/WaterloggedRemover.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/WaterloggedRemover.java @@ -27,6 +27,8 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; + import java.lang.ref.SoftReference; /** @@ -39,7 +41,7 @@ public class WaterloggedRemover extends AbstractExtentPattern { private synchronized BlockState[] getRemap() { BlockState[] remap = cache.get(); if (remap != null) return remap; - cache = new SoftReference<>(remap = new BlockState[BlockTypes.states.length]); + cache = new SoftReference<>(remap = new BlockState[BlockTypesCache.states.length]); // init for (int i = 0; i < remap.length; i++) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java index db39ad5e7..adf2d3f1c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java @@ -779,4 +779,9 @@ public abstract class BlockVector3 { return "(" + getX() + ", " + getY() + ", " + getZ() + ")"; } + //Used by VS fork + public BlockVector3 plus(BlockVector3 other) { + return add(other); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java index 58268c786..29fbc01b5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java @@ -23,27 +23,22 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import com.boydti.fawe.FaweCache; -import com.boydti.fawe.beta.ChunkFilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; import com.boydti.fawe.beta.Filter; -import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.BlockVectorSet; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.MutableBlockVector2; import com.sk89q.worldedit.math.MutableBlockVector3; -import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.storage.ChunkStore; import java.util.AbstractSet; -import java.util.Arrays; import java.util.Iterator; -import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import org.jetbrains.annotations.NotNull; @@ -675,6 +670,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { int bz = chunk.getZ() << 4; int tx = bx + 15; int tz = bz + 15; + if (bx >= minX && tx <= maxX && bz >= minZ && tz <= maxZ) { // contains all X/Z if (minY <= 0 && maxY >= 255) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java index 53f48baf9..c6f77597e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java @@ -21,7 +21,7 @@ package com.sk89q.worldedit.regions; import static com.google.common.base.Preconditions.checkNotNull; -import com.boydti.fawe.beta.ChunkFilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java index 4fbcc9577..896f3c701 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java @@ -20,7 +20,7 @@ package com.sk89q.worldedit.regions; -import com.boydti.fawe.beta.ChunkFilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java index 025428172..4d3a15d17 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java @@ -19,8 +19,7 @@ package com.sk89q.worldedit.regions; -import com.boydti.fawe.FaweCache; -import com.boydti.fawe.beta.ChunkFilterBlock; +import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.IChunk; @@ -28,16 +27,13 @@ import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.extent.SingleRegionExtent; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.world.World; -import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Set; import javax.annotation.Nullable; @@ -60,6 +56,10 @@ public interface Region extends Iterable, Cloneable, IBatchProcess */ BlockVector3 getMaximumPoint(); + default BlockVector3 getDimensions() { + return getMaximumPoint().subtract(getMinimumPoint()).add(1, 1, 1); + } + /** * Get the center point of a region. * Note: Coordinates will not be integers diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/AbstractProperty.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/AbstractProperty.java index 4c1028b5a..777e8afc9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/AbstractProperty.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/AbstractProperty.java @@ -23,6 +23,7 @@ import com.boydti.fawe.util.MathMan; import static com.google.common.base.Preconditions.checkState; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import javax.annotation.Nullable; import java.util.List; @@ -46,7 +47,7 @@ public abstract class AbstractProperty implements Property { this.name = name; this.values = values; this.numBits = MathMan.log2nlz(values.size()); - this.bitOffset = bitOffset + BlockTypes.BIT_OFFSET; + this.bitOffset = bitOffset + BlockTypesCache.BIT_OFFSET; this.bitMask = (((1 << numBits) - 1)) << this.bitOffset; this.bitMaskInverse = ~this.bitMask; this.key = PropertyKey.getOrCreate(name); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/PropertyGroup.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/PropertyGroup.java index 9079d8498..2441771b1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/PropertyGroup.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/PropertyGroup.java @@ -4,6 +4,7 @@ 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.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import java.util.ArrayList; import java.util.Arrays; @@ -57,7 +58,7 @@ public class PropertyGroup { public PropertyGroup build() { PropertyFunction[] states = new PropertyFunction[BlockTypes.size()]; Property prop; - for (BlockType type : BlockTypes.values) { + for (BlockType type : BlockTypesCache.values) { for (Object[] func : funcs) { if ((prop = type.getProperty((PropertyKey) func[0])) != null) { PropertyFunction pf = new PropertyFunction(prop, (Function) func[1], (Function) func[2]); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java index faf7db200..93e6b2b4a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java @@ -26,6 +26,8 @@ import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.math.transform.Identity; import com.sk89q.worldedit.math.transform.Transform; + +import java.io.Closeable; import java.util.Collections; import java.util.List; @@ -112,8 +114,8 @@ public class ClipboardHolder { } public void close() { - if (clipboard instanceof BlockArrayClipboard) { - ((BlockArrayClipboard) clipboard).close(); + if (clipboard != null) { + clipboard.close(); } clipboard = null; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/Request.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/Request.java index 8e588bb96..8c8c749c5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/Request.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/Request.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.session.request; +import com.boydti.fawe.object.collection.CleanableThreadLocal; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.extension.platform.Actor; @@ -26,13 +27,14 @@ import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.world.World; import javax.annotation.Nullable; +import java.util.List; /** * Describes the current request using a {@link ThreadLocal}. */ public final class Request { - private static final ThreadLocal threadLocal = ThreadLocal.withInitial(Request::new); + private static final CleanableThreadLocal threadLocal = new CleanableThreadLocal<>(Request::new); private @Nullable World world; private @Nullable Actor actor; @@ -44,6 +46,10 @@ public final class Request { private Request() { } + public static List getAll() { + return threadLocal.getAll(); + } + /** * Get the request world. * 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 e27a2c7ef..d7dc007c5 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 @@ -20,7 +20,8 @@ package com.sk89q.worldedit.world; import com.boydti.fawe.beta.IChunkGet; -import com.boydti.fawe.beta.implementation.NullChunkGet; +import com.boydti.fawe.beta.implementation.packet.ChunkPacket; +import com.boydti.fawe.beta.implementation.blocks.NullChunkGet; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; @@ -28,6 +29,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; @@ -148,7 +150,7 @@ public class NullWorld extends AbstractWorld { } @Override - public void sendChunk(int chunkX, int chunkZ, int bitMask) { + public void refreshChunk(int X, int Z) { } @@ -207,4 +209,8 @@ public class NullWorld extends AbstractWorld { return INSTANCE; } + @Override + public void sendFakeChunk(@Nullable Player player, ChunkPacket packet) { + + } } 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 8a066288a..95eb601ac 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 @@ -20,13 +20,15 @@ package com.sk89q.worldedit.world; import com.boydti.fawe.beta.IChunkGet; -import com.boydti.fawe.beta.implementation.IChunkCache; +import com.boydti.fawe.beta.implementation.packet.ChunkPacket; +import com.boydti.fawe.beta.IChunkCache; import com.boydti.fawe.object.extent.LightingExtent; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.Mask; @@ -300,8 +302,15 @@ public interface World extends Extent, Keyed, IChunkCache { * @param chunkZ * @param bitMask */ - void sendChunk(final int chunkX, final int chunkZ, final int bitMask); + void refreshChunk(final int X, final int Z); @Override IChunkGet get(int x, int z); + + /** + * Send a fake chunk to a player/s + * @param player may be null to send to everyone + * @param packet the chunk packet + */ + void sendFakeChunk(@Nullable Player player, ChunkPacket packet); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java index b91f19510..7d50e0739 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.world.block; -import com.boydti.fawe.beta.FilterBlock; import com.boydti.fawe.command.SuggestInputParseException; import com.boydti.fawe.object.string.MutableCharSequence; import com.boydti.fawe.util.StringMan; @@ -78,7 +77,7 @@ public class BlockState implements BlockStateHolder, FawePattern { @Deprecated public static BlockState getFromOrdinal(int ordinal) { - return BlockTypes.states[ordinal]; + return BlockTypesCache.states[ordinal]; } /** @@ -123,7 +122,7 @@ public class BlockState implements BlockStateHolder, FawePattern { type = BlockTypes.get(key); if (type == null) { String input = key.toString(); - throw new SuggestInputParseException("Does not match a valid block type: " + input, input, () -> Stream.of(BlockTypes.values) + throw new SuggestInputParseException("Does not match a valid block type: " + input, input, () -> Stream.of(BlockTypesCache.values) .filter(b -> StringMan.blockStateMatches(input, b.getId())) .map(BlockType::getId) .sorted(StringMan.blockStateComparator(input)) @@ -204,7 +203,7 @@ public class BlockState implements BlockStateHolder, FawePattern { continue; } } - return type.withPropertyId(stateId >> BlockTypes.BIT_OFFSET); + return type.withPropertyId(stateId >> BlockTypesCache.BIT_OFFSET); } @Override @@ -238,13 +237,13 @@ public class BlockState implements BlockStateHolder, FawePattern { @Deprecated @Override public final int getInternalPropertiesId() { - return this.getInternalId() >> BlockTypes.BIT_OFFSET; + return this.getInternalId() >> BlockTypesCache.BIT_OFFSET; } @Deprecated @Override public final int getInternalBlockTypeId() { - return this.getInternalId() & BlockTypes.BIT_MASK; + return this.getInternalId() & BlockTypesCache.BIT_MASK; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java index 421238f17..df3ba9827 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java @@ -51,7 +51,7 @@ public class BlockType implements FawePattern, Keyed { public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("block type"); private final String id; - private final BlockTypes.Settings settings; + private final BlockTypesCache.Settings settings; private boolean initItemType; private ItemType itemType; @@ -59,7 +59,7 @@ public class BlockType implements FawePattern, Keyed { protected BlockType(String id, int internalId, List states) { int i = id.indexOf("["); this.id = i == -1 ? id : id.substring(0, i); - this.settings = new BlockTypes.Settings(this, id, internalId, states); + this.settings = new BlockTypesCache.Settings(this, id, internalId, states); } @Deprecated @@ -105,12 +105,12 @@ public class BlockType implements FawePattern, Keyed { @Deprecated public BlockState withPropertyId(int propertyId) { if (settings.stateOrdinals == null) return settings.defaultState; - return BlockTypes.states[settings.stateOrdinals[propertyId]]; + return BlockTypesCache.states[settings.stateOrdinals[propertyId]]; } @Deprecated public BlockState withStateId(int internalStateId) { // - return this.withPropertyId(internalStateId >> BlockTypes.BIT_OFFSET); + return this.withPropertyId(internalStateId >> BlockTypesCache.BIT_OFFSET); } /** @@ -180,7 +180,7 @@ public class BlockType implements FawePattern, Keyed { */ public List getAllStates() { if (settings.stateOrdinals == null) return Collections.singletonList(getDefaultState()); - return IntStream.of(settings.stateOrdinals).filter(i -> i != -1).mapToObj(i -> BlockTypes.states[i]).collect(Collectors.toList()); + return IntStream.of(settings.stateOrdinals).filter(i -> i != -1).mapToObj(i -> BlockTypesCache.states[i]).collect(Collectors.toList()); } /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypeSwitchBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypeSwitchBuilder.java index 4d385e0b3..e31885305 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypeSwitchBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypeSwitchBuilder.java @@ -17,7 +17,7 @@ public class BlockTypeSwitchBuilder { } public BlockTypeSwitchBuilder add(Predicate predicate, T task) { - for (BlockType type : BlockTypes.values) { + for (BlockType type : BlockTypesCache.values) { if (predicate.test(type)) { this.runnables[type.getInternalId()] = task; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java index de2300b5f..2a43e629b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.world.block; import com.boydti.fawe.command.SuggestInputParseException; +import com.boydti.fawe.object.string.JoinedCharSequence; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.StringMan; @@ -55,921 +56,706 @@ import java.util.stream.Stream; */ public final class BlockTypes { // Doesn't really matter what the hardcoded values are, as FAWE will update it on load - @Nullable public static final BlockType __RESERVED__ = null; // Placeholder for null index (i.e. when block types are represented as primitives) - @Nullable public static final BlockType ACACIA_BUTTON = null; - @Nullable public static final BlockType ACACIA_DOOR = null; - @Nullable public static final BlockType ACACIA_FENCE = null; - @Nullable public static final BlockType ACACIA_FENCE_GATE = null; - @Nullable public static final BlockType ACACIA_LEAVES = null; - @Nullable public static final BlockType ACACIA_LOG = null; - @Nullable public static final BlockType ACACIA_PLANKS = null; - @Nullable public static final BlockType ACACIA_PRESSURE_PLATE = null; - @Nullable public static final BlockType ACACIA_SAPLING = null; - @Nullable public static final BlockType ACACIA_SIGN = null; - @Nullable public static final BlockType ACACIA_SLAB = null; - @Nullable public static final BlockType ACACIA_STAIRS = null; - @Nullable public static final BlockType ACACIA_TRAPDOOR = null; - @Nullable public static final BlockType ACACIA_WALL_SIGN = null; - @Nullable public static final BlockType ACACIA_WOOD = null; - @Nullable public static final BlockType ACTIVATOR_RAIL = null; - @Nullable public static final BlockType AIR = null; - @Nullable public static final BlockType ALLIUM = null; - @Nullable public static final BlockType ANDESITE = null; - @Nullable public static final BlockType ANDESITE_SLAB = null; - @Nullable public static final BlockType ANDESITE_STAIRS = null; - @Nullable public static final BlockType ANDESITE_WALL = null; - @Nullable public static final BlockType ANVIL = null; - @Nullable public static final BlockType ATTACHED_MELON_STEM = null; - @Nullable public static final BlockType ATTACHED_PUMPKIN_STEM = null; - @Nullable public static final BlockType AZURE_BLUET = null; - @Nullable public static final BlockType BAMBOO = null; - @Nullable public static final BlockType BAMBOO_SAPLING = null; - @Nullable public static final BlockType BARREL = null; - @Nullable public static final BlockType BARRIER = null; - @Nullable public static final BlockType BEACON = null; - @Nullable public static final BlockType BEDROCK = null; - @Nullable public static final BlockType BEETROOTS = null; - @Nullable public static final BlockType BELL = null; - @Nullable public static final BlockType BIRCH_BUTTON = null; - @Nullable public static final BlockType BIRCH_DOOR = null; - @Nullable public static final BlockType BIRCH_FENCE = null; - @Nullable public static final BlockType BIRCH_FENCE_GATE = null; - @Nullable public static final BlockType BIRCH_LEAVES = null; - @Nullable public static final BlockType BIRCH_LOG = null; - @Nullable public static final BlockType BIRCH_PLANKS = null; - @Nullable public static final BlockType BIRCH_PRESSURE_PLATE = null; - @Nullable public static final BlockType BIRCH_SAPLING = null; - @Nullable public static final BlockType BIRCH_SIGN = null; - @Nullable public static final BlockType BIRCH_SLAB = null; - @Nullable public static final BlockType BIRCH_STAIRS = null; - @Nullable public static final BlockType BIRCH_TRAPDOOR = null; - @Nullable public static final BlockType BIRCH_WALL_SIGN = null; - @Nullable public static final BlockType BIRCH_WOOD = null; - @Nullable public static final BlockType BLACK_BANNER = null; - @Nullable public static final BlockType BLACK_BED = null; - @Nullable public static final BlockType BLACK_CARPET = null; - @Nullable public static final BlockType BLACK_CONCRETE = null; - @Nullable public static final BlockType BLACK_CONCRETE_POWDER = null; - @Nullable public static final BlockType BLACK_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType BLACK_SHULKER_BOX = null; - @Nullable public static final BlockType BLACK_STAINED_GLASS = null; - @Nullable public static final BlockType BLACK_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType BLACK_TERRACOTTA = null; - @Nullable public static final BlockType BLACK_WALL_BANNER = null; - @Nullable public static final BlockType BLACK_WOOL = null; - @Nullable public static final BlockType BLAST_FURNACE = null; - @Nullable public static final BlockType BLUE_BANNER = null; - @Nullable public static final BlockType BLUE_BED = null; - @Nullable public static final BlockType BLUE_CARPET = null; - @Nullable public static final BlockType BLUE_CONCRETE = null; - @Nullable public static final BlockType BLUE_CONCRETE_POWDER = null; - @Nullable public static final BlockType BLUE_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType BLUE_ICE = null; - @Nullable public static final BlockType BLUE_ORCHID = null; - @Nullable public static final BlockType BLUE_SHULKER_BOX = null; - @Nullable public static final BlockType BLUE_STAINED_GLASS = null; - @Nullable public static final BlockType BLUE_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType BLUE_TERRACOTTA = null; - @Nullable public static final BlockType BLUE_WALL_BANNER = null; - @Nullable public static final BlockType BLUE_WOOL = null; - @Nullable public static final BlockType BONE_BLOCK = null; - @Nullable public static final BlockType BOOKSHELF = null; - @Nullable public static final BlockType BRAIN_CORAL = null; - @Nullable public static final BlockType BRAIN_CORAL_BLOCK = null; - @Nullable public static final BlockType BRAIN_CORAL_FAN = null; - @Nullable public static final BlockType BRAIN_CORAL_WALL_FAN = null; - @Nullable public static final BlockType BREWING_STAND = null; - @Nullable public static final BlockType BRICK_SLAB = null; - @Nullable public static final BlockType BRICK_STAIRS = null; - @Nullable public static final BlockType BRICK_WALL = null; - @Nullable public static final BlockType BRICKS = null; - @Nullable public static final BlockType BROWN_BANNER = null; - @Nullable public static final BlockType BROWN_BED = null; - @Nullable public static final BlockType BROWN_CARPET = null; - @Nullable public static final BlockType BROWN_CONCRETE = null; - @Nullable public static final BlockType BROWN_CONCRETE_POWDER = null; - @Nullable public static final BlockType BROWN_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType BROWN_MUSHROOM = null; - @Nullable public static final BlockType BROWN_MUSHROOM_BLOCK = null; - @Nullable public static final BlockType BROWN_SHULKER_BOX = null; - @Nullable public static final BlockType BROWN_STAINED_GLASS = null; - @Nullable public static final BlockType BROWN_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType BROWN_TERRACOTTA = null; - @Nullable public static final BlockType BROWN_WALL_BANNER = null; - @Nullable public static final BlockType BROWN_WOOL = null; - @Nullable public static final BlockType BUBBLE_COLUMN = null; - @Nullable public static final BlockType BUBBLE_CORAL = null; - @Nullable public static final BlockType BUBBLE_CORAL_BLOCK = null; - @Nullable public static final BlockType BUBBLE_CORAL_FAN = null; - @Nullable public static final BlockType BUBBLE_CORAL_WALL_FAN = null; - @Nullable public static final BlockType CACTUS = null; - @Nullable public static final BlockType CAKE = null; - @Nullable public static final BlockType CAMPFIRE = null; - @Nullable public static final BlockType CARROTS = null; - @Nullable public static final BlockType CARTOGRAPHY_TABLE = null; - @Nullable public static final BlockType CARVED_PUMPKIN = null; - @Nullable public static final BlockType CAULDRON = null; - @Nullable public static final BlockType CAVE_AIR = null; - @Nullable public static final BlockType CHAIN_COMMAND_BLOCK = null; - @Nullable public static final BlockType CHEST = null; - @Nullable public static final BlockType CHIPPED_ANVIL = null; - @Nullable public static final BlockType CHISELED_QUARTZ_BLOCK = null; - @Nullable public static final BlockType CHISELED_RED_SANDSTONE = null; - @Nullable public static final BlockType CHISELED_SANDSTONE = null; - @Nullable public static final BlockType CHISELED_STONE_BRICKS = null; - @Nullable public static final BlockType CHORUS_FLOWER = null; - @Nullable public static final BlockType CHORUS_PLANT = null; - @Nullable public static final BlockType CLAY = null; - @Nullable public static final BlockType COAL_BLOCK = null; - @Nullable public static final BlockType COAL_ORE = null; - @Nullable public static final BlockType COARSE_DIRT = null; - @Nullable public static final BlockType COBBLESTONE = null; - @Nullable public static final BlockType COBBLESTONE_SLAB = null; - @Nullable public static final BlockType COBBLESTONE_STAIRS = null; - @Nullable public static final BlockType COBBLESTONE_WALL = null; - @Nullable public static final BlockType COBWEB = null; - @Nullable public static final BlockType COCOA = null; - @Nullable public static final BlockType COMMAND_BLOCK = null; - @Nullable public static final BlockType COMPARATOR = null; - @Nullable public static final BlockType COMPOSTER = null; - @Nullable public static final BlockType CONDUIT = null; - @Nullable public static final BlockType CORNFLOWER = null; - @Nullable public static final BlockType CRACKED_STONE_BRICKS = null; - @Nullable public static final BlockType CRAFTING_TABLE = null; - @Nullable public static final BlockType CREEPER_HEAD = null; - @Nullable public static final BlockType CREEPER_WALL_HEAD = null; - @Nullable public static final BlockType CUT_RED_SANDSTONE = null; - @Nullable public static final BlockType CUT_RED_SANDSTONE_SLAB = null; - @Nullable public static final BlockType CUT_SANDSTONE = null; - @Nullable public static final BlockType CUT_SANDSTONE_SLAB = null; - @Nullable public static final BlockType CYAN_BANNER = null; - @Nullable public static final BlockType CYAN_BED = null; - @Nullable public static final BlockType CYAN_CARPET = null; - @Nullable public static final BlockType CYAN_CONCRETE = null; - @Nullable public static final BlockType CYAN_CONCRETE_POWDER = null; - @Nullable public static final BlockType CYAN_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType CYAN_SHULKER_BOX = null; - @Nullable public static final BlockType CYAN_STAINED_GLASS = null; - @Nullable public static final BlockType CYAN_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType CYAN_TERRACOTTA = null; - @Nullable public static final BlockType CYAN_WALL_BANNER = null; - @Nullable public static final BlockType CYAN_WOOL = null; - @Nullable public static final BlockType DAMAGED_ANVIL = null; - @Nullable public static final BlockType DANDELION = null; - @Nullable public static final BlockType DARK_OAK_BUTTON = null; - @Nullable public static final BlockType DARK_OAK_DOOR = null; - @Nullable public static final BlockType DARK_OAK_FENCE = null; - @Nullable public static final BlockType DARK_OAK_FENCE_GATE = null; - @Nullable public static final BlockType DARK_OAK_LEAVES = null; - @Nullable public static final BlockType DARK_OAK_LOG = null; - @Nullable public static final BlockType DARK_OAK_PLANKS = null; - @Nullable public static final BlockType DARK_OAK_PRESSURE_PLATE = null; - @Nullable public static final BlockType DARK_OAK_SAPLING = null; - @Nullable public static final BlockType DARK_OAK_SIGN = null; - @Nullable public static final BlockType DARK_OAK_SLAB = null; - @Nullable public static final BlockType DARK_OAK_STAIRS = null; - @Nullable public static final BlockType DARK_OAK_TRAPDOOR = null; - @Nullable public static final BlockType DARK_OAK_WALL_SIGN = null; - @Nullable public static final BlockType DARK_OAK_WOOD = null; - @Nullable public static final BlockType DARK_PRISMARINE = null; - @Nullable public static final BlockType DARK_PRISMARINE_SLAB = null; - @Nullable public static final BlockType DARK_PRISMARINE_STAIRS = null; - @Nullable public static final BlockType DAYLIGHT_DETECTOR = null; - @Nullable public static final BlockType DEAD_BRAIN_CORAL = null; - @Nullable public static final BlockType DEAD_BRAIN_CORAL_BLOCK = null; - @Nullable public static final BlockType DEAD_BRAIN_CORAL_FAN = null; - @Nullable public static final BlockType DEAD_BRAIN_CORAL_WALL_FAN = null; - @Nullable public static final BlockType DEAD_BUBBLE_CORAL = null; - @Nullable public static final BlockType DEAD_BUBBLE_CORAL_BLOCK = null; - @Nullable public static final BlockType DEAD_BUBBLE_CORAL_FAN = null; - @Nullable public static final BlockType DEAD_BUBBLE_CORAL_WALL_FAN = null; - @Nullable public static final BlockType DEAD_BUSH = null; - @Nullable public static final BlockType DEAD_FIRE_CORAL = null; - @Nullable public static final BlockType DEAD_FIRE_CORAL_BLOCK = null; - @Nullable public static final BlockType DEAD_FIRE_CORAL_FAN = null; - @Nullable public static final BlockType DEAD_FIRE_CORAL_WALL_FAN = null; - @Nullable public static final BlockType DEAD_HORN_CORAL = null; - @Nullable public static final BlockType DEAD_HORN_CORAL_BLOCK = null; - @Nullable public static final BlockType DEAD_HORN_CORAL_FAN = null; - @Nullable public static final BlockType DEAD_HORN_CORAL_WALL_FAN = null; - @Nullable public static final BlockType DEAD_TUBE_CORAL = null; - @Nullable public static final BlockType DEAD_TUBE_CORAL_BLOCK = null; - @Nullable public static final BlockType DEAD_TUBE_CORAL_FAN = null; - @Nullable public static final BlockType DEAD_TUBE_CORAL_WALL_FAN = null; - @Nullable public static final BlockType DETECTOR_RAIL = null; - @Nullable public static final BlockType DIAMOND_BLOCK = null; - @Nullable public static final BlockType DIAMOND_ORE = null; - @Nullable public static final BlockType DIORITE = null; - @Nullable public static final BlockType DIORITE_SLAB = null; - @Nullable public static final BlockType DIORITE_STAIRS = null; - @Nullable public static final BlockType DIORITE_WALL = null; - @Nullable public static final BlockType DIRT = null; - @Nullable public static final BlockType DISPENSER = null; - @Nullable public static final BlockType DRAGON_EGG = null; - @Nullable public static final BlockType DRAGON_HEAD = null; - @Nullable public static final BlockType DRAGON_WALL_HEAD = null; - @Nullable public static final BlockType DRIED_KELP_BLOCK = null; - @Nullable public static final BlockType DROPPER = null; - @Nullable public static final BlockType EMERALD_BLOCK = null; - @Nullable public static final BlockType EMERALD_ORE = null; - @Nullable public static final BlockType ENCHANTING_TABLE = null; - @Nullable public static final BlockType END_GATEWAY = null; - @Nullable public static final BlockType END_PORTAL = null; - @Nullable public static final BlockType END_PORTAL_FRAME = null; - @Nullable public static final BlockType END_ROD = null; - @Nullable public static final BlockType END_STONE = null; - @Nullable public static final BlockType END_STONE_BRICK_SLAB = null; - @Nullable public static final BlockType END_STONE_BRICK_STAIRS = null; - @Nullable public static final BlockType END_STONE_BRICK_WALL = null; - @Nullable public static final BlockType END_STONE_BRICKS = null; - @Nullable public static final BlockType ENDER_CHEST = null; - @Nullable public static final BlockType FARMLAND = null; - @Nullable public static final BlockType FERN = null; - @Nullable public static final BlockType FIRE = null; - @Nullable public static final BlockType FIRE_CORAL = null; - @Nullable public static final BlockType FIRE_CORAL_BLOCK = null; - @Nullable public static final BlockType FIRE_CORAL_FAN = null; - @Nullable public static final BlockType FIRE_CORAL_WALL_FAN = null; - @Nullable public static final BlockType FLETCHING_TABLE = null; - @Nullable public static final BlockType FLOWER_POT = null; - @Nullable public static final BlockType FROSTED_ICE = null; - @Nullable public static final BlockType FURNACE = null; - @Nullable public static final BlockType GLASS = null; - @Nullable public static final BlockType GLASS_PANE = null; - @Nullable public static final BlockType GLOWSTONE = null; - @Nullable public static final BlockType GOLD_BLOCK = null; - @Nullable public static final BlockType GOLD_ORE = null; - @Nullable public static final BlockType GRANITE = null; - @Nullable public static final BlockType GRANITE_SLAB = null; - @Nullable public static final BlockType GRANITE_STAIRS = null; - @Nullable public static final BlockType GRANITE_WALL = null; - @Nullable public static final BlockType GRASS = null; - @Nullable public static final BlockType GRASS_BLOCK = null; - @Nullable public static final BlockType GRASS_PATH = null; - @Nullable public static final BlockType GRAVEL = null; - @Nullable public static final BlockType GRAY_BANNER = null; - @Nullable public static final BlockType GRAY_BED = null; - @Nullable public static final BlockType GRAY_CARPET = null; - @Nullable public static final BlockType GRAY_CONCRETE = null; - @Nullable public static final BlockType GRAY_CONCRETE_POWDER = null; - @Nullable public static final BlockType GRAY_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType GRAY_SHULKER_BOX = null; - @Nullable public static final BlockType GRAY_STAINED_GLASS = null; - @Nullable public static final BlockType GRAY_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType GRAY_TERRACOTTA = null; - @Nullable public static final BlockType GRAY_WALL_BANNER = null; - @Nullable public static final BlockType GRAY_WOOL = null; - @Nullable public static final BlockType GREEN_BANNER = null; - @Nullable public static final BlockType GREEN_BED = null; - @Nullable public static final BlockType GREEN_CARPET = null; - @Nullable public static final BlockType GREEN_CONCRETE = null; - @Nullable public static final BlockType GREEN_CONCRETE_POWDER = null; - @Nullable public static final BlockType GREEN_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType GREEN_SHULKER_BOX = null; - @Nullable public static final BlockType GREEN_STAINED_GLASS = null; - @Nullable public static final BlockType GREEN_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType GREEN_TERRACOTTA = null; - @Nullable public static final BlockType GREEN_WALL_BANNER = null; - @Nullable public static final BlockType GREEN_WOOL = null; - @Nullable public static final BlockType GRINDSTONE = null; - @Nullable public static final BlockType HAY_BLOCK = null; - @Nullable public static final BlockType HEAVY_WEIGHTED_PRESSURE_PLATE = null; - @Nullable public static final BlockType HOPPER = null; - @Nullable public static final BlockType HORN_CORAL = null; - @Nullable public static final BlockType HORN_CORAL_BLOCK = null; - @Nullable public static final BlockType HORN_CORAL_FAN = null; - @Nullable public static final BlockType HORN_CORAL_WALL_FAN = null; - @Nullable public static final BlockType ICE = null; - @Nullable public static final BlockType INFESTED_CHISELED_STONE_BRICKS = null; - @Nullable public static final BlockType INFESTED_COBBLESTONE = null; - @Nullable public static final BlockType INFESTED_CRACKED_STONE_BRICKS = null; - @Nullable public static final BlockType INFESTED_MOSSY_STONE_BRICKS = null; - @Nullable public static final BlockType INFESTED_STONE = null; - @Nullable public static final BlockType INFESTED_STONE_BRICKS = null; - @Nullable public static final BlockType IRON_BARS = null; - @Nullable public static final BlockType IRON_BLOCK = null; - @Nullable public static final BlockType IRON_DOOR = null; - @Nullable public static final BlockType IRON_ORE = null; - @Nullable public static final BlockType IRON_TRAPDOOR = null; - @Nullable public static final BlockType JACK_O_LANTERN = null; - @Nullable public static final BlockType JIGSAW = null; - @Nullable public static final BlockType JUKEBOX = null; - @Nullable public static final BlockType JUNGLE_BUTTON = null; - @Nullable public static final BlockType JUNGLE_DOOR = null; - @Nullable public static final BlockType JUNGLE_FENCE = null; - @Nullable public static final BlockType JUNGLE_FENCE_GATE = null; - @Nullable public static final BlockType JUNGLE_LEAVES = null; - @Nullable public static final BlockType JUNGLE_LOG = null; - @Nullable public static final BlockType JUNGLE_PLANKS = null; - @Nullable public static final BlockType JUNGLE_PRESSURE_PLATE = null; - @Nullable public static final BlockType JUNGLE_SAPLING = null; - @Nullable public static final BlockType JUNGLE_SIGN = null; - @Nullable public static final BlockType JUNGLE_SLAB = null; - @Nullable public static final BlockType JUNGLE_STAIRS = null; - @Nullable public static final BlockType JUNGLE_TRAPDOOR = null; - @Nullable public static final BlockType JUNGLE_WALL_SIGN = null; - @Nullable public static final BlockType JUNGLE_WOOD = null; - @Nullable public static final BlockType KELP = null; - @Nullable public static final BlockType KELP_PLANT = null; - @Nullable public static final BlockType LADDER = null; - @Nullable public static final BlockType LANTERN = null; - @Nullable public static final BlockType LAPIS_BLOCK = null; - @Nullable public static final BlockType LAPIS_ORE = null; - @Nullable public static final BlockType LARGE_FERN = null; - @Nullable public static final BlockType LAVA = null; - @Nullable public static final BlockType LECTERN = null; - @Nullable public static final BlockType LEVER = null; - @Nullable public static final BlockType LIGHT_BLUE_BANNER = null; - @Nullable public static final BlockType LIGHT_BLUE_BED = null; - @Nullable public static final BlockType LIGHT_BLUE_CARPET = null; - @Nullable public static final BlockType LIGHT_BLUE_CONCRETE = null; - @Nullable public static final BlockType LIGHT_BLUE_CONCRETE_POWDER = null; - @Nullable public static final BlockType LIGHT_BLUE_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType LIGHT_BLUE_SHULKER_BOX = null; - @Nullable public static final BlockType LIGHT_BLUE_STAINED_GLASS = null; - @Nullable public static final BlockType LIGHT_BLUE_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType LIGHT_BLUE_TERRACOTTA = null; - @Nullable public static final BlockType LIGHT_BLUE_WALL_BANNER = null; - @Nullable public static final BlockType LIGHT_BLUE_WOOL = null; - @Nullable public static final BlockType LIGHT_GRAY_BANNER = null; - @Nullable public static final BlockType LIGHT_GRAY_BED = null; - @Nullable public static final BlockType LIGHT_GRAY_CARPET = null; - @Nullable public static final BlockType LIGHT_GRAY_CONCRETE = null; - @Nullable public static final BlockType LIGHT_GRAY_CONCRETE_POWDER = null; - @Nullable public static final BlockType LIGHT_GRAY_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType LIGHT_GRAY_SHULKER_BOX = null; - @Nullable public static final BlockType LIGHT_GRAY_STAINED_GLASS = null; - @Nullable public static final BlockType LIGHT_GRAY_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType LIGHT_GRAY_TERRACOTTA = null; - @Nullable public static final BlockType LIGHT_GRAY_WALL_BANNER = null; - @Nullable public static final BlockType LIGHT_GRAY_WOOL = null; - @Nullable public static final BlockType LIGHT_WEIGHTED_PRESSURE_PLATE = null; - @Nullable public static final BlockType LILAC = null; - @Nullable public static final BlockType LILY_OF_THE_VALLEY = null; - @Nullable public static final BlockType LILY_PAD = null; - @Nullable public static final BlockType LIME_BANNER = null; - @Nullable public static final BlockType LIME_BED = null; - @Nullable public static final BlockType LIME_CARPET = null; - @Nullable public static final BlockType LIME_CONCRETE = null; - @Nullable public static final BlockType LIME_CONCRETE_POWDER = null; - @Nullable public static final BlockType LIME_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType LIME_SHULKER_BOX = null; - @Nullable public static final BlockType LIME_STAINED_GLASS = null; - @Nullable public static final BlockType LIME_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType LIME_TERRACOTTA = null; - @Nullable public static final BlockType LIME_WALL_BANNER = null; - @Nullable public static final BlockType LIME_WOOL = null; - @Nullable public static final BlockType LOOM = null; - @Nullable public static final BlockType MAGENTA_BANNER = null; - @Nullable public static final BlockType MAGENTA_BED = null; - @Nullable public static final BlockType MAGENTA_CARPET = null; - @Nullable public static final BlockType MAGENTA_CONCRETE = null; - @Nullable public static final BlockType MAGENTA_CONCRETE_POWDER = null; - @Nullable public static final BlockType MAGENTA_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType MAGENTA_SHULKER_BOX = null; - @Nullable public static final BlockType MAGENTA_STAINED_GLASS = null; - @Nullable public static final BlockType MAGENTA_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType MAGENTA_TERRACOTTA = null; - @Nullable public static final BlockType MAGENTA_WALL_BANNER = null; - @Nullable public static final BlockType MAGENTA_WOOL = null; - @Nullable public static final BlockType MAGMA_BLOCK = null; - @Nullable public static final BlockType MELON = null; - @Nullable public static final BlockType MELON_STEM = null; - @Nullable public static final BlockType MOSSY_COBBLESTONE = null; - @Nullable public static final BlockType MOSSY_COBBLESTONE_SLAB = null; - @Nullable public static final BlockType MOSSY_COBBLESTONE_STAIRS = null; - @Nullable public static final BlockType MOSSY_COBBLESTONE_WALL = null; - @Nullable public static final BlockType MOSSY_STONE_BRICK_SLAB = null; - @Nullable public static final BlockType MOSSY_STONE_BRICK_STAIRS = null; - @Nullable public static final BlockType MOSSY_STONE_BRICK_WALL = null; - @Nullable public static final BlockType MOSSY_STONE_BRICKS = null; - @Nullable public static final BlockType MOVING_PISTON = null; - @Nullable public static final BlockType MUSHROOM_STEM = null; - @Nullable public static final BlockType MYCELIUM = null; - @Nullable public static final BlockType NETHER_BRICK_FENCE = null; - @Nullable public static final BlockType NETHER_BRICK_SLAB = null; - @Nullable public static final BlockType NETHER_BRICK_STAIRS = null; - @Nullable public static final BlockType NETHER_BRICK_WALL = null; - @Nullable public static final BlockType NETHER_BRICKS = null; - @Nullable public static final BlockType NETHER_PORTAL = null; - @Nullable public static final BlockType NETHER_QUARTZ_ORE = null; - @Nullable public static final BlockType NETHER_WART = null; - @Nullable public static final BlockType NETHER_WART_BLOCK = null; - @Nullable public static final BlockType NETHERRACK = null; - @Nullable public static final BlockType NOTE_BLOCK = null; - @Nullable public static final BlockType OAK_BUTTON = null; - @Nullable public static final BlockType OAK_DOOR = null; - @Nullable public static final BlockType OAK_FENCE = null; - @Nullable public static final BlockType OAK_FENCE_GATE = null; - @Nullable public static final BlockType OAK_LEAVES = null; - @Nullable public static final BlockType OAK_LOG = null; - @Nullable public static final BlockType OAK_PLANKS = null; - @Nullable public static final BlockType OAK_PRESSURE_PLATE = null; - @Nullable public static final BlockType OAK_SAPLING = null; - @Nullable public static final BlockType OAK_SIGN = null; - @Nullable public static final BlockType OAK_SLAB = null; - @Nullable public static final BlockType OAK_STAIRS = null; - @Nullable public static final BlockType OAK_TRAPDOOR = null; - @Nullable public static final BlockType OAK_WALL_SIGN = null; - @Nullable public static final BlockType OAK_WOOD = null; - @Nullable public static final BlockType OBSERVER = null; - @Nullable public static final BlockType OBSIDIAN = null; - @Nullable public static final BlockType ORANGE_BANNER = null; - @Nullable public static final BlockType ORANGE_BED = null; - @Nullable public static final BlockType ORANGE_CARPET = null; - @Nullable public static final BlockType ORANGE_CONCRETE = null; - @Nullable public static final BlockType ORANGE_CONCRETE_POWDER = null; - @Nullable public static final BlockType ORANGE_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType ORANGE_SHULKER_BOX = null; - @Nullable public static final BlockType ORANGE_STAINED_GLASS = null; - @Nullable public static final BlockType ORANGE_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType ORANGE_TERRACOTTA = null; - @Nullable public static final BlockType ORANGE_TULIP = null; - @Nullable public static final BlockType ORANGE_WALL_BANNER = null; - @Nullable public static final BlockType ORANGE_WOOL = null; - @Nullable public static final BlockType OXEYE_DAISY = null; - @Nullable public static final BlockType PACKED_ICE = null; - @Nullable public static final BlockType PEONY = null; - @Nullable public static final BlockType PETRIFIED_OAK_SLAB = null; - @Nullable public static final BlockType PINK_BANNER = null; - @Nullable public static final BlockType PINK_BED = null; - @Nullable public static final BlockType PINK_CARPET = null; - @Nullable public static final BlockType PINK_CONCRETE = null; - @Nullable public static final BlockType PINK_CONCRETE_POWDER = null; - @Nullable public static final BlockType PINK_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType PINK_SHULKER_BOX = null; - @Nullable public static final BlockType PINK_STAINED_GLASS = null; - @Nullable public static final BlockType PINK_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType PINK_TERRACOTTA = null; - @Nullable public static final BlockType PINK_TULIP = null; - @Nullable public static final BlockType PINK_WALL_BANNER = null; - @Nullable public static final BlockType PINK_WOOL = null; - @Nullable public static final BlockType PISTON = null; - @Nullable public static final BlockType PISTON_HEAD = null; - @Nullable public static final BlockType PLAYER_HEAD = null; - @Nullable public static final BlockType PLAYER_WALL_HEAD = null; - @Nullable public static final BlockType PODZOL = null; - @Nullable public static final BlockType POLISHED_ANDESITE = null; - @Nullable public static final BlockType POLISHED_ANDESITE_SLAB = null; - @Nullable public static final BlockType POLISHED_ANDESITE_STAIRS = null; - @Nullable public static final BlockType POLISHED_DIORITE = null; - @Nullable public static final BlockType POLISHED_DIORITE_SLAB = null; - @Nullable public static final BlockType POLISHED_DIORITE_STAIRS = null; - @Nullable public static final BlockType POLISHED_GRANITE = null; - @Nullable public static final BlockType POLISHED_GRANITE_SLAB = null; - @Nullable public static final BlockType POLISHED_GRANITE_STAIRS = null; - @Nullable public static final BlockType POPPY = null; - @Nullable public static final BlockType POTATOES = null; - @Nullable public static final BlockType POTTED_ACACIA_SAPLING = null; - @Nullable public static final BlockType POTTED_ALLIUM = null; - @Nullable public static final BlockType POTTED_AZURE_BLUET = null; - @Nullable public static final BlockType POTTED_BAMBOO = null; - @Nullable public static final BlockType POTTED_BIRCH_SAPLING = null; - @Nullable public static final BlockType POTTED_BLUE_ORCHID = null; - @Nullable public static final BlockType POTTED_BROWN_MUSHROOM = null; - @Nullable public static final BlockType POTTED_CACTUS = null; - @Nullable public static final BlockType POTTED_CORNFLOWER = null; - @Nullable public static final BlockType POTTED_DANDELION = null; - @Nullable public static final BlockType POTTED_DARK_OAK_SAPLING = null; - @Nullable public static final BlockType POTTED_DEAD_BUSH = null; - @Nullable public static final BlockType POTTED_FERN = null; - @Nullable public static final BlockType POTTED_JUNGLE_SAPLING = null; - @Nullable public static final BlockType POTTED_LILY_OF_THE_VALLEY = null; - @Nullable public static final BlockType POTTED_OAK_SAPLING = null; - @Nullable public static final BlockType POTTED_ORANGE_TULIP = null; - @Nullable public static final BlockType POTTED_OXEYE_DAISY = null; - @Nullable public static final BlockType POTTED_PINK_TULIP = null; - @Nullable public static final BlockType POTTED_POPPY = null; - @Nullable public static final BlockType POTTED_RED_MUSHROOM = null; - @Nullable public static final BlockType POTTED_RED_TULIP = null; - @Nullable public static final BlockType POTTED_SPRUCE_SAPLING = null; - @Nullable public static final BlockType POTTED_WHITE_TULIP = null; - @Nullable public static final BlockType POTTED_WITHER_ROSE = null; - @Nullable public static final BlockType POWERED_RAIL = null; - @Nullable public static final BlockType PRISMARINE = null; - @Nullable public static final BlockType PRISMARINE_BRICK_SLAB = null; - @Nullable public static final BlockType PRISMARINE_BRICK_STAIRS = null; - @Nullable public static final BlockType PRISMARINE_BRICKS = null; - @Nullable public static final BlockType PRISMARINE_SLAB = null; - @Nullable public static final BlockType PRISMARINE_STAIRS = null; - @Nullable public static final BlockType PRISMARINE_WALL = null; - @Nullable public static final BlockType PUMPKIN = null; - @Nullable public static final BlockType PUMPKIN_STEM = null; - @Nullable public static final BlockType PURPLE_BANNER = null; - @Nullable public static final BlockType PURPLE_BED = null; - @Nullable public static final BlockType PURPLE_CARPET = null; - @Nullable public static final BlockType PURPLE_CONCRETE = null; - @Nullable public static final BlockType PURPLE_CONCRETE_POWDER = null; - @Nullable public static final BlockType PURPLE_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType PURPLE_SHULKER_BOX = null; - @Nullable public static final BlockType PURPLE_STAINED_GLASS = null; - @Nullable public static final BlockType PURPLE_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType PURPLE_TERRACOTTA = null; - @Nullable public static final BlockType PURPLE_WALL_BANNER = null; - @Nullable public static final BlockType PURPLE_WOOL = null; - @Nullable public static final BlockType PURPUR_BLOCK = null; - @Nullable public static final BlockType PURPUR_PILLAR = null; - @Nullable public static final BlockType PURPUR_SLAB = null; - @Nullable public static final BlockType PURPUR_STAIRS = null; - @Nullable public static final BlockType QUARTZ_BLOCK = null; - @Nullable public static final BlockType QUARTZ_PILLAR = null; - @Nullable public static final BlockType QUARTZ_SLAB = null; - @Nullable public static final BlockType QUARTZ_STAIRS = null; - @Nullable public static final BlockType RAIL = null; - @Nullable public static final BlockType RED_BANNER = null; - @Nullable public static final BlockType RED_BED = null; - @Nullable public static final BlockType RED_CARPET = null; - @Nullable public static final BlockType RED_CONCRETE = null; - @Nullable public static final BlockType RED_CONCRETE_POWDER = null; - @Nullable public static final BlockType RED_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType RED_MUSHROOM = null; - @Nullable public static final BlockType RED_MUSHROOM_BLOCK = null; - @Nullable public static final BlockType RED_NETHER_BRICK_SLAB = null; - @Nullable public static final BlockType RED_NETHER_BRICK_STAIRS = null; - @Nullable public static final BlockType RED_NETHER_BRICK_WALL = null; - @Nullable public static final BlockType RED_NETHER_BRICKS = null; - @Nullable public static final BlockType RED_SAND = null; - @Nullable public static final BlockType RED_SANDSTONE = null; - @Nullable public static final BlockType RED_SANDSTONE_SLAB = null; - @Nullable public static final BlockType RED_SANDSTONE_STAIRS = null; - @Nullable public static final BlockType RED_SANDSTONE_WALL = null; - @Nullable public static final BlockType RED_SHULKER_BOX = null; - @Nullable public static final BlockType RED_STAINED_GLASS = null; - @Nullable public static final BlockType RED_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType RED_TERRACOTTA = null; - @Nullable public static final BlockType RED_TULIP = null; - @Nullable public static final BlockType RED_WALL_BANNER = null; - @Nullable public static final BlockType RED_WOOL = null; - @Nullable public static final BlockType REDSTONE_BLOCK = null; - @Nullable public static final BlockType REDSTONE_LAMP = null; - @Nullable public static final BlockType REDSTONE_ORE = null; - @Nullable public static final BlockType REDSTONE_TORCH = null; - @Nullable public static final BlockType REDSTONE_WALL_TORCH = null; - @Nullable public static final BlockType REDSTONE_WIRE = null; - @Nullable public static final BlockType REPEATER = null; - @Nullable public static final BlockType REPEATING_COMMAND_BLOCK = null; - @Nullable public static final BlockType ROSE_BUSH = null; - @Nullable public static final BlockType SAND = null; - @Nullable public static final BlockType SANDSTONE = null; - @Nullable public static final BlockType SANDSTONE_SLAB = null; - @Nullable public static final BlockType SANDSTONE_STAIRS = null; - @Nullable public static final BlockType SANDSTONE_WALL = null; - @Nullable public static final BlockType SCAFFOLDING = null; - @Nullable public static final BlockType SEA_LANTERN = null; - @Nullable public static final BlockType SEA_PICKLE = null; - @Nullable public static final BlockType SEAGRASS = null; - @Nullable public static final BlockType SHULKER_BOX = null; - @Nullable public static final BlockType SKELETON_SKULL = null; - @Nullable public static final BlockType SKELETON_WALL_SKULL = null; - @Nullable public static final BlockType SLIME_BLOCK = null; - @Nullable public static final BlockType SMITHING_TABLE = null; - @Nullable public static final BlockType SMOKER = null; - @Nullable public static final BlockType SMOOTH_QUARTZ = null; - @Nullable public static final BlockType SMOOTH_QUARTZ_SLAB = null; - @Nullable public static final BlockType SMOOTH_QUARTZ_STAIRS = null; - @Nullable public static final BlockType SMOOTH_RED_SANDSTONE = null; - @Nullable public static final BlockType SMOOTH_RED_SANDSTONE_SLAB = null; - @Nullable public static final BlockType SMOOTH_RED_SANDSTONE_STAIRS = null; - @Nullable public static final BlockType SMOOTH_SANDSTONE = null; - @Nullable public static final BlockType SMOOTH_SANDSTONE_SLAB = null; - @Nullable public static final BlockType SMOOTH_SANDSTONE_STAIRS = null; - @Nullable public static final BlockType SMOOTH_STONE = null; - @Nullable public static final BlockType SMOOTH_STONE_SLAB = null; - @Nullable public static final BlockType SNOW = null; - @Nullable public static final BlockType SNOW_BLOCK = null; - @Nullable public static final BlockType SOUL_SAND = null; - @Nullable public static final BlockType SPAWNER = null; - @Nullable public static final BlockType SPONGE = null; - @Nullable public static final BlockType SPRUCE_BUTTON = null; - @Nullable public static final BlockType SPRUCE_DOOR = null; - @Nullable public static final BlockType SPRUCE_FENCE = null; - @Nullable public static final BlockType SPRUCE_FENCE_GATE = null; - @Nullable public static final BlockType SPRUCE_LEAVES = null; - @Nullable public static final BlockType SPRUCE_LOG = null; - @Nullable public static final BlockType SPRUCE_PLANKS = null; - @Nullable public static final BlockType SPRUCE_PRESSURE_PLATE = null; - @Nullable public static final BlockType SPRUCE_SAPLING = null; - @Nullable public static final BlockType SPRUCE_SIGN = null; - @Nullable public static final BlockType SPRUCE_SLAB = null; - @Nullable public static final BlockType SPRUCE_STAIRS = null; - @Nullable public static final BlockType SPRUCE_TRAPDOOR = null; - @Nullable public static final BlockType SPRUCE_WALL_SIGN = null; - @Nullable public static final BlockType SPRUCE_WOOD = null; - @Nullable public static final BlockType STICKY_PISTON = null; - @Nullable public static final BlockType STONE = null; - @Nullable public static final BlockType STONE_BRICK_SLAB = null; - @Nullable public static final BlockType STONE_BRICK_STAIRS = null; - @Nullable public static final BlockType STONE_BRICK_WALL = null; - @Nullable public static final BlockType STONE_BRICKS = null; - @Nullable public static final BlockType STONE_BUTTON = null; - @Nullable public static final BlockType STONE_PRESSURE_PLATE = null; - @Nullable public static final BlockType STONE_SLAB = null; - @Nullable public static final BlockType STONE_STAIRS = null; - @Nullable public static final BlockType STONECUTTER = null; - @Nullable public static final BlockType STRIPPED_ACACIA_LOG = null; - @Nullable public static final BlockType STRIPPED_ACACIA_WOOD = null; - @Nullable public static final BlockType STRIPPED_BIRCH_LOG = null; - @Nullable public static final BlockType STRIPPED_BIRCH_WOOD = null; - @Nullable public static final BlockType STRIPPED_DARK_OAK_LOG = null; - @Nullable public static final BlockType STRIPPED_DARK_OAK_WOOD = null; - @Nullable public static final BlockType STRIPPED_JUNGLE_LOG = null; - @Nullable public static final BlockType STRIPPED_JUNGLE_WOOD = null; - @Nullable public static final BlockType STRIPPED_OAK_LOG = null; - @Nullable public static final BlockType STRIPPED_OAK_WOOD = null; - @Nullable public static final BlockType STRIPPED_SPRUCE_LOG = null; - @Nullable public static final BlockType STRIPPED_SPRUCE_WOOD = null; - @Nullable public static final BlockType STRUCTURE_BLOCK = null; - @Nullable public static final BlockType STRUCTURE_VOID = null; - @Nullable public static final BlockType SUGAR_CANE = null; - @Nullable public static final BlockType SUNFLOWER = null; - @Nullable public static final BlockType SWEET_BERRY_BUSH = null; - @Nullable public static final BlockType TALL_GRASS = null; - @Nullable public static final BlockType TALL_SEAGRASS = null; - @Nullable public static final BlockType TERRACOTTA = null; - @Nullable public static final BlockType TNT = null; - @Nullable public static final BlockType TORCH = null; - @Nullable public static final BlockType TRAPPED_CHEST = null; - @Nullable public static final BlockType TRIPWIRE = null; - @Nullable public static final BlockType TRIPWIRE_HOOK = null; - @Nullable public static final BlockType TUBE_CORAL = null; - @Nullable public static final BlockType TUBE_CORAL_BLOCK = null; - @Nullable public static final BlockType TUBE_CORAL_FAN = null; - @Nullable public static final BlockType TUBE_CORAL_WALL_FAN = null; - @Nullable public static final BlockType TURTLE_EGG = null; - @Nullable public static final BlockType VINE = null; - @Nullable public static final BlockType VOID_AIR = null; - @Nullable public static final BlockType WALL_TORCH = null; - @Nullable public static final BlockType WATER = null; - @Nullable public static final BlockType WET_SPONGE = null; - @Nullable public static final BlockType WHEAT = null; - @Nullable public static final BlockType WHITE_BANNER = null; - @Nullable public static final BlockType WHITE_BED = null; - @Nullable public static final BlockType WHITE_CARPET = null; - @Nullable public static final BlockType WHITE_CONCRETE = null; - @Nullable public static final BlockType WHITE_CONCRETE_POWDER = null; - @Nullable public static final BlockType WHITE_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType WHITE_SHULKER_BOX = null; - @Nullable public static final BlockType WHITE_STAINED_GLASS = null; - @Nullable public static final BlockType WHITE_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType WHITE_TERRACOTTA = null; - @Nullable public static final BlockType WHITE_TULIP = null; - @Nullable public static final BlockType WHITE_WALL_BANNER = null; - @Nullable public static final BlockType WHITE_WOOL = null; - @Nullable public static final BlockType WITHER_ROSE = null; - @Nullable public static final BlockType WITHER_SKELETON_SKULL = null; - @Nullable public static final BlockType WITHER_SKELETON_WALL_SKULL = null; - @Nullable public static final BlockType YELLOW_BANNER = null; - @Nullable public static final BlockType YELLOW_BED = null; - @Nullable public static final BlockType YELLOW_CARPET = null; - @Nullable public static final BlockType YELLOW_CONCRETE = null; - @Nullable public static final BlockType YELLOW_CONCRETE_POWDER = null; - @Nullable public static final BlockType YELLOW_GLAZED_TERRACOTTA = null; - @Nullable public static final BlockType YELLOW_SHULKER_BOX = null; - @Nullable public static final BlockType YELLOW_STAINED_GLASS = null; - @Nullable public static final BlockType YELLOW_STAINED_GLASS_PANE = null; - @Nullable public static final BlockType YELLOW_TERRACOTTA = null; - @Nullable public static final BlockType YELLOW_WALL_BANNER = null; - @Nullable public static final BlockType YELLOW_WOOL = null; - @Nullable public static final BlockType ZOMBIE_HEAD = null; - @Nullable public static final BlockType ZOMBIE_WALL_HEAD = null; + @Nullable public static final BlockType __RESERVED__ = init(); // Placeholder for null index (i.e. when block types are represented as primitives) + @Nullable public static final BlockType ACACIA_BUTTON = init(); + @Nullable public static final BlockType ACACIA_DOOR = init(); + @Nullable public static final BlockType ACACIA_FENCE = init(); + @Nullable public static final BlockType ACACIA_FENCE_GATE = init(); + @Nullable public static final BlockType ACACIA_LEAVES = init(); + @Nullable public static final BlockType ACACIA_LOG = init(); + @Nullable public static final BlockType ACACIA_PLANKS = init(); + @Nullable public static final BlockType ACACIA_PRESSURE_PLATE = init(); + @Nullable public static final BlockType ACACIA_SAPLING = init(); + @Nullable public static final BlockType ACACIA_SIGN = init(); + @Nullable public static final BlockType ACACIA_SLAB = init(); + @Nullable public static final BlockType ACACIA_STAIRS = init(); + @Nullable public static final BlockType ACACIA_TRAPDOOR = init(); + @Nullable public static final BlockType ACACIA_WALL_SIGN = init(); + @Nullable public static final BlockType ACACIA_WOOD = init(); + @Nullable public static final BlockType ACTIVATOR_RAIL = init(); + @Nullable public static final BlockType AIR = init(); + @Nullable public static final BlockType ALLIUM = init(); + @Nullable public static final BlockType ANDESITE = init(); + @Nullable public static final BlockType ANDESITE_SLAB = init(); + @Nullable public static final BlockType ANDESITE_STAIRS = init(); + @Nullable public static final BlockType ANDESITE_WALL = init(); + @Nullable public static final BlockType ANVIL = init(); + @Nullable public static final BlockType ATTACHED_MELON_STEM = init(); + @Nullable public static final BlockType ATTACHED_PUMPKIN_STEM = init(); + @Nullable public static final BlockType AZURE_BLUET = init(); + @Nullable public static final BlockType BAMBOO = init(); + @Nullable public static final BlockType BAMBOO_SAPLING = init(); + @Nullable public static final BlockType BARREL = init(); + @Nullable public static final BlockType BARRIER = init(); + @Nullable public static final BlockType BEACON = init(); + @Nullable public static final BlockType BEDROCK = init(); + @Nullable public static final BlockType BEETROOTS = init(); + @Nullable public static final BlockType BELL = init(); + @Nullable public static final BlockType BIRCH_BUTTON = init(); + @Nullable public static final BlockType BIRCH_DOOR = init(); + @Nullable public static final BlockType BIRCH_FENCE = init(); + @Nullable public static final BlockType BIRCH_FENCE_GATE = init(); + @Nullable public static final BlockType BIRCH_LEAVES = init(); + @Nullable public static final BlockType BIRCH_LOG = init(); + @Nullable public static final BlockType BIRCH_PLANKS = init(); + @Nullable public static final BlockType BIRCH_PRESSURE_PLATE = init(); + @Nullable public static final BlockType BIRCH_SAPLING = init(); + @Nullable public static final BlockType BIRCH_SIGN = init(); + @Nullable public static final BlockType BIRCH_SLAB = init(); + @Nullable public static final BlockType BIRCH_STAIRS = init(); + @Nullable public static final BlockType BIRCH_TRAPDOOR = init(); + @Nullable public static final BlockType BIRCH_WALL_SIGN = init(); + @Nullable public static final BlockType BIRCH_WOOD = init(); + @Nullable public static final BlockType BLACK_BANNER = init(); + @Nullable public static final BlockType BLACK_BED = init(); + @Nullable public static final BlockType BLACK_CARPET = init(); + @Nullable public static final BlockType BLACK_CONCRETE = init(); + @Nullable public static final BlockType BLACK_CONCRETE_POWDER = init(); + @Nullable public static final BlockType BLACK_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType BLACK_SHULKER_BOX = init(); + @Nullable public static final BlockType BLACK_STAINED_GLASS = init(); + @Nullable public static final BlockType BLACK_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType BLACK_TERRACOTTA = init(); + @Nullable public static final BlockType BLACK_WALL_BANNER = init(); + @Nullable public static final BlockType BLACK_WOOL = init(); + @Nullable public static final BlockType BLAST_FURNACE = init(); + @Nullable public static final BlockType BLUE_BANNER = init(); + @Nullable public static final BlockType BLUE_BED = init(); + @Nullable public static final BlockType BLUE_CARPET = init(); + @Nullable public static final BlockType BLUE_CONCRETE = init(); + @Nullable public static final BlockType BLUE_CONCRETE_POWDER = init(); + @Nullable public static final BlockType BLUE_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType BLUE_ICE = init(); + @Nullable public static final BlockType BLUE_ORCHID = init(); + @Nullable public static final BlockType BLUE_SHULKER_BOX = init(); + @Nullable public static final BlockType BLUE_STAINED_GLASS = init(); + @Nullable public static final BlockType BLUE_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType BLUE_TERRACOTTA = init(); + @Nullable public static final BlockType BLUE_WALL_BANNER = init(); + @Nullable public static final BlockType BLUE_WOOL = init(); + @Nullable public static final BlockType BONE_BLOCK = init(); + @Nullable public static final BlockType BOOKSHELF = init(); + @Nullable public static final BlockType BRAIN_CORAL = init(); + @Nullable public static final BlockType BRAIN_CORAL_BLOCK = init(); + @Nullable public static final BlockType BRAIN_CORAL_FAN = init(); + @Nullable public static final BlockType BRAIN_CORAL_WALL_FAN = init(); + @Nullable public static final BlockType BREWING_STAND = init(); + @Nullable public static final BlockType BRICK_SLAB = init(); + @Nullable public static final BlockType BRICK_STAIRS = init(); + @Nullable public static final BlockType BRICK_WALL = init(); + @Nullable public static final BlockType BRICKS = init(); + @Nullable public static final BlockType BROWN_BANNER = init(); + @Nullable public static final BlockType BROWN_BED = init(); + @Nullable public static final BlockType BROWN_CARPET = init(); + @Nullable public static final BlockType BROWN_CONCRETE = init(); + @Nullable public static final BlockType BROWN_CONCRETE_POWDER = init(); + @Nullable public static final BlockType BROWN_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType BROWN_MUSHROOM = init(); + @Nullable public static final BlockType BROWN_MUSHROOM_BLOCK = init(); + @Nullable public static final BlockType BROWN_SHULKER_BOX = init(); + @Nullable public static final BlockType BROWN_STAINED_GLASS = init(); + @Nullable public static final BlockType BROWN_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType BROWN_TERRACOTTA = init(); + @Nullable public static final BlockType BROWN_WALL_BANNER = init(); + @Nullable public static final BlockType BROWN_WOOL = init(); + @Nullable public static final BlockType BUBBLE_COLUMN = init(); + @Nullable public static final BlockType BUBBLE_CORAL = init(); + @Nullable public static final BlockType BUBBLE_CORAL_BLOCK = init(); + @Nullable public static final BlockType BUBBLE_CORAL_FAN = init(); + @Nullable public static final BlockType BUBBLE_CORAL_WALL_FAN = init(); + @Nullable public static final BlockType CACTUS = init(); + @Nullable public static final BlockType CAKE = init(); + @Nullable public static final BlockType CAMPFIRE = init(); + @Nullable public static final BlockType CARROTS = init(); + @Nullable public static final BlockType CARTOGRAPHY_TABLE = init(); + @Nullable public static final BlockType CARVED_PUMPKIN = init(); + @Nullable public static final BlockType CAULDRON = init(); + @Nullable public static final BlockType CAVE_AIR = init(); + @Nullable public static final BlockType CHAIN_COMMAND_BLOCK = init(); + @Nullable public static final BlockType CHEST = init(); + @Nullable public static final BlockType CHIPPED_ANVIL = init(); + @Nullable public static final BlockType CHISELED_QUARTZ_BLOCK = init(); + @Nullable public static final BlockType CHISELED_RED_SANDSTONE = init(); + @Nullable public static final BlockType CHISELED_SANDSTONE = init(); + @Nullable public static final BlockType CHISELED_STONE_BRICKS = init(); + @Nullable public static final BlockType CHORUS_FLOWER = init(); + @Nullable public static final BlockType CHORUS_PLANT = init(); + @Nullable public static final BlockType CLAY = init(); + @Nullable public static final BlockType COAL_BLOCK = init(); + @Nullable public static final BlockType COAL_ORE = init(); + @Nullable public static final BlockType COARSE_DIRT = init(); + @Nullable public static final BlockType COBBLESTONE = init(); + @Nullable public static final BlockType COBBLESTONE_SLAB = init(); + @Nullable public static final BlockType COBBLESTONE_STAIRS = init(); + @Nullable public static final BlockType COBBLESTONE_WALL = init(); + @Nullable public static final BlockType COBWEB = init(); + @Nullable public static final BlockType COCOA = init(); + @Nullable public static final BlockType COMMAND_BLOCK = init(); + @Nullable public static final BlockType COMPARATOR = init(); + @Nullable public static final BlockType COMPOSTER = init(); + @Nullable public static final BlockType CONDUIT = init(); + @Nullable public static final BlockType CORNFLOWER = init(); + @Nullable public static final BlockType CRACKED_STONE_BRICKS = init(); + @Nullable public static final BlockType CRAFTING_TABLE = init(); + @Nullable public static final BlockType CREEPER_HEAD = init(); + @Nullable public static final BlockType CREEPER_WALL_HEAD = init(); + @Nullable public static final BlockType CUT_RED_SANDSTONE = init(); + @Nullable public static final BlockType CUT_RED_SANDSTONE_SLAB = init(); + @Nullable public static final BlockType CUT_SANDSTONE = init(); + @Nullable public static final BlockType CUT_SANDSTONE_SLAB = init(); + @Nullable public static final BlockType CYAN_BANNER = init(); + @Nullable public static final BlockType CYAN_BED = init(); + @Nullable public static final BlockType CYAN_CARPET = init(); + @Nullable public static final BlockType CYAN_CONCRETE = init(); + @Nullable public static final BlockType CYAN_CONCRETE_POWDER = init(); + @Nullable public static final BlockType CYAN_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType CYAN_SHULKER_BOX = init(); + @Nullable public static final BlockType CYAN_STAINED_GLASS = init(); + @Nullable public static final BlockType CYAN_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType CYAN_TERRACOTTA = init(); + @Nullable public static final BlockType CYAN_WALL_BANNER = init(); + @Nullable public static final BlockType CYAN_WOOL = init(); + @Nullable public static final BlockType DAMAGED_ANVIL = init(); + @Nullable public static final BlockType DANDELION = init(); + @Nullable public static final BlockType DARK_OAK_BUTTON = init(); + @Nullable public static final BlockType DARK_OAK_DOOR = init(); + @Nullable public static final BlockType DARK_OAK_FENCE = init(); + @Nullable public static final BlockType DARK_OAK_FENCE_GATE = init(); + @Nullable public static final BlockType DARK_OAK_LEAVES = init(); + @Nullable public static final BlockType DARK_OAK_LOG = init(); + @Nullable public static final BlockType DARK_OAK_PLANKS = init(); + @Nullable public static final BlockType DARK_OAK_PRESSURE_PLATE = init(); + @Nullable public static final BlockType DARK_OAK_SAPLING = init(); + @Nullable public static final BlockType DARK_OAK_SIGN = init(); + @Nullable public static final BlockType DARK_OAK_SLAB = init(); + @Nullable public static final BlockType DARK_OAK_STAIRS = init(); + @Nullable public static final BlockType DARK_OAK_TRAPDOOR = init(); + @Nullable public static final BlockType DARK_OAK_WALL_SIGN = init(); + @Nullable public static final BlockType DARK_OAK_WOOD = init(); + @Nullable public static final BlockType DARK_PRISMARINE = init(); + @Nullable public static final BlockType DARK_PRISMARINE_SLAB = init(); + @Nullable public static final BlockType DARK_PRISMARINE_STAIRS = init(); + @Nullable public static final BlockType DAYLIGHT_DETECTOR = init(); + @Nullable public static final BlockType DEAD_BRAIN_CORAL = init(); + @Nullable public static final BlockType DEAD_BRAIN_CORAL_BLOCK = init(); + @Nullable public static final BlockType DEAD_BRAIN_CORAL_FAN = init(); + @Nullable public static final BlockType DEAD_BRAIN_CORAL_WALL_FAN = init(); + @Nullable public static final BlockType DEAD_BUBBLE_CORAL = init(); + @Nullable public static final BlockType DEAD_BUBBLE_CORAL_BLOCK = init(); + @Nullable public static final BlockType DEAD_BUBBLE_CORAL_FAN = init(); + @Nullable public static final BlockType DEAD_BUBBLE_CORAL_WALL_FAN = init(); + @Nullable public static final BlockType DEAD_BUSH = init(); + @Nullable public static final BlockType DEAD_FIRE_CORAL = init(); + @Nullable public static final BlockType DEAD_FIRE_CORAL_BLOCK = init(); + @Nullable public static final BlockType DEAD_FIRE_CORAL_FAN = init(); + @Nullable public static final BlockType DEAD_FIRE_CORAL_WALL_FAN = init(); + @Nullable public static final BlockType DEAD_HORN_CORAL = init(); + @Nullable public static final BlockType DEAD_HORN_CORAL_BLOCK = init(); + @Nullable public static final BlockType DEAD_HORN_CORAL_FAN = init(); + @Nullable public static final BlockType DEAD_HORN_CORAL_WALL_FAN = init(); + @Nullable public static final BlockType DEAD_TUBE_CORAL = init(); + @Nullable public static final BlockType DEAD_TUBE_CORAL_BLOCK = init(); + @Nullable public static final BlockType DEAD_TUBE_CORAL_FAN = init(); + @Nullable public static final BlockType DEAD_TUBE_CORAL_WALL_FAN = init(); + @Nullable public static final BlockType DETECTOR_RAIL = init(); + @Nullable public static final BlockType DIAMOND_BLOCK = init(); + @Nullable public static final BlockType DIAMOND_ORE = init(); + @Nullable public static final BlockType DIORITE = init(); + @Nullable public static final BlockType DIORITE_SLAB = init(); + @Nullable public static final BlockType DIORITE_STAIRS = init(); + @Nullable public static final BlockType DIORITE_WALL = init(); + @Nullable public static final BlockType DIRT = init(); + @Nullable public static final BlockType DISPENSER = init(); + @Nullable public static final BlockType DRAGON_EGG = init(); + @Nullable public static final BlockType DRAGON_HEAD = init(); + @Nullable public static final BlockType DRAGON_WALL_HEAD = init(); + @Nullable public static final BlockType DRIED_KELP_BLOCK = init(); + @Nullable public static final BlockType DROPPER = init(); + @Nullable public static final BlockType EMERALD_BLOCK = init(); + @Nullable public static final BlockType EMERALD_ORE = init(); + @Nullable public static final BlockType ENCHANTING_TABLE = init(); + @Nullable public static final BlockType END_GATEWAY = init(); + @Nullable public static final BlockType END_PORTAL = init(); + @Nullable public static final BlockType END_PORTAL_FRAME = init(); + @Nullable public static final BlockType END_ROD = init(); + @Nullable public static final BlockType END_STONE = init(); + @Nullable public static final BlockType END_STONE_BRICK_SLAB = init(); + @Nullable public static final BlockType END_STONE_BRICK_STAIRS = init(); + @Nullable public static final BlockType END_STONE_BRICK_WALL = init(); + @Nullable public static final BlockType END_STONE_BRICKS = init(); + @Nullable public static final BlockType ENDER_CHEST = init(); + @Nullable public static final BlockType FARMLAND = init(); + @Nullable public static final BlockType FERN = init(); + @Nullable public static final BlockType FIRE = init(); + @Nullable public static final BlockType FIRE_CORAL = init(); + @Nullable public static final BlockType FIRE_CORAL_BLOCK = init(); + @Nullable public static final BlockType FIRE_CORAL_FAN = init(); + @Nullable public static final BlockType FIRE_CORAL_WALL_FAN = init(); + @Nullable public static final BlockType FLETCHING_TABLE = init(); + @Nullable public static final BlockType FLOWER_POT = init(); + @Nullable public static final BlockType FROSTED_ICE = init(); + @Nullable public static final BlockType FURNACE = init(); + @Nullable public static final BlockType GLASS = init(); + @Nullable public static final BlockType GLASS_PANE = init(); + @Nullable public static final BlockType GLOWSTONE = init(); + @Nullable public static final BlockType GOLD_BLOCK = init(); + @Nullable public static final BlockType GOLD_ORE = init(); + @Nullable public static final BlockType GRANITE = init(); + @Nullable public static final BlockType GRANITE_SLAB = init(); + @Nullable public static final BlockType GRANITE_STAIRS = init(); + @Nullable public static final BlockType GRANITE_WALL = init(); + @Nullable public static final BlockType GRASS = init(); + @Nullable public static final BlockType GRASS_BLOCK = init(); + @Nullable public static final BlockType GRASS_PATH = init(); + @Nullable public static final BlockType GRAVEL = init(); + @Nullable public static final BlockType GRAY_BANNER = init(); + @Nullable public static final BlockType GRAY_BED = init(); + @Nullable public static final BlockType GRAY_CARPET = init(); + @Nullable public static final BlockType GRAY_CONCRETE = init(); + @Nullable public static final BlockType GRAY_CONCRETE_POWDER = init(); + @Nullable public static final BlockType GRAY_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType GRAY_SHULKER_BOX = init(); + @Nullable public static final BlockType GRAY_STAINED_GLASS = init(); + @Nullable public static final BlockType GRAY_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType GRAY_TERRACOTTA = init(); + @Nullable public static final BlockType GRAY_WALL_BANNER = init(); + @Nullable public static final BlockType GRAY_WOOL = init(); + @Nullable public static final BlockType GREEN_BANNER = init(); + @Nullable public static final BlockType GREEN_BED = init(); + @Nullable public static final BlockType GREEN_CARPET = init(); + @Nullable public static final BlockType GREEN_CONCRETE = init(); + @Nullable public static final BlockType GREEN_CONCRETE_POWDER = init(); + @Nullable public static final BlockType GREEN_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType GREEN_SHULKER_BOX = init(); + @Nullable public static final BlockType GREEN_STAINED_GLASS = init(); + @Nullable public static final BlockType GREEN_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType GREEN_TERRACOTTA = init(); + @Nullable public static final BlockType GREEN_WALL_BANNER = init(); + @Nullable public static final BlockType GREEN_WOOL = init(); + @Nullable public static final BlockType GRINDSTONE = init(); + @Nullable public static final BlockType HAY_BLOCK = init(); + @Nullable public static final BlockType HEAVY_WEIGHTED_PRESSURE_PLATE = init(); + @Nullable public static final BlockType HOPPER = init(); + @Nullable public static final BlockType HORN_CORAL = init(); + @Nullable public static final BlockType HORN_CORAL_BLOCK = init(); + @Nullable public static final BlockType HORN_CORAL_FAN = init(); + @Nullable public static final BlockType HORN_CORAL_WALL_FAN = init(); + @Nullable public static final BlockType ICE = init(); + @Nullable public static final BlockType INFESTED_CHISELED_STONE_BRICKS = init(); + @Nullable public static final BlockType INFESTED_COBBLESTONE = init(); + @Nullable public static final BlockType INFESTED_CRACKED_STONE_BRICKS = init(); + @Nullable public static final BlockType INFESTED_MOSSY_STONE_BRICKS = init(); + @Nullable public static final BlockType INFESTED_STONE = init(); + @Nullable public static final BlockType INFESTED_STONE_BRICKS = init(); + @Nullable public static final BlockType IRON_BARS = init(); + @Nullable public static final BlockType IRON_BLOCK = init(); + @Nullable public static final BlockType IRON_DOOR = init(); + @Nullable public static final BlockType IRON_ORE = init(); + @Nullable public static final BlockType IRON_TRAPDOOR = init(); + @Nullable public static final BlockType JACK_O_LANTERN = init(); + @Nullable public static final BlockType JIGSAW = init(); + @Nullable public static final BlockType JUKEBOX = init(); + @Nullable public static final BlockType JUNGLE_BUTTON = init(); + @Nullable public static final BlockType JUNGLE_DOOR = init(); + @Nullable public static final BlockType JUNGLE_FENCE = init(); + @Nullable public static final BlockType JUNGLE_FENCE_GATE = init(); + @Nullable public static final BlockType JUNGLE_LEAVES = init(); + @Nullable public static final BlockType JUNGLE_LOG = init(); + @Nullable public static final BlockType JUNGLE_PLANKS = init(); + @Nullable public static final BlockType JUNGLE_PRESSURE_PLATE = init(); + @Nullable public static final BlockType JUNGLE_SAPLING = init(); + @Nullable public static final BlockType JUNGLE_SIGN = init(); + @Nullable public static final BlockType JUNGLE_SLAB = init(); + @Nullable public static final BlockType JUNGLE_STAIRS = init(); + @Nullable public static final BlockType JUNGLE_TRAPDOOR = init(); + @Nullable public static final BlockType JUNGLE_WALL_SIGN = init(); + @Nullable public static final BlockType JUNGLE_WOOD = init(); + @Nullable public static final BlockType KELP = init(); + @Nullable public static final BlockType KELP_PLANT = init(); + @Nullable public static final BlockType LADDER = init(); + @Nullable public static final BlockType LANTERN = init(); + @Nullable public static final BlockType LAPIS_BLOCK = init(); + @Nullable public static final BlockType LAPIS_ORE = init(); + @Nullable public static final BlockType LARGE_FERN = init(); + @Nullable public static final BlockType LAVA = init(); + @Nullable public static final BlockType LECTERN = init(); + @Nullable public static final BlockType LEVER = init(); + @Nullable public static final BlockType LIGHT_BLUE_BANNER = init(); + @Nullable public static final BlockType LIGHT_BLUE_BED = init(); + @Nullable public static final BlockType LIGHT_BLUE_CARPET = init(); + @Nullable public static final BlockType LIGHT_BLUE_CONCRETE = init(); + @Nullable public static final BlockType LIGHT_BLUE_CONCRETE_POWDER = init(); + @Nullable public static final BlockType LIGHT_BLUE_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType LIGHT_BLUE_SHULKER_BOX = init(); + @Nullable public static final BlockType LIGHT_BLUE_STAINED_GLASS = init(); + @Nullable public static final BlockType LIGHT_BLUE_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType LIGHT_BLUE_TERRACOTTA = init(); + @Nullable public static final BlockType LIGHT_BLUE_WALL_BANNER = init(); + @Nullable public static final BlockType LIGHT_BLUE_WOOL = init(); + @Nullable public static final BlockType LIGHT_GRAY_BANNER = init(); + @Nullable public static final BlockType LIGHT_GRAY_BED = init(); + @Nullable public static final BlockType LIGHT_GRAY_CARPET = init(); + @Nullable public static final BlockType LIGHT_GRAY_CONCRETE = init(); + @Nullable public static final BlockType LIGHT_GRAY_CONCRETE_POWDER = init(); + @Nullable public static final BlockType LIGHT_GRAY_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType LIGHT_GRAY_SHULKER_BOX = init(); + @Nullable public static final BlockType LIGHT_GRAY_STAINED_GLASS = init(); + @Nullable public static final BlockType LIGHT_GRAY_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType LIGHT_GRAY_TERRACOTTA = init(); + @Nullable public static final BlockType LIGHT_GRAY_WALL_BANNER = init(); + @Nullable public static final BlockType LIGHT_GRAY_WOOL = init(); + @Nullable public static final BlockType LIGHT_WEIGHTED_PRESSURE_PLATE = init(); + @Nullable public static final BlockType LILAC = init(); + @Nullable public static final BlockType LILY_OF_THE_VALLEY = init(); + @Nullable public static final BlockType LILY_PAD = init(); + @Nullable public static final BlockType LIME_BANNER = init(); + @Nullable public static final BlockType LIME_BED = init(); + @Nullable public static final BlockType LIME_CARPET = init(); + @Nullable public static final BlockType LIME_CONCRETE = init(); + @Nullable public static final BlockType LIME_CONCRETE_POWDER = init(); + @Nullable public static final BlockType LIME_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType LIME_SHULKER_BOX = init(); + @Nullable public static final BlockType LIME_STAINED_GLASS = init(); + @Nullable public static final BlockType LIME_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType LIME_TERRACOTTA = init(); + @Nullable public static final BlockType LIME_WALL_BANNER = init(); + @Nullable public static final BlockType LIME_WOOL = init(); + @Nullable public static final BlockType LOOM = init(); + @Nullable public static final BlockType MAGENTA_BANNER = init(); + @Nullable public static final BlockType MAGENTA_BED = init(); + @Nullable public static final BlockType MAGENTA_CARPET = init(); + @Nullable public static final BlockType MAGENTA_CONCRETE = init(); + @Nullable public static final BlockType MAGENTA_CONCRETE_POWDER = init(); + @Nullable public static final BlockType MAGENTA_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType MAGENTA_SHULKER_BOX = init(); + @Nullable public static final BlockType MAGENTA_STAINED_GLASS = init(); + @Nullable public static final BlockType MAGENTA_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType MAGENTA_TERRACOTTA = init(); + @Nullable public static final BlockType MAGENTA_WALL_BANNER = init(); + @Nullable public static final BlockType MAGENTA_WOOL = init(); + @Nullable public static final BlockType MAGMA_BLOCK = init(); + @Nullable public static final BlockType MELON = init(); + @Nullable public static final BlockType MELON_STEM = init(); + @Nullable public static final BlockType MOSSY_COBBLESTONE = init(); + @Nullable public static final BlockType MOSSY_COBBLESTONE_SLAB = init(); + @Nullable public static final BlockType MOSSY_COBBLESTONE_STAIRS = init(); + @Nullable public static final BlockType MOSSY_COBBLESTONE_WALL = init(); + @Nullable public static final BlockType MOSSY_STONE_BRICK_SLAB = init(); + @Nullable public static final BlockType MOSSY_STONE_BRICK_STAIRS = init(); + @Nullable public static final BlockType MOSSY_STONE_BRICK_WALL = init(); + @Nullable public static final BlockType MOSSY_STONE_BRICKS = init(); + @Nullable public static final BlockType MOVING_PISTON = init(); + @Nullable public static final BlockType MUSHROOM_STEM = init(); + @Nullable public static final BlockType MYCELIUM = init(); + @Nullable public static final BlockType NETHER_BRICK_FENCE = init(); + @Nullable public static final BlockType NETHER_BRICK_SLAB = init(); + @Nullable public static final BlockType NETHER_BRICK_STAIRS = init(); + @Nullable public static final BlockType NETHER_BRICK_WALL = init(); + @Nullable public static final BlockType NETHER_BRICKS = init(); + @Nullable public static final BlockType NETHER_PORTAL = init(); + @Nullable public static final BlockType NETHER_QUARTZ_ORE = init(); + @Nullable public static final BlockType NETHER_WART = init(); + @Nullable public static final BlockType NETHER_WART_BLOCK = init(); + @Nullable public static final BlockType NETHERRACK = init(); + @Nullable public static final BlockType NOTE_BLOCK = init(); + @Nullable public static final BlockType OAK_BUTTON = init(); + @Nullable public static final BlockType OAK_DOOR = init(); + @Nullable public static final BlockType OAK_FENCE = init(); + @Nullable public static final BlockType OAK_FENCE_GATE = init(); + @Nullable public static final BlockType OAK_LEAVES = init(); + @Nullable public static final BlockType OAK_LOG = init(); + @Nullable public static final BlockType OAK_PLANKS = init(); + @Nullable public static final BlockType OAK_PRESSURE_PLATE = init(); + @Nullable public static final BlockType OAK_SAPLING = init(); + @Nullable public static final BlockType OAK_SIGN = init(); + @Nullable public static final BlockType OAK_SLAB = init(); + @Nullable public static final BlockType OAK_STAIRS = init(); + @Nullable public static final BlockType OAK_TRAPDOOR = init(); + @Nullable public static final BlockType OAK_WALL_SIGN = init(); + @Nullable public static final BlockType OAK_WOOD = init(); + @Nullable public static final BlockType OBSERVER = init(); + @Nullable public static final BlockType OBSIDIAN = init(); + @Nullable public static final BlockType ORANGE_BANNER = init(); + @Nullable public static final BlockType ORANGE_BED = init(); + @Nullable public static final BlockType ORANGE_CARPET = init(); + @Nullable public static final BlockType ORANGE_CONCRETE = init(); + @Nullable public static final BlockType ORANGE_CONCRETE_POWDER = init(); + @Nullable public static final BlockType ORANGE_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType ORANGE_SHULKER_BOX = init(); + @Nullable public static final BlockType ORANGE_STAINED_GLASS = init(); + @Nullable public static final BlockType ORANGE_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType ORANGE_TERRACOTTA = init(); + @Nullable public static final BlockType ORANGE_TULIP = init(); + @Nullable public static final BlockType ORANGE_WALL_BANNER = init(); + @Nullable public static final BlockType ORANGE_WOOL = init(); + @Nullable public static final BlockType OXEYE_DAISY = init(); + @Nullable public static final BlockType PACKED_ICE = init(); + @Nullable public static final BlockType PEONY = init(); + @Nullable public static final BlockType PETRIFIED_OAK_SLAB = init(); + @Nullable public static final BlockType PINK_BANNER = init(); + @Nullable public static final BlockType PINK_BED = init(); + @Nullable public static final BlockType PINK_CARPET = init(); + @Nullable public static final BlockType PINK_CONCRETE = init(); + @Nullable public static final BlockType PINK_CONCRETE_POWDER = init(); + @Nullable public static final BlockType PINK_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType PINK_SHULKER_BOX = init(); + @Nullable public static final BlockType PINK_STAINED_GLASS = init(); + @Nullable public static final BlockType PINK_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType PINK_TERRACOTTA = init(); + @Nullable public static final BlockType PINK_TULIP = init(); + @Nullable public static final BlockType PINK_WALL_BANNER = init(); + @Nullable public static final BlockType PINK_WOOL = init(); + @Nullable public static final BlockType PISTON = init(); + @Nullable public static final BlockType PISTON_HEAD = init(); + @Nullable public static final BlockType PLAYER_HEAD = init(); + @Nullable public static final BlockType PLAYER_WALL_HEAD = init(); + @Nullable public static final BlockType PODZOL = init(); + @Nullable public static final BlockType POLISHED_ANDESITE = init(); + @Nullable public static final BlockType POLISHED_ANDESITE_SLAB = init(); + @Nullable public static final BlockType POLISHED_ANDESITE_STAIRS = init(); + @Nullable public static final BlockType POLISHED_DIORITE = init(); + @Nullable public static final BlockType POLISHED_DIORITE_SLAB = init(); + @Nullable public static final BlockType POLISHED_DIORITE_STAIRS = init(); + @Nullable public static final BlockType POLISHED_GRANITE = init(); + @Nullable public static final BlockType POLISHED_GRANITE_SLAB = init(); + @Nullable public static final BlockType POLISHED_GRANITE_STAIRS = init(); + @Nullable public static final BlockType POPPY = init(); + @Nullable public static final BlockType POTATOES = init(); + @Nullable public static final BlockType POTTED_ACACIA_SAPLING = init(); + @Nullable public static final BlockType POTTED_ALLIUM = init(); + @Nullable public static final BlockType POTTED_AZURE_BLUET = init(); + @Nullable public static final BlockType POTTED_BAMBOO = init(); + @Nullable public static final BlockType POTTED_BIRCH_SAPLING = init(); + @Nullable public static final BlockType POTTED_BLUE_ORCHID = init(); + @Nullable public static final BlockType POTTED_BROWN_MUSHROOM = init(); + @Nullable public static final BlockType POTTED_CACTUS = init(); + @Nullable public static final BlockType POTTED_CORNFLOWER = init(); + @Nullable public static final BlockType POTTED_DANDELION = init(); + @Nullable public static final BlockType POTTED_DARK_OAK_SAPLING = init(); + @Nullable public static final BlockType POTTED_DEAD_BUSH = init(); + @Nullable public static final BlockType POTTED_FERN = init(); + @Nullable public static final BlockType POTTED_JUNGLE_SAPLING = init(); + @Nullable public static final BlockType POTTED_LILY_OF_THE_VALLEY = init(); + @Nullable public static final BlockType POTTED_OAK_SAPLING = init(); + @Nullable public static final BlockType POTTED_ORANGE_TULIP = init(); + @Nullable public static final BlockType POTTED_OXEYE_DAISY = init(); + @Nullable public static final BlockType POTTED_PINK_TULIP = init(); + @Nullable public static final BlockType POTTED_POPPY = init(); + @Nullable public static final BlockType POTTED_RED_MUSHROOM = init(); + @Nullable public static final BlockType POTTED_RED_TULIP = init(); + @Nullable public static final BlockType POTTED_SPRUCE_SAPLING = init(); + @Nullable public static final BlockType POTTED_WHITE_TULIP = init(); + @Nullable public static final BlockType POTTED_WITHER_ROSE = init(); + @Nullable public static final BlockType POWERED_RAIL = init(); + @Nullable public static final BlockType PRISMARINE = init(); + @Nullable public static final BlockType PRISMARINE_BRICK_SLAB = init(); + @Nullable public static final BlockType PRISMARINE_BRICK_STAIRS = init(); + @Nullable public static final BlockType PRISMARINE_BRICKS = init(); + @Nullable public static final BlockType PRISMARINE_SLAB = init(); + @Nullable public static final BlockType PRISMARINE_STAIRS = init(); + @Nullable public static final BlockType PRISMARINE_WALL = init(); + @Nullable public static final BlockType PUMPKIN = init(); + @Nullable public static final BlockType PUMPKIN_STEM = init(); + @Nullable public static final BlockType PURPLE_BANNER = init(); + @Nullable public static final BlockType PURPLE_BED = init(); + @Nullable public static final BlockType PURPLE_CARPET = init(); + @Nullable public static final BlockType PURPLE_CONCRETE = init(); + @Nullable public static final BlockType PURPLE_CONCRETE_POWDER = init(); + @Nullable public static final BlockType PURPLE_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType PURPLE_SHULKER_BOX = init(); + @Nullable public static final BlockType PURPLE_STAINED_GLASS = init(); + @Nullable public static final BlockType PURPLE_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType PURPLE_TERRACOTTA = init(); + @Nullable public static final BlockType PURPLE_WALL_BANNER = init(); + @Nullable public static final BlockType PURPLE_WOOL = init(); + @Nullable public static final BlockType PURPUR_BLOCK = init(); + @Nullable public static final BlockType PURPUR_PILLAR = init(); + @Nullable public static final BlockType PURPUR_SLAB = init(); + @Nullable public static final BlockType PURPUR_STAIRS = init(); + @Nullable public static final BlockType QUARTZ_BLOCK = init(); + @Nullable public static final BlockType QUARTZ_PILLAR = init(); + @Nullable public static final BlockType QUARTZ_SLAB = init(); + @Nullable public static final BlockType QUARTZ_STAIRS = init(); + @Nullable public static final BlockType RAIL = init(); + @Nullable public static final BlockType RED_BANNER = init(); + @Nullable public static final BlockType RED_BED = init(); + @Nullable public static final BlockType RED_CARPET = init(); + @Nullable public static final BlockType RED_CONCRETE = init(); + @Nullable public static final BlockType RED_CONCRETE_POWDER = init(); + @Nullable public static final BlockType RED_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType RED_MUSHROOM = init(); + @Nullable public static final BlockType RED_MUSHROOM_BLOCK = init(); + @Nullable public static final BlockType RED_NETHER_BRICK_SLAB = init(); + @Nullable public static final BlockType RED_NETHER_BRICK_STAIRS = init(); + @Nullable public static final BlockType RED_NETHER_BRICK_WALL = init(); + @Nullable public static final BlockType RED_NETHER_BRICKS = init(); + @Nullable public static final BlockType RED_SAND = init(); + @Nullable public static final BlockType RED_SANDSTONE = init(); + @Nullable public static final BlockType RED_SANDSTONE_SLAB = init(); + @Nullable public static final BlockType RED_SANDSTONE_STAIRS = init(); + @Nullable public static final BlockType RED_SANDSTONE_WALL = init(); + @Nullable public static final BlockType RED_SHULKER_BOX = init(); + @Nullable public static final BlockType RED_STAINED_GLASS = init(); + @Nullable public static final BlockType RED_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType RED_TERRACOTTA = init(); + @Nullable public static final BlockType RED_TULIP = init(); + @Nullable public static final BlockType RED_WALL_BANNER = init(); + @Nullable public static final BlockType RED_WOOL = init(); + @Nullable public static final BlockType REDSTONE_BLOCK = init(); + @Nullable public static final BlockType REDSTONE_LAMP = init(); + @Nullable public static final BlockType REDSTONE_ORE = init(); + @Nullable public static final BlockType REDSTONE_TORCH = init(); + @Nullable public static final BlockType REDSTONE_WALL_TORCH = init(); + @Nullable public static final BlockType REDSTONE_WIRE = init(); + @Nullable public static final BlockType REPEATER = init(); + @Nullable public static final BlockType REPEATING_COMMAND_BLOCK = init(); + @Nullable public static final BlockType ROSE_BUSH = init(); + @Nullable public static final BlockType SAND = init(); + @Nullable public static final BlockType SANDSTONE = init(); + @Nullable public static final BlockType SANDSTONE_SLAB = init(); + @Nullable public static final BlockType SANDSTONE_STAIRS = init(); + @Nullable public static final BlockType SANDSTONE_WALL = init(); + @Nullable public static final BlockType SCAFFOLDING = init(); + @Nullable public static final BlockType SEA_LANTERN = init(); + @Nullable public static final BlockType SEA_PICKLE = init(); + @Nullable public static final BlockType SEAGRASS = init(); + @Nullable public static final BlockType SHULKER_BOX = init(); + @Nullable public static final BlockType SKELETON_SKULL = init(); + @Nullable public static final BlockType SKELETON_WALL_SKULL = init(); + @Nullable public static final BlockType SLIME_BLOCK = init(); + @Nullable public static final BlockType SMITHING_TABLE = init(); + @Nullable public static final BlockType SMOKER = init(); + @Nullable public static final BlockType SMOOTH_QUARTZ = init(); + @Nullable public static final BlockType SMOOTH_QUARTZ_SLAB = init(); + @Nullable public static final BlockType SMOOTH_QUARTZ_STAIRS = init(); + @Nullable public static final BlockType SMOOTH_RED_SANDSTONE = init(); + @Nullable public static final BlockType SMOOTH_RED_SANDSTONE_SLAB = init(); + @Nullable public static final BlockType SMOOTH_RED_SANDSTONE_STAIRS = init(); + @Nullable public static final BlockType SMOOTH_SANDSTONE = init(); + @Nullable public static final BlockType SMOOTH_SANDSTONE_SLAB = init(); + @Nullable public static final BlockType SMOOTH_SANDSTONE_STAIRS = init(); + @Nullable public static final BlockType SMOOTH_STONE = init(); + @Nullable public static final BlockType SMOOTH_STONE_SLAB = init(); + @Nullable public static final BlockType SNOW = init(); + @Nullable public static final BlockType SNOW_BLOCK = init(); + @Nullable public static final BlockType SOUL_SAND = init(); + @Nullable public static final BlockType SPAWNER = init(); + @Nullable public static final BlockType SPONGE = init(); + @Nullable public static final BlockType SPRUCE_BUTTON = init(); + @Nullable public static final BlockType SPRUCE_DOOR = init(); + @Nullable public static final BlockType SPRUCE_FENCE = init(); + @Nullable public static final BlockType SPRUCE_FENCE_GATE = init(); + @Nullable public static final BlockType SPRUCE_LEAVES = init(); + @Nullable public static final BlockType SPRUCE_LOG = init(); + @Nullable public static final BlockType SPRUCE_PLANKS = init(); + @Nullable public static final BlockType SPRUCE_PRESSURE_PLATE = init(); + @Nullable public static final BlockType SPRUCE_SAPLING = init(); + @Nullable public static final BlockType SPRUCE_SIGN = init(); + @Nullable public static final BlockType SPRUCE_SLAB = init(); + @Nullable public static final BlockType SPRUCE_STAIRS = init(); + @Nullable public static final BlockType SPRUCE_TRAPDOOR = init(); + @Nullable public static final BlockType SPRUCE_WALL_SIGN = init(); + @Nullable public static final BlockType SPRUCE_WOOD = init(); + @Nullable public static final BlockType STICKY_PISTON = init(); + @Nullable public static final BlockType STONE = init(); + @Nullable public static final BlockType STONE_BRICK_SLAB = init(); + @Nullable public static final BlockType STONE_BRICK_STAIRS = init(); + @Nullable public static final BlockType STONE_BRICK_WALL = init(); + @Nullable public static final BlockType STONE_BRICKS = init(); + @Nullable public static final BlockType STONE_BUTTON = init(); + @Nullable public static final BlockType STONE_PRESSURE_PLATE = init(); + @Nullable public static final BlockType STONE_SLAB = init(); + @Nullable public static final BlockType STONE_STAIRS = init(); + @Nullable public static final BlockType STONECUTTER = init(); + @Nullable public static final BlockType STRIPPED_ACACIA_LOG = init(); + @Nullable public static final BlockType STRIPPED_ACACIA_WOOD = init(); + @Nullable public static final BlockType STRIPPED_BIRCH_LOG = init(); + @Nullable public static final BlockType STRIPPED_BIRCH_WOOD = init(); + @Nullable public static final BlockType STRIPPED_DARK_OAK_LOG = init(); + @Nullable public static final BlockType STRIPPED_DARK_OAK_WOOD = init(); + @Nullable public static final BlockType STRIPPED_JUNGLE_LOG = init(); + @Nullable public static final BlockType STRIPPED_JUNGLE_WOOD = init(); + @Nullable public static final BlockType STRIPPED_OAK_LOG = init(); + @Nullable public static final BlockType STRIPPED_OAK_WOOD = init(); + @Nullable public static final BlockType STRIPPED_SPRUCE_LOG = init(); + @Nullable public static final BlockType STRIPPED_SPRUCE_WOOD = init(); + @Nullable public static final BlockType STRUCTURE_BLOCK = init(); + @Nullable public static final BlockType STRUCTURE_VOID = init(); + @Nullable public static final BlockType SUGAR_CANE = init(); + @Nullable public static final BlockType SUNFLOWER = init(); + @Nullable public static final BlockType SWEET_BERRY_BUSH = init(); + @Nullable public static final BlockType TALL_GRASS = init(); + @Nullable public static final BlockType TALL_SEAGRASS = init(); + @Nullable public static final BlockType TERRACOTTA = init(); + @Nullable public static final BlockType TNT = init(); + @Nullable public static final BlockType TORCH = init(); + @Nullable public static final BlockType TRAPPED_CHEST = init(); + @Nullable public static final BlockType TRIPWIRE = init(); + @Nullable public static final BlockType TRIPWIRE_HOOK = init(); + @Nullable public static final BlockType TUBE_CORAL = init(); + @Nullable public static final BlockType TUBE_CORAL_BLOCK = init(); + @Nullable public static final BlockType TUBE_CORAL_FAN = init(); + @Nullable public static final BlockType TUBE_CORAL_WALL_FAN = init(); + @Nullable public static final BlockType TURTLE_EGG = init(); + @Nullable public static final BlockType VINE = init(); + @Nullable public static final BlockType VOID_AIR = init(); + @Nullable public static final BlockType WALL_TORCH = init(); + @Nullable public static final BlockType WATER = init(); + @Nullable public static final BlockType WET_SPONGE = init(); + @Nullable public static final BlockType WHEAT = init(); + @Nullable public static final BlockType WHITE_BANNER = init(); + @Nullable public static final BlockType WHITE_BED = init(); + @Nullable public static final BlockType WHITE_CARPET = init(); + @Nullable public static final BlockType WHITE_CONCRETE = init(); + @Nullable public static final BlockType WHITE_CONCRETE_POWDER = init(); + @Nullable public static final BlockType WHITE_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType WHITE_SHULKER_BOX = init(); + @Nullable public static final BlockType WHITE_STAINED_GLASS = init(); + @Nullable public static final BlockType WHITE_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType WHITE_TERRACOTTA = init(); + @Nullable public static final BlockType WHITE_TULIP = init(); + @Nullable public static final BlockType WHITE_WALL_BANNER = init(); + @Nullable public static final BlockType WHITE_WOOL = init(); + @Nullable public static final BlockType WITHER_ROSE = init(); + @Nullable public static final BlockType WITHER_SKELETON_SKULL = init(); + @Nullable public static final BlockType WITHER_SKELETON_WALL_SKULL = init(); + @Nullable public static final BlockType YELLOW_BANNER = init(); + @Nullable public static final BlockType YELLOW_BED = init(); + @Nullable public static final BlockType YELLOW_CARPET = init(); + @Nullable public static final BlockType YELLOW_CONCRETE = init(); + @Nullable public static final BlockType YELLOW_CONCRETE_POWDER = init(); + @Nullable public static final BlockType YELLOW_GLAZED_TERRACOTTA = init(); + @Nullable public static final BlockType YELLOW_SHULKER_BOX = init(); + @Nullable public static final BlockType YELLOW_STAINED_GLASS = init(); + @Nullable public static final BlockType YELLOW_STAINED_GLASS_PANE = init(); + @Nullable public static final BlockType YELLOW_TERRACOTTA = init(); + @Nullable public static final BlockType YELLOW_WALL_BANNER = init(); + @Nullable public static final BlockType YELLOW_WOOL = init(); + @Nullable public static final BlockType ZOMBIE_HEAD = init(); + @Nullable public static final BlockType ZOMBIE_WALL_HEAD = init(); // deprecated - @Deprecated @Nullable public static BlockType SIGN; - @Deprecated @Nullable public static BlockType WALL_SIGN; + @Deprecated @Nullable public static BlockType SIGN = OAK_SIGN; + @Deprecated @Nullable public static BlockType WALL_SIGN = OAK_WALL_SIGN; - /* - ----------------------------------------------------- - Settings - ----------------------------------------------------- - */ - protected final static class Settings { - protected final int internalId; - protected final BlockState defaultState; - protected final AbstractProperty[] propertiesMapArr; - protected final AbstractProperty[] propertiesArr; - protected final List> propertiesList; - protected final Map> propertiesMap; - protected final Set> propertiesSet; - protected final BlockMaterial blockMaterial; - protected final int permutations; - protected int[] stateOrdinals; - - Settings(BlockType type, String id, int internalId, List states) { - this.internalId = internalId; - String propertyString = null; - int propI = id.indexOf('['); - if (propI != -1) { - propertyString = id.substring(propI + 1, id.length() - 1); - } - - int maxInternalStateId = 0; - Map> properties = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry().getProperties(type); - if (!properties.isEmpty()) { - // Ensure the properties are registered - int maxOrdinal = 0; - for (String key : properties.keySet()) { - maxOrdinal = Math.max(PropertyKey.getOrCreate(key).ordinal(), maxOrdinal); - } - this.propertiesMapArr = new AbstractProperty[maxOrdinal + 1]; - int prop_arr_i = 0; - this.propertiesArr = new AbstractProperty[properties.size()]; - HashMap> propMap = new HashMap<>(); - - int bitOffset = 0; - for (Map.Entry> entry : properties.entrySet()) { - PropertyKey key = PropertyKey.getOrCreate(entry.getKey()); - AbstractProperty property = ((AbstractProperty) entry.getValue()).withOffset(bitOffset); - this.propertiesMapArr[key.ordinal()] = property; - this.propertiesArr[prop_arr_i++] = property; - propMap.put(entry.getKey(), property); - - maxInternalStateId += (property.getValues().size() << bitOffset); - bitOffset += property.getNumBits(); - } - this.propertiesList = Arrays.asList(this.propertiesArr); - this.propertiesMap = Collections.unmodifiableMap(propMap); - this.propertiesSet = new LinkedHashSet<>(this.propertiesMap.values()); - } else { - this.propertiesMapArr = new AbstractProperty[0]; - this.propertiesArr = this.propertiesMapArr; - this.propertiesList = Collections.emptyList(); - this.propertiesMap = Collections.emptyMap(); - this.propertiesSet = Collections.emptySet(); - } - this.permutations = maxInternalStateId; - - this.blockMaterial = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry().getMaterial(type); - - if (!propertiesList.isEmpty()) { - this.stateOrdinals = generateStateOrdinals(internalId, states.size(), maxInternalStateId, propertiesList); - - for (int propId = 0; propId < this.stateOrdinals.length; propId++) { - int ordinal = this.stateOrdinals[propId]; - if (ordinal != -1) { - int stateId = internalId + (propId << BlockTypes.BIT_OFFSET); - BlockState state = new BlockState(type, stateId, ordinal); - states.add(state); - } - } - int defaultPropId = parseProperties(propertyString, propertiesMap) >> BlockTypes.BIT_OFFSET; - - this.defaultState = states.get(this.stateOrdinals[defaultPropId]); - } else { - this.defaultState = new BlockState(type, internalId, states.size()); - states.add(this.defaultState); - } - } - - private int parseProperties(String properties, Map> propertyMap) { - int id = internalId; - for (String keyPair : properties.split(",")) { - String[] split = keyPair.split("="); - String name = split[0]; - String value = split[1]; - AbstractProperty btp = propertyMap.get(name); - id = btp.modify(id, btp.getValueFor(value)); - } - return id; + private static Field[] fieldsTmp; + private static JoinedCharSequence joined; + private static int initIndex = 0; + public static BlockType init() { + if (fieldsTmp == null) { + fieldsTmp = BlockTypes.class.getDeclaredFields(); + BlockTypesCache.$NAMESPACES.isEmpty(); // initialize cache + joined = new JoinedCharSequence(); } + String name = fieldsTmp[initIndex++].getName().toLowerCase(Locale.ROOT); + CharSequence fullName = joined.init(BlockType.REGISTRY.getDefaultNamespace(), ':', name); + return BlockType.REGISTRY.getMap().get(fullName); } - - - private static int[] generateStateOrdinals(int internalId, int ordinal, int maxStateId, List> props) { - if (props.isEmpty()) return null; - int[] result = new int[maxStateId]; - Arrays.fill(result, -1); - int[] state = new int[props.size()]; - int[] sizes = new int[props.size()]; - for (int i = 0; i < props.size(); i++) { - sizes[i] = props.get(i).getValues().size(); - } - int index = 0; - outer: - while (true) { - // Create the state - int stateId = internalId; - for (int i = 0; i < state.length; i++) { - stateId = props.get(i).modifyIndex(stateId, state[i]); - } - // Map it to the ordinal - result[stateId >> BlockTypes.BIT_OFFSET] = ordinal++; - // Increment the state - while (++state[index] == sizes[index]) { - state[index] = 0; - index++; - if (index == state.length) break outer; - } - index = 0; - } - return result; - } - - /* - ----------------------------------------------------- - Static Initializer - ----------------------------------------------------- - */ - - public static final int BIT_OFFSET; // Used internally - protected static final int BIT_MASK; // Used internally - -// private static final Map $REGISTRY = new HashMap<>(); -// public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("block type", $REGISTRY); - - public static final BlockType[] values; - public static final BlockState[] states; - - private static final Set $NAMESPACES = new LinkedHashSet<>(); - static { - try { - ArrayList stateList = new ArrayList<>(); - - Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS); - Registries registries = platform.getRegistries(); - BlockRegistry blockReg = registries.getBlockRegistry(); - Collection blocks = blockReg.registerBlocks(); - Map blockMap = blocks.stream().collect(Collectors.toMap(item -> item.charAt(item.length() - 1) == ']' ? item.substring(0, item.indexOf('[')) : item, item -> item)); - - int size = blockMap.size(); - for (Field field : BlockID.class.getDeclaredFields()) { - size = Math.max(field.getInt(null) + 1, size); - } - BIT_OFFSET = MathMan.log2nlz(size); - BIT_MASK = ((1 << BIT_OFFSET) - 1); - values = new BlockType[size]; - - // Register the statically declared ones first - Field[] oldFields = BlockID.class.getDeclaredFields(); - for (Field field : oldFields) { - if (field.getType() == int.class) { - int internalId = field.getInt(null); - String id = "minecraft:" + field.getName().toLowerCase(Locale.ROOT); - String defaultState = blockMap.remove(id); - if (defaultState == null) { - if (internalId != 0) { - System.out.println("Ignoring invalid block " + id); - continue; - } - defaultState = id; - } - if (values[internalId] != null) { - throw new IllegalStateException("Invalid duplicate id for " + field.getName()); - } - BlockType type = register(defaultState, internalId, stateList); - // Note: Throws IndexOutOfBoundsError if nothing is registered and blocksMap is empty - values[internalId] = type; - } - } - - // Register new blocks - int internalId = 1; - for (Map.Entry entry : blockMap.entrySet()) { - String defaultState = entry.getValue(); - // Skip already registered ids - while(values[internalId] != null) { - internalId++; - } - BlockType type = register(defaultState, internalId, stateList); - values[internalId] = type; - } - - states = stateList.toArray(new BlockState[stateList.size()]); - - - // Init deprecated - SIGN = OAK_SIGN; - WALL_SIGN = OAK_WALL_SIGN; - } catch (Throwable e) { - e.printStackTrace(); - throw new RuntimeException(e); - } + fieldsTmp = null; + joined = null; } - private static BlockType register(final String id, int internalId, List states) { - // Get the enum name (remove namespace if minecraft:) - int propStart = id.indexOf('['); - String typeName = id.substring(0, propStart == -1 ? id.length() : propStart); - String enumName = (typeName.startsWith("minecraft:") ? typeName.substring(10) : typeName).toUpperCase(Locale.ROOT); - BlockType existing = new BlockType(id, internalId, states); - - - // Set field value - try { - Field field = BlockTypes.class.getDeclaredField(enumName); - ReflectionUtils.setFailsafeFieldValue(field, null, existing); - } catch (NoSuchFieldException | IllegalAccessException e) { - e.printStackTrace(); - } - - // register states - BlockType.REGISTRY.register(typeName, existing); - String nameSpace = typeName.substring(0, typeName.indexOf(':')); - $NAMESPACES.add(nameSpace); - return existing; - } - - /* ----------------------------------------------------- Parsing @@ -990,7 +776,7 @@ public final class BlockTypes { } catch (NumberFormatException | IndexOutOfBoundsException e) { } - throw new SuggestInputParseException("Does not match a valid block type: " + inputLower, inputLower, () -> Stream.of(BlockTypes.values) + throw new SuggestInputParseException("Does not match a valid block type: " + inputLower, inputLower, () -> Stream.of(BlockTypesCache.values) .filter(b -> StringMan.blockStateMatches(inputLower, b.getId())) .map(BlockType::getId) .sorted(StringMan.blockStateComparator(inputLower)) @@ -999,7 +785,7 @@ public final class BlockTypes { } public static Set getNameSpaces() { - return $NAMESPACES; + return BlockTypesCache.$NAMESPACES; } @Nullable @@ -1014,21 +800,21 @@ public final class BlockTypes { @Deprecated public static BlockType get(final int ordinal) { - return values[ordinal]; + return BlockTypesCache.values[ordinal]; } @Deprecated public static BlockType getFromStateId(final int internalStateId) { - return values[internalStateId & BIT_MASK]; + return BlockTypesCache.values[internalStateId & BlockTypesCache.BIT_MASK]; } @Deprecated public static BlockType getFromStateOrdinal(final int internalStateOrdinal) { - return states[internalStateOrdinal].getBlockType(); + return BlockTypesCache.states[internalStateOrdinal].getBlockType(); } public static int size() { - return values.length; + return BlockTypesCache.values.length; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypesCache.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypesCache.java new file mode 100644 index 000000000..52d6216f5 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypesCache.java @@ -0,0 +1,243 @@ +package com.sk89q.worldedit.world.block; + +import com.boydti.fawe.util.MathMan; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.registry.state.AbstractProperty; +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.registry.state.PropertyKey; +import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.world.registry.BlockRegistry; +import com.sk89q.worldedit.world.registry.Registries; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class BlockTypesCache { + /* + ----------------------------------------------------- + Settings + ----------------------------------------------------- + */ + protected final static class Settings { + protected final int internalId; + protected final BlockState defaultState; + protected final AbstractProperty[] propertiesMapArr; + protected final AbstractProperty[] propertiesArr; + protected final List> propertiesList; + protected final Map> propertiesMap; + protected final Set> propertiesSet; + protected final BlockMaterial blockMaterial; + protected final int permutations; + protected int[] stateOrdinals; + + Settings(BlockType type, String id, int internalId, List states) { + this.internalId = internalId; + String propertyString = null; + int propI = id.indexOf('['); + if (propI != -1) { + propertyString = id.substring(propI + 1, id.length() - 1); + } + + int maxInternalStateId = 0; + Map> properties = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry().getProperties(type); + if (!properties.isEmpty()) { + // Ensure the properties are registered + int maxOrdinal = 0; + for (String key : properties.keySet()) { + maxOrdinal = Math.max(PropertyKey.getOrCreate(key).ordinal(), maxOrdinal); + } + this.propertiesMapArr = new AbstractProperty[maxOrdinal + 1]; + int prop_arr_i = 0; + this.propertiesArr = new AbstractProperty[properties.size()]; + HashMap> propMap = new HashMap<>(); + + int bitOffset = 0; + for (Map.Entry> entry : properties.entrySet()) { + PropertyKey key = PropertyKey.getOrCreate(entry.getKey()); + AbstractProperty property = ((AbstractProperty) entry.getValue()).withOffset(bitOffset); + this.propertiesMapArr[key.ordinal()] = property; + this.propertiesArr[prop_arr_i++] = property; + propMap.put(entry.getKey(), property); + + maxInternalStateId += (property.getValues().size() << bitOffset); + bitOffset += property.getNumBits(); + } + this.propertiesList = Arrays.asList(this.propertiesArr); + this.propertiesMap = Collections.unmodifiableMap(propMap); + this.propertiesSet = new LinkedHashSet<>(this.propertiesMap.values()); + } else { + this.propertiesMapArr = new AbstractProperty[0]; + this.propertiesArr = this.propertiesMapArr; + this.propertiesList = Collections.emptyList(); + this.propertiesMap = Collections.emptyMap(); + this.propertiesSet = Collections.emptySet(); + } + this.permutations = maxInternalStateId; + + this.blockMaterial = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry().getMaterial(type); + + if (!propertiesList.isEmpty()) { + this.stateOrdinals = generateStateOrdinals(internalId, states.size(), maxInternalStateId, propertiesList); + + for (int propId = 0; propId < this.stateOrdinals.length; propId++) { + int ordinal = this.stateOrdinals[propId]; + if (ordinal != -1) { + int stateId = internalId + (propId << BIT_OFFSET); + BlockState state = new BlockState(type, stateId, ordinal); + states.add(state); + } + } + int defaultPropId = parseProperties(propertyString, propertiesMap) >> BIT_OFFSET; + + this.defaultState = states.get(this.stateOrdinals[defaultPropId]); + } else { + this.defaultState = new BlockState(type, internalId, states.size()); + states.add(this.defaultState); + } + } + + private int parseProperties(String properties, Map> propertyMap) { + int id = internalId; + for (String keyPair : properties.split(",")) { + String[] split = keyPair.split("="); + String name = split[0]; + String value = split[1]; + AbstractProperty btp = propertyMap.get(name); + id = btp.modify(id, btp.getValueFor(value)); + } + return id; + } + } + + + private static int[] generateStateOrdinals(int internalId, int ordinal, int maxStateId, List> props) { + if (props.isEmpty()) return null; + int[] result = new int[maxStateId]; + Arrays.fill(result, -1); + int[] state = new int[props.size()]; + int[] sizes = new int[props.size()]; + for (int i = 0; i < props.size(); i++) { + sizes[i] = props.get(i).getValues().size(); + } + int index = 0; + outer: + while (true) { + // Create the state + int stateId = internalId; + for (int i = 0; i < state.length; i++) { + stateId = props.get(i).modifyIndex(stateId, state[i]); + } + // Map it to the ordinal + result[stateId >> BIT_OFFSET] = ordinal++; + // Increment the state + while (++state[index] == sizes[index]) { + state[index] = 0; + index++; + if (index == state.length) break outer; + } + index = 0; + } + return result; + } + + /* + ----------------------------------------------------- + Static Initializer + ----------------------------------------------------- + */ + + public static final int BIT_OFFSET; // Used internally + protected static final int BIT_MASK; // Used internally + +// private static final Map $REGISTRY = new HashMap<>(); +// public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("block type", $REGISTRY); + + public static final BlockType[] values; + public static final BlockState[] states; + + protected static final Set $NAMESPACES = new LinkedHashSet<>(); + + static { + try { + ArrayList stateList = new ArrayList<>(); + + Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS); + Registries registries = platform.getRegistries(); + BlockRegistry blockReg = registries.getBlockRegistry(); + Collection blocks = blockReg.registerBlocks(); + Map blockMap = blocks.stream().collect(Collectors.toMap(item -> item.charAt(item.length() - 1) == ']' ? item.substring(0, item.indexOf('[')) : item, item -> item)); + + int size = blockMap.size(); + Field[] idFields = BlockID.class.getDeclaredFields(); + for (Field field : idFields) size = Math.max(field.getInt(null) + 1, size); + BIT_OFFSET = MathMan.log2nlz(size); + BIT_MASK = ((1 << BIT_OFFSET) - 1); + values = new BlockType[size]; + + // Register the statically declared ones first + for (Field field : idFields) { + if (field.getType() == int.class) { + int internalId = field.getInt(null); + String id = "minecraft:" + field.getName().toLowerCase(Locale.ROOT); + String defaultState = blockMap.remove(id); + if (defaultState == null) { + if (internalId != 0) { + System.out.println("Ignoring invalid block " + id); + continue; + } + defaultState = id; + } + if (values[internalId] != null) { + throw new IllegalStateException("Invalid duplicate id for " + field.getName()); + } + BlockType type = register(defaultState, internalId, stateList); + // Note: Throws IndexOutOfBoundsError if nothing is registered and blocksMap is empty + values[internalId] = type; + } + } + + { // Register new blocks + int internalId = 1; + for (Map.Entry entry : blockMap.entrySet()) { + String defaultState = entry.getValue(); + // Skip already registered ids + for (; values[internalId] != null; internalId++); + BlockType type = register(defaultState, internalId, stateList); + values[internalId] = type; + } + } + + states = stateList.toArray(new BlockState[stateList.size()]); + + + } catch (Throwable e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + private static BlockType register(final String id, int internalId, List states) { + // Get the enum name (remove namespace if minecraft:) + int propStart = id.indexOf('['); + String typeName = id.substring(0, propStart == -1 ? id.length() : propStart); + String enumName = (typeName.startsWith("minecraft:") ? typeName.substring(10) : typeName).toUpperCase(Locale.ROOT); + BlockType existing = new BlockType(id, internalId, states); + // register states + BlockType.REGISTRY.register(typeName, existing); + String nameSpace = typeName.substring(0, typeName.indexOf(':')); + $NAMESPACES.add(nameSpace); + return existing; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/ImmutableBaseBlock.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/ImmutableBaseBlock.java index bb86f0b65..08469e2c6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/ImmutableBaseBlock.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/ImmutableBaseBlock.java @@ -1,6 +1,5 @@ package com.sk89q.worldedit.world.block; -import com.boydti.fawe.beta.FilterBlock; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java index 0c4feae79..f86b04d9b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java @@ -57,7 +57,7 @@ import java.util.Map; public final class LegacyMapper { private static final Logger log = LoggerFactory.getLogger(LegacyMapper.class); - private static LegacyMapper INSTANCE; + private static LegacyMapper INSTANCE = new LegacyMapper(); private final Int2ObjectArrayMap blockStateToLegacyId4Data = new Int2ObjectArrayMap<>(); private final Int2ObjectArrayMap extraId4DataToStateId = new Int2ObjectArrayMap<>(); @@ -267,10 +267,7 @@ public final class LegacyMapper { } } - public static LegacyMapper getInstance() { - if (INSTANCE == null) { - INSTANCE = new LegacyMapper(); - } + public final static LegacyMapper getInstance() { return INSTANCE; } diff --git a/worldedit-libs/core/build.gradle.kts b/worldedit-libs/core/build.gradle.kts index 4a115d889..1b4dbc30d 100644 --- a/worldedit-libs/core/build.gradle.kts +++ b/worldedit-libs/core/build.gradle.kts @@ -9,7 +9,6 @@ dependencies { exclude(group = "junit", module = "junit") } "shade"("com.github.luben:zstd-jni:1.4.3-1") - "shade"("com.thoughtworks.paranamer:paranamer:2.6") "shade"("com.sk89q.lib:jlibnoise:1.0.0") "shade"("FAWE-Piston:core/build/libs/core-${Versions.PISTON}:lastSuccessfulBuild@jar") "shade"("FAWE-Piston:core-ap/runtime/build/libs/runtime-${Versions.PISTON}:lastSuccessfulBuild@jar")