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 b076bcd74..c38f130dd 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 @@ -36,9 +36,10 @@ import java.util.Arrays; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.locks.Lock; +import java.util.function.Function; import java.util.function.Supplier; -public class BukkitAdapter_1_14 { +public final class BukkitAdapter_1_14 { /* NMS fields @@ -87,13 +88,13 @@ public class BukkitAdapter_1_14 { } catch (NoSuchFieldException paper) { tmp = DataPaletteBlock.class.getDeclaredField("j"); } - fieldLock = tmp; - fieldLock.setAccessible(true); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); - int modifiers = modifiersField.getInt(fieldLock); + int modifiers = modifiersField.getInt(tmp); int newModifiers = modifiers & (~Modifier.FINAL); - if (newModifiers != modifiers) modifiersField.setInt(fieldLock, newModifiers); + if (newModifiers != modifiers) modifiersField.setInt(tmp, newModifiers); + fieldLock = tmp; + fieldLock.setAccessible(true); } Unsafe unsafe = UnsafeUtils.getUNSAFE(); @@ -213,23 +214,41 @@ public class BukkitAdapter_1_14 { /* NMS conversion */ - public static ChunkSection newChunkSection(final int layer, final char[] blocks) { - ChunkSection section = new ChunkSection(layer << 4); - if (blocks == null) { - return section; + return newChunkSection(layer, null, blocks); + } + + private static int createPalette(int layer, int[] blockToPalette, int[] paletteToBlock, int[] blocksCopy, int[] num_palette_buffer, Function get, char[] set) { + int air = 0; + int num_palette = 0; + int i = 0; + outer: + for (; i < 4096; i++) { + char ordinal = set[i]; + switch (ordinal) { + case 0: + break outer; + case BlockID.AIR: + case BlockID.CAVE_AIR: + case BlockID.VOID_AIR: + air++; + } + int palette = blockToPalette[ordinal]; + if (palette == Integer.MAX_VALUE) { + blockToPalette[ordinal] = palette = num_palette; + paletteToBlock[num_palette] = ordinal; + num_palette++; + } + blocksCopy[i] = palette; } - final int[] blockToPalette = FaweCache.IMP.BLOCK_TO_PALETTE.get(); - final int[] paletteToBlock = FaweCache.IMP.PALETTE_TO_BLOCK.get(); - final long[] blockStates = FaweCache.IMP.BLOCK_STATES.get(); - final int[] blocksCopy = FaweCache.IMP.SECTION_BLOCKS.get(); - try { - int num_palette = 0; - int air = 0; - for (int i = 0; i < 4096; i++) { - char ordinal = blocks[i]; + if (i != 4096) { + char[] getArr = get.apply(layer); + for (; i < 4096; i++) { + char ordinal = set[i]; switch (ordinal) { case 0: + ordinal = getArr[i]; + set[i] = ordinal; case BlockID.AIR: case BlockID.CAVE_AIR: case BlockID.VOID_AIR: @@ -243,7 +262,54 @@ public class BukkitAdapter_1_14 { } blocksCopy[i] = palette; } + } + num_palette_buffer[0] = num_palette; + return air; + } + private static int createPalette(int[] blockToPalette, int[] paletteToBlock, int[] blocksCopy, int[] num_palette_buffer, char[] set) { + int air = 0; + int num_palette = 0; + char airOrdinal = BlockTypes.AIR.getDefaultState().getOrdinalChar(); + for (int i = 0; i < 4096; i++) { + char ordinal = set[i]; + switch (ordinal) { + case 0: + ordinal = airOrdinal; + case BlockID.AIR: + case BlockID.CAVE_AIR: + case BlockID.VOID_AIR: + air++; + } + int palette = blockToPalette[ordinal]; + if (palette == Integer.MAX_VALUE) { + blockToPalette[ordinal] = palette = num_palette; + paletteToBlock[num_palette] = ordinal; + num_palette++; + } + blocksCopy[i] = palette; + } + num_palette_buffer[0] = num_palette; + return air; + } + + public static ChunkSection newChunkSection(final int layer, final Function get, char[] set) { + if (set == null) { + return newChunkSection(layer); + } + final int[] blockToPalette = FaweCache.IMP.BLOCK_TO_PALETTE.get(); + final int[] paletteToBlock = FaweCache.IMP.PALETTE_TO_BLOCK.get(); + final long[] blockStates = FaweCache.IMP.BLOCK_STATES.get(); + final int[] blocksCopy = FaweCache.IMP.SECTION_BLOCKS.get(); + try { + int[] num_palette_buffer = new int[1]; + int air; + if (get == null) { + air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, set); + } else { + air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, get, set); + } + int num_palette = num_palette_buffer[0]; // BlockStates int bitsPerEntry = MathMan.log2nlz(num_palette - 1); if (Settings.IMP.PROTOCOL_SUPPORT_FIX || num_palette != 1) { @@ -260,6 +326,7 @@ public class BukkitAdapter_1_14 { bitArray.fromRaw(blocksCopy); } + ChunkSection section = newChunkSection(layer); // set palette & data bits final DataPaletteBlock dataPaletteBlocks = section.getBlocks(); // private DataPalette h; @@ -294,6 +361,10 @@ public class BukkitAdapter_1_14 { } } + private static ChunkSection newChunkSection(int layer) { + return new ChunkSection(layer << 4); + } + public static void setCount(final int tickingBlockCount, final int nonEmptyBlockCount, final ChunkSection section) throws NoSuchFieldException, IllegalAccessException { fieldFluidCount.setShort(section, (short) 0); // TODO FIXME fieldTickingBlockCount.setShort(section, (short) tickingBlockCount); 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 8bff62644..e4dd1e5f6 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 @@ -300,19 +300,12 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { this.reset(layer); } } - char[] getArr = this.load(layer); - for (int i = 0; i < 4096; i++) { - char value = setArr[i]; - if (value != 0) { - getArr[i] = value; - } - } - newSection = BukkitAdapter_1_14.newChunkSection(layer, getArr); + newSection = BukkitAdapter_1_14.newChunkSection(layer, this::load, setArr); if (!BukkitAdapter_1_14.setSectionAtomic(sections, existingSection, newSection, layer)) { System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); continue; } else { - updateGet(this, nmsChunk, sections, newSection, getArr, layer); + updateGet(this, nmsChunk, sections, newSection, setArr, layer); } } } 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 63002f492..2a8a8b63f 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 @@ -12,6 +12,7 @@ import java.io.IOException; import java.util.Arrays; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; import java.util.zip.Deflater; public abstract class MCAWriter implements Extent { @@ -74,6 +75,15 @@ public abstract class MCAWriter implements Extent { public abstract MCAChunk write(MCAChunk input, int startX, int endX, int startZ, int endZ); + private static CleanableThreadLocal createCache() { + return new CleanableThreadLocal<>(() -> { + MCAChunk chunk = new MCAChunk(); + Arrays.fill(chunk.blocks, (char) BlockID.AIR); +// Arrays.fill(chunk.skyLight, (byte) 255); + return chunk; + }); + } + public void generate() throws IOException { if (!folder.exists()) { folder.mkdirs(); @@ -81,122 +91,116 @@ public abstract class MCAWriter implements Extent { final ForkJoinPool pool = new ForkJoinPool(); int tcx = (width - 1) >> 4; int tcz = (length - 1) >> 4; - 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]); - final ThreadLocal byteStore2 = ThreadLocal.withInitial(() -> new byte[500000]); - final ThreadLocal deflateStore = ThreadLocal - .withInitial(() -> new Deflater(Deflater.BEST_SPEED, false)); - byte[] fileBuf = new byte[1 << 16]; - int mcaXMin = 0; - int mcaZMin = 0; - int mcaXMax = mcaXMin + ((width - 1) >> 9); - int mcaZMax = mcaZMin + ((length - 1) >> 9); + try (CleanableThreadLocal chunkStore = createCache()){ + final ThreadLocal byteStore1 = ThreadLocal.withInitial(() -> new byte[500000]); + final ThreadLocal byteStore2 = ThreadLocal.withInitial(() -> new byte[500000]); + final ThreadLocal deflateStore = ThreadLocal + .withInitial(() -> new Deflater(Deflater.BEST_SPEED, false)); + byte[] fileBuf = new byte[1 << 16]; + int mcaXMin = 0; + int mcaZMin = 0; + int mcaXMax = mcaXMin + ((width - 1) >> 9); + int mcaZMax = mcaZMin + ((length - 1) >> 9); - final byte[] header = new byte[4096]; + final byte[] header = new byte[4096]; - for (int mcaZ = mcaXMin; mcaZ <= mcaZMax; mcaZ++) { - for (int mcaX = mcaXMin; mcaX <= mcaXMax; mcaX++) { - File file = new File(folder, "r." + (mcaX + (getOffsetX() >> 9)) + "." + (mcaZ + (getOffsetZ() >> 9)) + ".mca"); - if (!file.exists()) { - file.createNewFile(); - } - final BufferedRandomAccessFile raf = new BufferedRandomAccessFile(file, "rw", fileBuf); - final byte[][] compressed = new byte[1024][]; - int bx = mcaX << 9; - int bz = mcaZ << 9; - int scx = bx >> 4; - int ecx = Math.min(scx + 31, tcx); - int scz = bz >> 4; - int ecz = Math.min(scz + 31, tcz); - for (int cz = scz; cz <= ecz; cz++) { - final int csz = cz << 4; - final int cez = Math.min(csz + 15, length - 1); - for (int cx = scx; cx <= ecx; cx++) { - final int csx = cx << 4; - final int cex = Math.min(csx + 15, width - 1); - final int fcx = cx; - final int fcz = cz; - if (shouldWrite(cx, cz)) { - pool.submit(() -> { - try { - MCAChunk chunk = chunkStore.get(); - chunk.reset(); - chunk.setPosition(fcx, fcz); - chunk = write(chunk, csx, cex, csz, cez); - if (chunk != null) { - // Generation offset - chunk.setPosition( fcx + (getOffsetX() >> 4), fcz + (getOffsetZ() >> 4)); + for (int mcaZ = mcaXMin; mcaZ <= mcaZMax; mcaZ++) { + for (int mcaX = mcaXMin; mcaX <= mcaXMax; mcaX++) { + File file = new File(folder, "r." + (mcaX + (getOffsetX() >> 9)) + "." + (mcaZ + (getOffsetZ() >> 9)) + ".mca"); + if (!file.exists()) { + file.createNewFile(); + } + final BufferedRandomAccessFile raf = new BufferedRandomAccessFile(file, "rw", fileBuf); + final byte[][] compressed = new byte[1024][]; + int bx = mcaX << 9; + int bz = mcaZ << 9; + int scx = bx >> 4; + int ecx = Math.min(scx + 31, tcx); + int scz = bz >> 4; + int ecz = Math.min(scz + 31, tcz); + for (int cz = scz; cz <= ecz; cz++) { + final int csz = cz << 4; + final int cez = Math.min(csz + 15, length - 1); + for (int cx = scx; cx <= ecx; cx++) { + final int csx = cx << 4; + final int cex = Math.min(csx + 15, width - 1); + final int fcx = cx; + final int fcz = cz; + if (shouldWrite(cx, cz)) { + pool.submit(() -> { + try { + MCAChunk chunk = chunkStore.get(); + chunk.reset(); + chunk.setPosition(fcx, fcz); + chunk = write(chunk, csx, cex, csz, cez); + if (chunk != null) { + // Generation offset + chunk.setPosition(fcx + (getOffsetX() >> 4), fcz + (getOffsetZ() >> 4)); - // Compress - byte[] bytes = chunk.toBytes(byteStore1.get()); - byte[] compressedBytes = MainUtil.compress(bytes, byteStore2.get(), deflateStore.get()); + // Compress + byte[] bytes = chunk.toBytes(byteStore1.get()); + byte[] compressedBytes = MainUtil.compress(bytes, byteStore2.get(), deflateStore.get()); - // TODO optimize (avoid cloning) by add a synchronized block and write to the RAF here instead of below - compressed[((fcx & 31)) + ((fcz & 31) << 5)] = compressedBytes.clone(); + // TODO optimize (avoid cloning) by add a synchronized block and write to the RAF here instead of below + compressed[((fcx & 31)) + ((fcz & 31) << 5)] = compressedBytes.clone(); + } + } catch (Throwable e) { + e.printStackTrace(); } - } catch (Throwable e) { - e.printStackTrace(); - } - }); + }); + } } } - } - pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); - pool.submit(() -> { - try { - int totalLength = 8192; - for (byte[] compressedBytes : compressed) { - if (compressedBytes != null) { - int blocks = ((4095 + compressedBytes.length + 5) / 4096) * 4096; - totalLength += blocks; - } - } - raf.setLength(totalLength); - int offset = 8192; - for (int i = 0; i < compressed.length; i++) { - byte[] compressedBytes = compressed[i]; - if (compressedBytes != null) { - // Set header - int index = i << 2; - int offsetMedium = offset >> 12; - int blocks = ((4095 + compressedBytes.length + 5) / 4096); - header[index] = (byte) (offsetMedium >> 16); - header[index + 1] = (byte) ((offsetMedium >> 8)); - header[index + 2] = (byte) ((offsetMedium >> 0)); - header[index + 3] = (byte) (blocks); - // Write bytes - raf.seek(offset); - raf.writeInt(compressedBytes.length + 1); - raf.write(2); - raf.write(compressedBytes); - offset += blocks * 4096; - } - } - raf.seek(0); - raf.write(header); - } catch (IOException e) { - e.printStackTrace(); - } finally { + pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + pool.submit(() -> { try { - raf.close(); + int totalLength = 8192; + for (byte[] compressedBytes : compressed) { + if (compressedBytes != null) { + int blocks = ((4095 + compressedBytes.length + 5) / 4096) * 4096; + totalLength += blocks; + } + } + raf.setLength(totalLength); + int offset = 8192; + for (int i = 0; i < compressed.length; i++) { + byte[] compressedBytes = compressed[i]; + if (compressedBytes != null) { + // Set header + int index = i << 2; + int offsetMedium = offset >> 12; + int blocks = ((4095 + compressedBytes.length + 5) / 4096); + header[index] = (byte) (offsetMedium >> 16); + header[index + 1] = (byte) ((offsetMedium >> 8)); + header[index + 2] = (byte) ((offsetMedium >> 0)); + header[index + 3] = (byte) (blocks); + // Write bytes + raf.seek(offset); + raf.writeInt(compressedBytes.length + 1); + raf.write(2); + raf.write(compressedBytes); + offset += blocks * 4096; + } + } + raf.seek(0); + raf.write(header); } catch (IOException e) { e.printStackTrace(); + } finally { + try { + raf.close(); + } catch (IOException e) { + e.printStackTrace(); + } } - } - }); + }); + } } + pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + pool.shutdown(); + CleanableThreadLocal.clean(byteStore1); + CleanableThreadLocal.clean(byteStore2); + CleanableThreadLocal.clean(deflateStore); } - pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); - pool.shutdown(); - CleanableThreadLocal.clean(byteStore1); - CleanableThreadLocal.clean(byteStore2); - CleanableThreadLocal.clean(deflateStore); } } 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 9a1c6ff34..2f61a48ea 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 @@ -2,6 +2,8 @@ package com.boydti.fawe.object.collection; import com.boydti.fawe.util.MainUtil; +import java.io.Closeable; +import java.io.IOException; import java.lang.ref.Reference; import java.lang.reflect.Array; import java.lang.reflect.Field; @@ -14,7 +16,7 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -public class CleanableThreadLocal extends ThreadLocal { +public class CleanableThreadLocal extends ThreadLocal implements Closeable { private final Supplier supplier; private final Function modifier; private LongAdder count = new LongAdder(); @@ -167,4 +169,9 @@ public class CleanableThreadLocal extends ThreadLocal { clean(this); super.finalize(); } + + @Override + public void close() throws IOException { + clean(); + } } 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 df3ba9827..60ab33cfe 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 @@ -164,7 +164,7 @@ public class BlockType implements FawePattern, Keyed { * * @return The default state */ - public BlockState getDefaultState() { + public final BlockState getDefaultState() { return this.settings.defaultState; }