From 31797d4231b1682e3b10c6b340cf3ea305d8cf6c Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Wed, 10 Apr 2019 18:32:21 +1000 Subject: [PATCH] WIP on 1.13 CFI --- .../boydti/fawe/bukkit/v0/BukkitQueue_0.java | 5 - .../fawe/bukkit/v0/BukkitQueue_All.java | 2 - .../fawe/example/NMSMappedFaweQueue.java | 2 - .../boydti/fawe/jnbt/anvil/BitArray4096.java | 163 +++++ .../jnbt/anvil/HeightMapMCAGenerator.java | 564 ++++++------------ .../com/boydti/fawe/jnbt/anvil/MCAWriter.java | 133 ++--- .../fawe/jnbt/anvil/WritableMCAChunk.java | 398 ++++++++++++ .../java/com/sk89q/jnbt/NBTOutputStream.java | 73 ++- .../java/com/sk89q/worldedit/EditSession.java | 2 +- 9 files changed, 895 insertions(+), 447 deletions(-) create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/BitArray4096.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/WritableMCAChunk.java diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java index 3f133cc3c..bcb83b6cd 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java @@ -293,11 +293,6 @@ public abstract class BukkitQueue_0 extends NMSMa return result; } - @Override - public IntFaweChunk getPrevious(IntFaweChunk fs, CHUNKSECTIONS sections, Map tiles, Collection[] entities, Set createdEntities, boolean all) throws Exception { - return fs; - } - @Override public boolean hasSky() { World world = getWorld(); diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java index 32c2c8436..60b3fe0f1 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java @@ -349,8 +349,6 @@ public class BukkitQueue_All extends BukkitQueue_0 ex public abstract void setBlockLight(SECTION section, int x, int y, int z, int value); public abstract void refreshChunk(FaweChunk fs); - - public abstract IntFaweChunk getPrevious(IntFaweChunk fs, CHUNKSECTION sections, Map tiles, Collection[] entities, Set createdEntities, boolean all) throws Exception; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/BitArray4096.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/BitArray4096.java new file mode 100644 index 000000000..d025aa5c6 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/BitArray4096.java @@ -0,0 +1,163 @@ +package com.boydti.fawe.jnbt.anvil; + +public final class BitArray4096 { + + private final int bitsPerEntry; + private final int maxSeqLocIndex; + private final int maxEntryValue; + private final long[] data; + private final int longLen; + + public BitArray4096(long[] buffer, int bitsPerEntry) { + this.bitsPerEntry = bitsPerEntry; + this.maxSeqLocIndex = 64 - bitsPerEntry; + maxEntryValue = (1 << bitsPerEntry) - 1; + this.longLen = (this.bitsPerEntry * 4096) >> 6; + if (buffer.length < longLen) { + System.out.println("Invalid buffer " + buffer.length + " | " + longLen); + this.data = new long[longLen]; + } else { + this.data = buffer; + } + } + + public BitArray4096(int bitsPerEntry) { + this.bitsPerEntry = bitsPerEntry; + this.maxSeqLocIndex = 64 - bitsPerEntry; + maxEntryValue = (1 << bitsPerEntry) - 1; + this.longLen = (this.bitsPerEntry * 4096) >> 6; + this.data = new long[longLen]; + } + + public final void setAt(int index, int value) { + if (longLen == 0) return; + int bitIndexStart = index * bitsPerEntry; + int longIndexStart = bitIndexStart >> 6; + int localBitIndexStart = bitIndexStart & 63; + this.data[longIndexStart] = this.data[longIndexStart] & ~((long) maxEntryValue << localBitIndexStart) | ((long) value) << localBitIndexStart; + + if(localBitIndexStart > maxSeqLocIndex) { + int longIndexEnd = longIndexStart + 1; + int localShiftStart = 64 - localBitIndexStart; + int localShiftEnd = bitsPerEntry - localShiftStart; + this.data[longIndexEnd] = this.data[longIndexEnd] >>> localShiftEnd << localShiftEnd | (((long) value) >> localShiftStart); + } + } + + public final int getAt(int index) { + if (longLen == 0) return 0; + int bitIndexStart = index * bitsPerEntry; + + int longIndexStart = bitIndexStart >> 6; + + int localBitIndexStart = bitIndexStart & 63; + if(localBitIndexStart <= maxSeqLocIndex) { + return (int)(this.data[longIndexStart] >>> localBitIndexStart & maxEntryValue); + } else { + int localShift = 64 - localBitIndexStart; + return (int) ((this.data[longIndexStart] >>> localBitIndexStart | this.data[longIndexStart + 1] << localShift) & maxEntryValue); + } + } + + public int getLength() { + return longLen; + } + + public final void fromRawSlow(char[] arr) { + for (int i = 0; i < arr.length; i++) { + setAt(i, arr[i]); + } + } + + public final void fromRaw(int[] arr, int offset) { + final long[] data = this.data; + final int bitsPerEntry = this.bitsPerEntry; + final int maxEntryValue = this.maxEntryValue; + final int maxSeqLocIndex = this.maxSeqLocIndex; + + int localStart = 0; + int lastVal; + int arrI = offset; + long l = 0; + long nextVal; + for (int i = 0; i < longLen; i++) { + for (; localStart <= maxSeqLocIndex; localStart += bitsPerEntry) { + lastVal = arr[arrI++]; + l |= ((long) lastVal << localStart); + } + if (localStart < 64) { + if (i != longLen - 1) { + lastVal = arr[arrI++]; + int shift = 64 - localStart; + + nextVal = lastVal >> shift; + + l |= ((lastVal - (nextVal << shift)) << localStart); + + data[i] = l; + data[i + 1] = l = nextVal; + + localStart -= maxSeqLocIndex; + } + } else { + localStart = 0; + data[i] = l; + l = 0; + } + } + } + + public BitArray4096 growSlow(int bitsPerEntry) { + BitArray4096 newBitArray = new BitArray4096(bitsPerEntry); + for (int i = 0; i < 4096; i++) { + newBitArray.setAt(i, getAt(i)); + } + return newBitArray; + } + + public final char[] toRawSlow() { + char[] arr = new char[4096]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (char) getAt(i); + } + return arr; + } + + public final char[] toRaw() { + return toRaw(new char[4096]); + } + + protected final char[] toRaw(char[] buffer) { + final long[] data = this.data; + final int dataLength = longLen; + final int bitsPerEntry = this.bitsPerEntry; + final int maxEntryValue = this.maxEntryValue; + final int maxSeqLocIndex = this.maxSeqLocIndex; + + int localStart = 0; + char lastVal; + int arrI = 0; + long l; + for (int i = 0; i < dataLength; i++) { + l = data[i]; + for (; localStart <= maxSeqLocIndex; localStart += bitsPerEntry) { + lastVal = (char) (l >>> localStart & maxEntryValue); + buffer[arrI++] = lastVal; + } + if (localStart < 64) { + if (i != dataLength - 1) { + lastVal = (char) (l >>> localStart); + localStart -= maxSeqLocIndex; + l = data[i + 1]; + int localShift = bitsPerEntry - localStart; + lastVal |= l << localShift; + lastVal &= maxEntryValue; + buffer[arrI++] = lastVal; + } + } else { + localStart = 0; + } + } + return buffer; + } +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java index 8f6bf65fb..2d176d0f2 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java @@ -1,7 +1,11 @@ package com.boydti.fawe.jnbt.anvil; import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; import com.boydti.fawe.example.SimpleIntFaweChunk; +import com.boydti.fawe.jnbt.anvil.HeightMapMCADrawer; +import com.boydti.fawe.jnbt.anvil.MCAChunk; +import com.boydti.fawe.jnbt.anvil.MCAWriter; import com.boydti.fawe.object.*; import com.boydti.fawe.object.brush.visualization.VirtualWorld; import com.boydti.fawe.object.change.StreamChange; @@ -77,7 +81,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr protected boolean randomVariation = true; protected int biomePriority = 0; protected int waterId = BlockTypes.WATER.getInternalId(); - protected int bedrockId = 7; + protected int bedrockId = BlockID.BEDROCK; protected boolean modifiedMain = false; @Override @@ -210,7 +214,6 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public HeightMapMCAGenerator(int width, int length, File regionFolder) { super(width, length, regionFolder); - int area = getArea(); blocks = new DifferentialBlockBuffer(width, length); heights = new DifferentialArray(new byte[getArea()]); @@ -296,7 +299,6 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int ecx = Math.min(lenCX - 1, pcx + 15); int ecz = Math.min(lenCZ - 1, pcz + 15); - MCAChunk chunk = new MCAChunk(this, 0, 0); for (int cz = scz; cz <= ecz; cz++) { for (int cx = scx; cx <= ecx; cx++) { final int finalCX = cx; @@ -517,7 +519,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int x = 0; x < getWidth(); x++, index++) { int height = img.getRGB(x, z) & 0xFF; if (height == 255 || height > 0 && !white && ThreadLocalRandom.current() - .nextInt(256) <= height) { + .nextInt(256) <= height) { int newHeight = table.average(x, z, index); setLayerHeightRaw(index, newHeight); } @@ -767,16 +769,15 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr return getSnapshot(null, chunkX, chunkZ); } - private FaweChunk getSnapshot(final MCAChunk chunk, int chunkX, int chunkZ) { - return new LazyFaweChunk(this, chunkX, chunkZ) { + private FaweChunk getSnapshot(final WritableMCAChunk chunk, int chunkX, int chunkZ) { + return new LazyFaweChunk(this, chunkX, chunkZ) { @Override - public MCAChunk getChunk() { - MCAChunk tmp = chunk; + public WritableMCAChunk getChunk() { + WritableMCAChunk tmp = chunk; if (tmp == null) { - tmp = new MCAChunk(HeightMapMCAGenerator.this, chunkX, chunkZ); - } else { - tmp.setLoc(HeightMapMCAGenerator.this, chunkX, chunkZ); + tmp = new WritableMCAChunk(); } + tmp.setLoc(HeightMapMCAGenerator.this, chunkX, chunkZ); int cbx = chunkX << 4; int cbz = chunkZ << 4; int csx = Math.max(0, cbx); @@ -790,7 +791,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr @Override public void addToQueue() { - MCAChunk cached = getCachedChunk(); + WritableMCAChunk cached = getCachedChunk(); if (cached != null) setChunk(cached); } }; @@ -1091,7 +1092,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int x = 0; x < getWidth(); x++, index++) { int height = img.getRGB(x, z) & 0xFF; if (height == 255 || height > 0 && !white && ThreadLocalRandom.current() - .nextInt(256) <= height) { + .nextInt(256) <= height) { biomeArr[index] = biomeByte; } } @@ -1143,7 +1144,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr if (imgMask != null) { int height = imgMask.getRGB(x, z) & 0xFF; if (height != 255 && (height <= 0 || !whiteOnly || ThreadLocalRandom - .current().nextInt(256) > height)) continue; + .current().nextInt(256) > height)) continue; } int color = img.getRGB(x, z); if (textureUtil.getIsBlockCloserThanBiome(buffer, color, primtives.biomePriority)) { @@ -1228,7 +1229,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int x = 0; x < getWidth(); x++, index++) { int height = mask.getRGB(x, z) & 0xFF; if (height == 255 || height > 0 && !white && ThreadLocalRandom.current() - .nextInt(256) <= height) { + .nextInt(256) <= height) { int color = img.getRGB(x, z); BlockType block = textureUtil.getNearestBlock(color); if (block != null) { @@ -1347,7 +1348,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr if (pattern instanceof BlockStateHolder) { setOverlay(img, ((BlockStateHolder) pattern).getInternalId(), white); } else if (pattern instanceof BlockType) { - setOverlay(img, ((BlockType) pattern).getInternalId(), white); + setOverlay(img, ((BlockType) pattern).getInternalId(), white); } else { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); @@ -1363,7 +1364,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int x = 0; x < getWidth(); x++, index++) { int height = img.getRGB(x, z) & 0xFF; if (height == 255 || height > 0 && !white && ThreadLocalRandom.current() - .nextInt(256) <= height) { + .nextInt(256) <= height) { mutable.mutX(x); mutable.mutY(height); overlayArr[index] = pattern.apply(mutable).getInternalId(); @@ -1391,7 +1392,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int x = 0; x < getWidth(); x++, index++) { int height = img.getRGB(x, z) & 0xFF; if (height == 255 || height > 0 && !white && ThreadLocalRandom.current() - .nextInt(256) <= height) { + .nextInt(256) <= height) { mutable.mutX(x); mutable.mutY(height); mainArr[index] = pattern.apply(mutable).getInternalId(); @@ -1417,7 +1418,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int x = 0; x < getWidth(); x++, index++) { int height = img.getRGB(x, z) & 0xFF; if (height == 255 || height > 0 && !white && ThreadLocalRandom.current() - .nextInt(256) <= height) { + .nextInt(256) <= height) { mutable.mutX(x); mutable.mutY(height); floorArr[index] = pattern.apply(mutable).getInternalId(); @@ -1445,7 +1446,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int x = 0; x < getWidth(); x++, index++) { int height = img.getRGB(x, z) & 0xFF; if (height == 255 || height > 0 && !white && ThreadLocalRandom.current() - .nextInt(256) <= height) { + .nextInt(256) <= height) { mutable.mutX(x); mutable.mutY(height); int combined = pattern.apply(mutable).getInternalId(); @@ -1649,328 +1650,159 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } @Override - public MCAChunk write(MCAChunk chunk, int csx, int cex, int csz, int cez) { - // TODO FIXME -// 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; -// try { -// int[] indexes = indexStore.get(); -// for (int i = 0; i < chunk.ids.length; i++) { -// byte[] idsArray = chunk.ids[i]; -// if (idsArray != null) { -// Arrays.fill(idsArray, (byte) 0); -// Arrays.fill(chunk.data[i], (byte) 0); -// } -// } -// int index; -// int maxY = 0; -// int minY = Integer.MAX_VALUE; -// int[] heightMap = chunk.getHeightMapArray(); -// int globalIndex; -// for (int z = csz; z <= cez; z++) { -// globalIndex = z * getWidth() + csx; -// index = (z & 15) << 4; -// for (int x = csx; x <= cex; x++, index++, globalIndex++) { -// indexes[index] = globalIndex; -// int height = heights[globalIndex] & 0xFF; -// heightMap[index] = height; -// maxY = Math.max(maxY, height); -// minY = Math.min(minY, height); -// } -// } -// boolean hasOverlay = this.overlay != null; -// if (hasOverlay) { -// maxY++; -// } -// int maxLayer = maxY >> 4; -// int fillLayers = Math.max(0, (minY - 1)) >> 4; -// for (int layer = 0; layer <= maxLayer; layer++) { -// if (chunk.ids[layer] == null) { -// chunk.ids[layer] = new byte[4096]; -// chunk.data[layer] = new byte[2048]; -// chunk.skyLight[layer] = new byte[2048]; -// chunk.blockLight[layer] = new byte[2048]; -// } -// } -// if (primtives.waterHeight != 0) { -// maxY = Math.max(maxY, primtives.waterHeight); -// int maxWaterLayer = ((primtives.waterHeight + 15) >> 4); -// for (int layer = 0; layer < maxWaterLayer; layer++) { -// boolean fillAll = (layer << 4) + 15 <= primtives.waterHeight; -// byte[] ids = chunk.ids[layer]; -// if (ids == null) { -// chunk.ids[layer] = ids = new byte[4096]; -// chunk.data[layer] = new byte[2048]; -// chunk.skyLight[layer] = new byte[2048]; -// chunk.blockLight[layer] = new byte[2048]; -// Arrays.fill(chunk.skyLight[layer], (byte) 255); -// } -// if (fillAll) { -// Arrays.fill(ids, primtives.waterId); -// } else { -// int maxIndex = (primtives.waterHeight & 15) << 8; -// Arrays.fill(ids, 0, maxIndex, primtives.waterId); -// } -// } -// } -// -// if (primtives.modifiedMain) { // If the main block is modified, we can't short circuit this -// for (int layer = 0; layer < fillLayers; layer++) { -// byte[] layerIds = chunk.ids[layer]; -// byte[] layerDatas = chunk.data[layer]; -// for (int z = csz; z <= cez; z++) { -// index = (z & 15) << 4; -// for (int x = csx; x <= cex; x++, index++) { -// globalIndex = indexes[index]; -// char mainCombined = main[globalIndex]; -// byte id = (byte) FaweCache.getId(mainCombined); -// int data = FaweCache.getData(mainCombined); -// if (data != 0) { -// for (int y = 0; y < 16; y++) { -// int mainIndex = index + (y << 8); -// chunk.setNibble(mainIndex, layerDatas, data); -// } -// } -// for (int y = 0; y < 16; y++) { -// layerIds[index + (y << 8)] = id; -// } -// } -// } -// } -// } else { -// for (int layer = 0; layer < fillLayers; layer++) { -// Arrays.fill(chunk.ids[layer], (byte) 1); -// } -// } -// -// for (int layer = fillLayers; layer <= maxLayer; layer++) { -// Arrays.fill(chunk.skyLight[layer], (byte) 255); -// byte[] layerIds = chunk.ids[layer]; -// byte[] layerDatas = chunk.data[layer]; -// int startY = layer << 4; -// int endY = startY + 15; -// for (int z = csz; z <= cez; z++) { -// index = (z & 15) << 4; -// for (int x = csx; x <= cex; x++, index++) { -// globalIndex = indexes[index]; -// int height = heightMap[index]; -// int diff; -// if (height > endY) { -// diff = 16; -// } else if (height >= startY) { -// diff = height - startY; -// char floorCombined = floor[globalIndex]; -// int id = FaweCache.getId(floorCombined); -// int floorIndex = index + ((height & 15) << 8); -// layerIds[floorIndex] = (byte) id; -// int data = FaweCache.getData(floorCombined); -// if (data != 0) { -// chunk.setNibble(floorIndex, layerDatas, data); -// } -// if (hasOverlay && height >= startY - 1 && height < endY) { -// char overlayCombined = overlay[globalIndex]; -// id = FaweCache.getId(overlayCombined); -// int overlayIndex = index + (((height + 1) & 15) << 8); -// layerIds[overlayIndex] = (byte) id; -// data = FaweCache.getData(overlayCombined); -// if (data != 0) { -// chunk.setNibble(overlayIndex, layerDatas, data); -// } -// } -// } else if (hasOverlay && height == startY - 1) { -// char overlayCombined = overlay[globalIndex]; -// int id = FaweCache.getId(overlayCombined); -// int overlayIndex = index + (((height + 1) & 15) << 8); -// layerIds[overlayIndex] = (byte) id; -// int data = FaweCache.getData(overlayCombined); -// if (data != 0) { -// chunk.setNibble(overlayIndex, layerDatas, data); -// } -// continue; -// } else { -// continue; -// } -// char mainCombined = main[globalIndex]; -// byte id = (byte) FaweCache.getId(mainCombined); -// int data = FaweCache.getData(mainCombined); -// if (data != 0) { -// for (int y = 0; y < diff; y++) { -// int mainIndex = index + (y << 8); -// chunk.setNibble(mainIndex, layerDatas, data); -// } -// } -// for (int y = 0; y < diff; y++) { -// layerIds[index + (y << 8)] = id; -// } -// } -// } -// } -// -// int maxYMod = 15 + (maxLayer << 4); -// for (int layer = (maxY >> 4) + 1; layer < 16; layer++) { -// chunk.ids[layer] = null; -// chunk.data[layer] = null; -// } -// -// if (primtives.bedrockId != 0) { // Bedrock -// byte[] layerIds = chunk.ids[0]; -// for (int z = csz; z <= cez; z++) { -// index = (z & 15) << 4; -// for (int x = csx; x <= cex; x++) { -// layerIds[index++] = primtives.bedrockId; -// } -// } -// } -// -// char[][][] localBlocks = getChunkArray(chunk.getX(), chunk.getZ()); -// if (localBlocks != null) { -// for (int layer = 0; layer < 16; layer++) { -// int by = layer << 4; -// int ty = by + 15; -// index = 0; -// for (int y = by; y <= ty; y++, index += 256) { -// char[][] yBlocks = localBlocks[y]; -// if (yBlocks != null) { -// if (chunk.ids[layer] == null) { -// chunk.ids[layer] = new byte[4096]; -// chunk.data[layer] = new byte[2048]; -// chunk.skyLight[layer] = new byte[2048]; -// chunk.blockLight[layer] = new byte[2048]; -// Arrays.fill(chunk.skyLight[layer], (byte) 255); -// } -// byte[] idsLayer = chunk.ids[layer]; -// byte[] dataLayer = chunk.data[layer]; -// for (int z = 0; z < yBlocks.length; z++) { -// char[] zBlocks = yBlocks[z]; -// if (zBlocks != null) { -// int zIndex = index + (z << 4); -// for (int x = 0; x < zBlocks.length; x++, zIndex++) { -// char combined = zBlocks[x]; -// if (combined == 0) continue; -// int id = FaweCache.getId(combined); -// int data = FaweCache.getData(combined); -// if (data == 0) { -// chunk.setIdUnsafe(idsLayer, zIndex, (byte) id); -// } else { -// chunk.setBlockUnsafe(idsLayer, dataLayer, zIndex, (byte) id, FaweCache.getData(combined)); -// } -// } -// } -// } -// } -// } -// } -// } -// -// if (primtives.floorThickness != 0 || primtives.worldThickness != 0) { -// // Use biomes array as temporary buffer -// byte[] minArr = chunk.biomes; -// for (int z = csz; z <= cez; z++) { -// index = (z & 15) << 4; -// for (int x = csx; x <= cex; x++, index++) { -// int gi = indexes[index]; -// int height = heightMap[index]; -// int min = height; -// if (x > 0) min = Math.min(heights[gi - 1] & 0xFF, min); -// if (x < getWidth() - 1) min = Math.min(heights[gi + 1] & 0xFF, min); -// if (z > 0) min = Math.min(heights[gi - getWidth()] & 0xFF, min); -// if (z < getLength() - 1) min = Math.min(heights[gi + getWidth()] & 0xFF, min); -// minArr[index] = (byte) min; -// } -// } -// -// int minLayer = Math.max(0, (minY - primtives.floorThickness) >> 4); -// -// if (primtives.floorThickness != 0) { -// for (int layer = minLayer; layer <= maxLayer; layer++) { -// byte[] layerIds = chunk.ids[layer]; -// byte[] layerDatas = chunk.data[layer]; -// int startY = layer << 4; -// int endY = startY + 15; -// for (int z = csz; z <= cez; z++) { -// index = (z & 15) << 4; -// for (int x = csx; x <= cex; x++, index++) { -// globalIndex = indexes[index]; -// int height = heightMap[index]; -// -// int min = (minArr[index] & 0xFF) - primtives.floorThickness; -// int localMin = min - startY; -// -// int max = height + 1; -// if (min < startY) min = startY; -// if (max > endY) max = endY + 1; -// -// -// if (min < max) { -// char floorCombined = floor[globalIndex]; -// final byte id = (byte) FaweCache.getId(floorCombined); -// final int data = FaweCache.getData(floorCombined); -// for (int y = min; y < max; y++) { -// int floorIndex = index + ((y & 15) << 8); -// layerIds[floorIndex] = id; -// if (data != 0) { -// chunk.setNibble(floorIndex, layerDatas, data); -// } -// } -// } -// -// } -// } -// } -// } -// if (primtives.worldThickness != 0) { -// for (int layer = 0; layer < minLayer; layer++) { -// chunk.ids[layer] = null; -// chunk.data[layer] = null; -// } -// for (int layer = minLayer; layer <= maxLayer; layer++) { -// byte[] layerIds = chunk.ids[layer]; -// byte[] layerDatas = chunk.data[layer]; -// int startY = layer << 4; -// int endY = startY + 15; -// for (int z = csz; z <= cez; z++) { -// index = (z & 15) << 4; -// for (int x = csx; x <= cex; x++, index++) { -// globalIndex = indexes[index]; -// int height = heightMap[index]; -// -// int min = (minArr[index] & 0xFF) - primtives.worldThickness; -// int localMin = min - startY; -// if (localMin > 0) { -// char floorCombined = floor[globalIndex]; -// final byte id = (byte) FaweCache.getId(floorCombined); -// final int data = FaweCache.getData(floorCombined); -// -// for (int y = 0; y < localMin; y++) { -// int floorIndex = index + ((y & 15) << 8); -// layerIds[floorIndex] = 0; -// if (data != 0) { -// chunk.setNibble(floorIndex, layerDatas, 0); -// } -// } -// } -// } -// } -// } -// } -// -// for (int layer = fillLayers; layer <= maxLayer; layer++) { -// Arrays.fill(chunk.skyLight[layer], (byte) 255); -// -// } -// } -// -// for (int i = 0; i < 256; i++) { -// chunk.biomes[i] = biomes[indexes[i]]; -// } -// -// -// } catch (Throwable e) { -// e.printStackTrace(); -// } + public WritableMCAChunk write(WritableMCAChunk 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; + try { + int[] indexes = indexStore.get(); + + int index; + int maxY = 0; + int minY = Integer.MAX_VALUE; + int[] heightMap = chunk.biomes; + int globalIndex; + for (int z = csz; z <= cez; z++) { + globalIndex = z * getWidth() + csx; + index = (z & 15) << 4; + for (int x = csx; x <= cex; x++, index++, globalIndex++) { + indexes[index] = globalIndex; + int height = heights[globalIndex] & 0xFF; + heightMap[index] = height; + maxY = Math.max(maxY, height); + minY = Math.min(minY, height); + } + } + boolean hasOverlay = this.overlay != null; + if (hasOverlay) { + maxY++; + } + int maxLayer = maxY >> 4; + for (int layer = 0; layer <= maxLayer; layer++) { + chunk.hasSections[layer] = true; + } + if (primtives.waterHeight != 0) { + maxY = Math.max(maxY, primtives.waterHeight); + int maxIndex = (primtives.waterHeight) << 8; + Arrays.fill(chunk.blocks, 0, maxIndex, primtives.waterId); + } + + if (primtives.modifiedMain) { // If the main block is modified, we can't short circuit this + for (int z = csz; z <= cez; z++) { + index = (z & 15) << 4; + for (int x = csx; x <= cex; x++, index++) { + globalIndex = indexes[index]; + int mainCombined = main[globalIndex]; + for (int y = 0; y < minY; y++) { + chunk.blocks[index + (y << 8)] = mainCombined; + } + } + } + } else { + int maxIndex = minY << 8; + Arrays.fill(chunk.blocks, 0, maxIndex, BlockID.STONE); + } + + final boolean hasFloorThickness = primtives.floorThickness != 0 || primtives.worldThickness != 0; + if (primtives.worldThickness != 0) { + int endLayer = ((minY - primtives.worldThickness + 1) >> 4); + for (int layer = 0; layer < endLayer; layer++) { + chunk.hasSections[layer] = false; + } + } + + for (int z = csz; z <= cez; z++) { + index = (z & 15) << 4; + for (int x = csx; x <= cex; x++, index++) { + globalIndex = indexes[index]; + int height = heightMap[index]; + int maxMainY = height; + int minMainY = minY; + + int mainCombined = main[globalIndex]; + + int 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); + if (z > 0) maxMainY = Math.min(heights[globalIndex - getWidth()] & 0xFF, maxMainY); + if (z < getLength() - 1) maxMainY = Math.min(heights[globalIndex + getWidth()] & 0xFF, maxMainY); + + int min = maxMainY; + + if (primtives.floorThickness != 0) { + maxMainY = Math.max(0, maxMainY - (primtives.floorThickness - 1)); + for (int y = maxMainY; y <= height; y++) { + chunk.blocks[index + (y << 8)] = floorCombined; + } + } + else { + chunk.blocks[index + ((height) << 8)] = floorCombined; + } + + if (primtives.worldThickness != 0) { + minMainY = Math.max(minY, min - primtives.worldThickness + 1); + for (int y = minY; y < minMainY; y++) { + chunk.blocks[index + (y << 8)] = BlockID.AIR; + } + } + + } else { + chunk.blocks[index + ((height) << 8)] = floorCombined; + } + + for (int y = minMainY; y < maxMainY; y++) { + chunk.blocks[index + (y << 8)] = mainCombined; + } + + if (hasOverlay) { + int overlayCombined = overlay[globalIndex]; + int overlayIndex = index + ((height + 1) << 8); + chunk.blocks[overlayIndex] = overlayCombined; + } + + if (primtives.bedrockId != 0) { + chunk.blocks[index] = primtives.bedrockId; + } + } + } + + int[][][] 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]; + if (yBlocks != null) { + chunk.hasSections[layer] = true; + for (int z = 0; z < yBlocks.length; z++) { + int[] zBlocks = yBlocks[z]; + if (zBlocks != null) { + int zIndex = index + (z << 4); + for (int x = 0; x < zBlocks.length; x++, zIndex++) { + int combined = zBlocks[x]; + if (combined == 0) continue; + chunk.blocks[zIndex] = combined; + } + } + } + } + } + } + } + + for (int i = 0; i < 256; i++) { + chunk.biomes[i] = biomes[indexes[i]]; + } + + + } catch (Throwable e) { + e.printStackTrace(); + } return chunk; } @@ -2092,7 +1924,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int x = 0; x < getWidth(); x++, index++) { int height = img.getRGB(x, z) & 0xFF; if (height == 255 || height > 0 && white && ThreadLocalRandom.current() - .nextInt(256) <= height) { + .nextInt(256) <= height) { overlay.get()[index] = combined; } } @@ -2111,7 +1943,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int x = 0; x < getWidth(); x++, index++) { int height = img.getRGB(x, z) & 0xFF; if (height == 255 || height > 0 && !white && ThreadLocalRandom.current() - .nextInt(256) <= height) { + .nextInt(256) <= height) { main.get()[index] = combined; } } @@ -2129,7 +1961,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int x = 0; x < getWidth(); x++, index++) { int height = img.getRGB(x, z) & 0xFF; if (height == 255 || height > 0 && !white && ThreadLocalRandom.current() - .nextInt(256) <= height) { + .nextInt(256) <= height) { floor.get()[index] = combined; } } @@ -2148,7 +1980,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int x = 0; x < getWidth(); x++, index++) { int height = img.getRGB(x, z) & 0xFF; if (height == 255 || height > 0 && !white && ThreadLocalRandom.current() - .nextInt(256) <= height) { + .nextInt(256) <= height) { main.get()[index] = combined; floor.get()[index] = combined; } @@ -2286,27 +2118,27 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr return false; } - @Override - public void dropItem(Vector3 position, BaseItemStack item) { - // TODO Auto-generated method stub - - } + @Override + public void dropItem(Vector3 position, BaseItemStack item) { + // TODO Auto-generated method stub - @Override - public boolean playEffect(Vector3 position, int type, int data) { - // TODO Auto-generated method stub - return false; - } + } - @Override - public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException { - // TODO Auto-generated method stub - return false; - } + @Override + public boolean playEffect(Vector3 position, int type, int data) { + // TODO Auto-generated method stub + return false; + } - @Override - public BlockVector3 getSpawnPosition() { - // TODO Auto-generated method stub - return null; - } + @Override + public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException { + // TODO Auto-generated method stub + return false; + } + + @Override + public BlockVector3 getSpawnPosition() { + // TODO Auto-generated method stub + return null; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWriter.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWriter.java index 921a8de2c..5d4421d12 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWriter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAWriter.java @@ -3,8 +3,10 @@ package com.boydti.fawe.jnbt.anvil; import com.boydti.fawe.object.collection.IterableThreadLocal; import com.boydti.fawe.object.io.BufferedRandomAccessFile; import com.boydti.fawe.util.MainUtil; + import java.io.File; import java.io.IOException; +import java.util.Arrays; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; import java.util.zip.Deflater; @@ -67,22 +69,20 @@ public abstract class MCAWriter { public abstract boolean shouldWrite(int chunkX, int chunkZ); - public abstract MCAChunk write(MCAChunk input, int startX, int endX, int startZ, int endZ); + public abstract WritableMCAChunk write(WritableMCAChunk input, int startX, int endX, int startZ, int endZ); public void generate() throws IOException { if (!folder.exists()) { folder.mkdirs(); } final ForkJoinPool pool = new ForkJoinPool(); - int bcx = 0; - int bcz = 0; int tcx = (width - 1) >> 4; int tcz = (length - 1) >> 4; - final ThreadLocal chunkStore = new ThreadLocal() { + final ThreadLocal chunkStore = new ThreadLocal() { @Override - protected MCAChunk initialValue() { - MCAChunk chunk = new MCAChunk(null, 0, 0); - chunk.biomes = new byte[256]; + protected WritableMCAChunk initialValue() { + WritableMCAChunk chunk = new WritableMCAChunk(); + Arrays.fill(chunk.skyLight, (byte) 255); return chunk; } }; @@ -111,16 +111,15 @@ public abstract class MCAWriter { int mcaXMax = mcaXMin + ((width - 1) >> 9); int mcaZMax = mcaZMin + ((length - 1) >> 9); + final byte[] header = new byte[4096]; + for (int mcaZ = mcaXMin; mcaZ <= mcaZMax; mcaZ++) { for (int mcaX = mcaXMin; mcaX <= mcaXMax; mcaX++) { - final int fmcaX = mcaX; - final int fmcaZ = mcaZ; 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[] header = new byte[4096]; final byte[][] compressed = new byte[1024][]; int bx = mcaX << 9; int bz = mcaZ << 9; @@ -137,76 +136,70 @@ public abstract class MCAWriter { final int fcx = cx; final int fcz = cz; if (shouldWrite(cx, cz)) { - pool.submit(new Runnable() { - @Override - public void run() { - try { - MCAChunk chunk = chunkStore.get(); - chunk.setLoc(null, fcx, fcz); - chunk = write(chunk, csx, cex, csz, cez); - if (chunk != null) { - // Generation offset - chunk.setLoc(null, fcx + (getOffsetX() >> 4), fcz + (getOffsetZ() >> 4)); - // Compress - byte[] bytes = chunk.toBytes(byteStore1.get()); - byte[] compressedBytes = MainUtil.compress(bytes, byteStore2.get(), deflateStore.get()); - int blocks = (compressed.length + 4095) >> 12; - compressed[((fcx & 31)) + ((fcz & 31) << 5)] = compressedBytes.clone(); - } - } catch (Throwable e) { - e.printStackTrace(); + pool.submit(() -> { + try { + WritableMCAChunk chunk = chunkStore.get(); + chunk.clear(fcx, fcz); + chunk = write(chunk, csx, cex, csz, cez); + if (chunk != null) { + // Generation offset + chunk.setLoc( fcx + (getOffsetX() >> 4), fcz + (getOffsetZ() >> 4)); + + // 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(); } + } catch (Throwable e) { + e.printStackTrace(); } }); } } } pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); - pool.submit(new Runnable() { - @Override - public void run() { + pool.submit(() -> { + try { + int totalLength = 8192; + for (int i = 0; i < compressed.length; i++) { + byte[] compressedBytes = compressed[i]; + 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 { - int totalLength = 8192; - for (int i = 0; i < compressed.length; i++) { - byte[] compressedBytes = compressed[i]; - 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 - int cx = (fmcaX << 5) + (i & 31); - int cz = (fmcaZ << 5) + (i >> 5); - raf.seek(offset); - raf.writeInt(compressedBytes.length + 1); - raf.write(2); - raf.write(compressedBytes); - offset += blocks * 4096; - } - } - raf.seek(0); - raf.write(header); + raf.close(); } catch (IOException e) { e.printStackTrace(); - } finally { - try { - raf.close(); - } catch (IOException e) { - e.printStackTrace(); - } } } }); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/WritableMCAChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/WritableMCAChunk.java new file mode 100644 index 000000000..8283145c1 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/WritableMCAChunk.java @@ -0,0 +1,398 @@ +package com.boydti.fawe.jnbt.anvil; + +import com.boydti.fawe.object.FaweChunk; +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.NBTOutputStream; +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; + +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +public class WritableMCAChunk extends FaweChunk { + 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 int[] blocks = new int[65536]; + + public Map tiles = new HashMap<>(); + public Map entities = new HashMap<>(); + public long inhabitedTime = System.currentTimeMillis(); + public long lastUpdate; + + public int modified; + public boolean deleted; + + public int chunkX, chunkZ; + + protected WritableMCAChunk() { + super(null, 0, 0); + } + + public int getX() { + return chunkX; + } + + public int getZ() { + return chunkZ; + } + + public void setLoc(int X, int Z) { + this.chunkX = X; + this.chunkZ = Z; + } + + public void clear(int X, int Z) { + this.chunkX = X; + this.chunkZ = Z; + if (!tiles.isEmpty()) { + tiles.clear(); + } + if (!entities.isEmpty()) { + entities.clear(); + } + modified = 0; + deleted = false; + hasBiomes = false; + Arrays.fill(hasSections, false); + } + + private transient final int[] blockToPalette = new int[BlockTypes.states.length]; + private transient final boolean[] hasBlock = new boolean[BlockTypes.states.length]; + private transient final int[] paletteToBlock = new int[Character.MAX_VALUE]; + private transient final long[] blockstates = new long[2048]; + + public void write(NBTOutputStream nbtOut) throws IOException { + nbtOut.writeNamedTagName("", NBTConstants.TYPE_COMPOUND); + nbtOut.writeLazyCompoundTag("Level", out -> { + out.writeNamedTag("V", (byte) 1); + out.writeNamedTag("xPos", getX()); + out.writeNamedTag("zPos", getZ()); + out.writeNamedTag("LightPopulated", (byte) 0); + out.writeNamedTag("TerrainPopulated", (byte) 1); + if (entities.isEmpty()) { + out.writeNamedEmptyList("Entities"); + } else { + out.writeNamedTag("Entities", new ListTag(CompoundTag.class, new ArrayList<>(entities.values()))); + } + if (tiles.isEmpty()) { + out.writeNamedEmptyList("TileEntities"); + } else { + out.writeNamedTag("TileEntities", new ListTag(CompoundTag.class, + new ArrayList<>(tiles.values()))); + } + out.writeNamedTag("InhabitedTime", inhabitedTime); + out.writeNamedTag("LastUpdate", lastUpdate); + if (biomes != null) { + out.writeNamedTag("Biomes", biomes); + } + out.writeNamedTagName("Sections", NBTConstants.TYPE_LIST); + nbtOut.writeByte(NBTConstants.TYPE_COMPOUND); + int len = 0; + for (int layer = 0; layer < hasSections.length; layer++) { + if (hasSections[layer]) len++; + } + + nbtOut.writeInt(len); + + for (int layer = 0; layer < hasSections.length; layer++) { + if (hasSections[layer]) { + continue; + } + out.writeNamedTag("Y", (byte) layer); + + 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); + + int blockIndexStart = layer << 8; + int blockIndexEnd = blockIndexStart << 1; + int num_palette = 0; + try { + out.writeNamedTagName("Palette", NBTConstants.TYPE_LIST); + out.writeByte(NBTConstants.TYPE_COMPOUND); + out.writeInt(num_palette); + + for (int i = blockIndexStart; i < blockIndexEnd; i++) { + int stateId = blocks[i]; + if (!hasBlock[stateId]) { + hasBlock[stateId] = true; + blockToPalette[stateId] = num_palette; + paletteToBlock[num_palette++] = stateId; + + BlockState state = BlockTypes.states[stateId]; + BlockType type = state.getBlockType(); + out.writeNamedTag("Name", type.getId()); + + // Properties + if (type.getDefaultState() == state) continue; + + out.writeNamedTagName("Properties", NBTConstants.TYPE_COMPOUND); + for (Property property : type.getProperties()) { + String key = property.getName(); + Object value = state.getState(property); + String valueStr = value.toString(); + if (Character.isUpperCase(valueStr.charAt(0))) { + System.out.println("Invalid uppercase value " + value); + valueStr = valueStr.toLowerCase(); + } + out.writeNamedTag(key, valueStr); + } + out.writeByte(NBTConstants.TYPE_END); + } + } + + // BlockStates + int bitsPerEntry = MathMan.log2nlz(num_palette - 1); + int blockBitArrayEnd = (bitsPerEntry * 4096) >> 6; + if (num_palette == 1) { + Arrays.fill(blockstates, 0, blockBitArrayEnd, 0); + } + BitArray4096 bitArray = new BitArray4096(blockstates, bitsPerEntry); + bitArray.fromRaw(blocks, blockIndexStart); + + out.writeNamedTagName("BlockStates", NBTConstants.TYPE_LONG_ARRAY); + out.writeInt(blockBitArrayEnd); + for (int i = 0; i < blockBitArrayEnd; i++) out.writeLong(blockstates[i]); + + out.writeEndTag(); + + // cleanup + } catch (Throwable e) { + for (int i = 0; i < num_palette; i++) { + hasBlock[i] = false; + } + throw e; + } + } + }); + nbtOut.writeEndTag(); + } + + public byte[] toBytes(byte[] buffer) throws IOException { + if (buffer == null) { + buffer = new byte[8192]; + } + FastByteArrayOutputStream buffered = new FastByteArrayOutputStream(buffer); + DataOutputStream dataOut = new DataOutputStream(buffered); + try (NBTOutputStream nbtOut = new NBTOutputStream((DataOutput) dataOut)) { + write(nbtOut); + } + return buffered.toByteArray(); + } + + public long getInhabitedTime() { + return inhabitedTime; + } + + public long getLastUpdate() { + return lastUpdate; + } + + public void setInhabitedTime(long inhabitedTime) { + this.inhabitedTime = inhabitedTime; + } + + public void setLastUpdate(long lastUpdate) { + this.lastUpdate = lastUpdate; + } + + public void setDeleted(boolean deleted) { + setModified(); + this.deleted = deleted; + } + + public boolean isDeleted() { + return deleted; + } + + public boolean isModified() { + return modified != 0; + } + + public int getModified() { + return modified; + } + + public final void setModified() { + this.modified++; + } + + public int getBitMask() { + int bitMask = 0; + for (int section = 0; section < hasSections.length; section++) { + if (hasSections[section]) { + bitMask += 1 << section; + } + } + return bitMask; + } + + public void setTile(int x, int y, int z, CompoundTag tile) { + setModified(); + short pair = MathMan.tripleBlockCoord(x, y, z); + if (tile != null) { + tiles.put(pair, tile); + } else { + tiles.remove(pair); + } + } + + public void setEntity(CompoundTag entityTag) { + setModified(); + long least = entityTag.getLong("UUIDLeast"); + long most = entityTag.getLong("UUIDMost"); + entities.put(new UUID(most, least), entityTag); + } + + public void setBiome(int x, int z, BiomeType biome) { + setModified(); + biomes[x + (z << 4)] = biome.getInternalId(); + } + + public Set getEntities() { + return new HashSet<>(entities.values()); + } + + public Map getTiles() { + return tiles == null ? new HashMap<>() : tiles; + } + + public CompoundTag getTile(int x, int y, int z) { + if (tiles == null || tiles.isEmpty()) { + return null; + } + short pair = MathMan.tripleBlockCoord(x, y, z); + 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) { + return blocks[x | (z << 4) | (y << 8)]; + } + + public BiomeType[] getBiomeArray() { + return null; + } + + public Set getEntityRemoves() { + return new HashSet<>(); + } + + public void setSkyLight(int x, int y, int z, int value) { + setNibble(getIndex(x, y, z), skyLight, value); + } + + 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); + } + } + } + + public void removeLight() { + for (int i = 0; i < skyLight.length; i++) { + removeLight(i); + } + } + + 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 setBiome(BiomeType biome) { + Arrays.fill(biomes, (byte) biome.getInternalId()); + } + + @Override + public FaweChunk copy(boolean shallow) { + throw new UnsupportedOperationException("Unsupported"); + } + + public void removeEntity(UUID uuid) { + entities.remove(uuid); + } + + public Void getChunk() { + throw new UnsupportedOperationException("Not applicable for this"); + } + + public FaweChunk call() { + throw new UnsupportedOperationException("Not supported"); + } +} 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 d1e860e6d..7ad1a8142 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java @@ -39,7 +39,7 @@ import java.util.Map; * found at * http://www.minecraft.net/docs/NBT.txt.

*/ -public final class NBTOutputStream implements Closeable { +public final class NBTOutputStream extends OutputStream implements Closeable, DataOutput { /** * The output stream. @@ -426,11 +426,82 @@ public final class NBTOutputStream implements Closeable { if (os instanceof Closeable) ((Closeable) os).close(); } + @Override + public void write(int b) throws IOException { + os.write(b); + } + + @Override + public void write(byte[] b) throws IOException { + os.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + os.write(b, off, len); + } + + @Override + public void writeBoolean(boolean v) throws IOException { + os.writeBoolean(v); + } + + @Override + public void writeByte(int v) throws IOException { + os.writeByte(v); + } + + @Override + public void writeShort(int v) throws IOException { + os.writeShort(v); + } + + @Override + public void writeChar(int v) throws IOException { + os.writeChar(v); + } + + @Override + public void writeInt(int v) throws IOException { + os.writeInt(v); + } + + @Override + public void writeLong(long v) throws IOException { + os.writeLong(v); + } + + @Override + public void writeFloat(float v) throws IOException { + os.writeFloat(v); + } + + @Override + public void writeDouble(double v) throws IOException { + os.writeDouble(v); + } + + @Override + public void writeBytes(String s) throws IOException { + os.writeBytes(s); + } + + @Override + public void writeChars(String s) throws IOException { + os.writeChars(s); + } + + @Override + public void writeUTF(String s) throws IOException { + os.writeUTF(s); + } + /** * Flush output. * * @throws IOException */ + @Override public void flush() throws IOException { if (os instanceof Flushable) ((Flushable) os).flush(); } 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 ee865680e..b571555cf 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -974,7 +974,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, BBC.WORLDEDIT_SOME_FAILS_BLOCKBAG.send(player, str.toString()); } } - return new HashMap<>(); + return Collections.emptyMap(); } /**