diff --git a/src/main/java/net/minecraft/server/ChunkSection.java b/src/main/java/net/minecraft/server/ChunkSection.java index a05efa0166..787077dcd1 100644 --- a/src/main/java/net/minecraft/server/ChunkSection.java +++ b/src/main/java/net/minecraft/server/ChunkSection.java @@ -1,5 +1,7 @@ package net.minecraft.server; +import java.util.Arrays; // CraftBukkit + public class ChunkSection { private int yPos; @@ -10,34 +12,83 @@ public class ChunkSection { private NibbleArray blockData; private NibbleArray emittedLight; private NibbleArray skyLight; + // CraftBukkit start - Compact storage + private byte compactId; + private byte compactExtId; + private byte compactData; + private byte compactEmitted; + private byte compactSky; + + // Pre-generated (read-only!) NibbleArrays for every possible value, used for chunk saving + private static NibbleArray[] compactPregen = new NibbleArray[16]; + static { + for (int i = 0; i < 16; i++) { + compactPregen[i] = expandCompactNibble((byte) i); + } + } + + private static NibbleArray expandCompactNibble(byte value) { + byte[] data = new byte[2048]; + Arrays.fill(data, (byte) (value | (value << 4))); + return new NibbleArray(data, 4); + } + + private boolean canBeCompact(byte[] array) { + byte value = array[0]; + for (int i = 1; i < array.length; i++) { + if (value != array[i]) { + return false; + } + } + + return true; + } + // CraftBukkit end public ChunkSection(int i, boolean flag) { this.yPos = i; + /* CraftBukkit - Start as null, using compact storage this.blockIds = new byte[4096]; this.blockData = new NibbleArray(this.blockIds.length, 4); this.emittedLight = new NibbleArray(this.blockIds.length, 4); if (flag) { this.skyLight = new NibbleArray(this.blockIds.length, 4); } + */ + if (!flag) { + this.compactSky = -1; + } + // CraftBukkit end } // CraftBukkit start public ChunkSection(int y, boolean flag, byte[] blkIds, byte[] extBlkIds) { this.yPos = y; - this.blockIds = blkIds; + this.setIdArray(blkIds); if (extBlkIds != null) { - this.extBlockIds = new NibbleArray(extBlkIds, 4); + this.setExtendedIdArray(new NibbleArray(extBlkIds, 4)); } - this.blockData = new NibbleArray(this.blockIds.length, 4); - this.emittedLight = new NibbleArray(this.blockIds.length, 4); - if (flag) { - this.skyLight = new NibbleArray(this.blockIds.length, 4); + if (!flag) { + this.compactSky = -1; } this.recalcBlockCounts(); } // CraftBukkit end public Block getTypeId(int i, int j, int k) { + // CraftBukkit start - Compact storage + if (this.blockIds == null) { + int id = this.compactId; + if (this.extBlockIds == null) { + id |= this.compactExtId << 8; + } else { + id |= this.extBlockIds.a(i, j, k) << 8; + } + + return Block.e(id); // Should be getBlockForId + } + // CraftBukkit end + int l = this.blockIds[j << 8 | k << 4 | i] & 255; if (this.extBlockIds != null) { @@ -48,13 +99,12 @@ public class ChunkSection { } public void setTypeId(int i, int j, int k, Block block) { - int l = this.blockIds[j << 8 | k << 4 | i] & 255; - - if (this.extBlockIds != null) { - l |= this.extBlockIds.a(i, j, k) << 8; + // CraftBukkit start - Compact storage + Block block1 = this.getTypeId(i, j, k); + if (block == block1) { + return; } - - Block block1 = Block.e(l); + // CraftBukkit end if (block1 != Blocks.AIR) { --this.nonEmptyBlockCount; @@ -72,10 +122,17 @@ public class ChunkSection { int i1 = Block.b(block); + // CraftBukkit start - Compact storage + if (this.blockIds == null) { + this.blockIds = new byte[4096]; + Arrays.fill(this.blockIds, this.compactId); + } + // CraftBukkit end + this.blockIds[j << 8 | k << 4 | i] = (byte) (i1 & 255); if (i1 > 255) { if (this.extBlockIds == null) { - this.extBlockIds = new NibbleArray(this.blockIds.length, 4); + this.extBlockIds = expandCompactNibble(this.compactExtId); // CraftBukkit - Compact storage } this.extBlockIds.a(i, j, k, (i1 & 3840) >> 8); @@ -85,10 +142,23 @@ public class ChunkSection { } public int getData(int i, int j, int k) { + // CraftBukkit start - Compact storage + if (this.blockData == null) { + return this.compactData; + } + // CraftBukkit end return this.blockData.a(i, j, k); } public void setData(int i, int j, int k, int l) { + // CraftBukkit start - Compact storage + if (this.blockData == null) { + if (this.compactData == l) { + return; + } + this.blockData = expandCompactNibble(this.compactData); + } + // CraftBukkit end this.blockData.a(i, j, k, l); } @@ -105,71 +175,151 @@ public class ChunkSection { } public void setSkyLight(int i, int j, int k, int l) { + // CraftBukkit start - Compact storage + if (this.skyLight == null) { + if (this.compactSky == l) { + return; + } + this.skyLight = expandCompactNibble(this.compactSky); + } + // CraftBukkit end this.skyLight.a(i, j, k, l); } public int getSkyLight(int i, int j, int k) { + // CraftBukkit start - Compact storage + if (this.skyLight == null) { + return this.compactSky; + } + // CraftBukkit end return this.skyLight.a(i, j, k); } public void setEmittedLight(int i, int j, int k, int l) { + // CraftBukkit start - Compact storage + if (this.emittedLight == null) { + if (this.compactEmitted == l) { + return; + } + this.emittedLight = expandCompactNibble(this.compactEmitted); + } + // CraftBukkit end this.emittedLight.a(i, j, k, l); } public int getEmittedLight(int i, int j, int k) { + // CraftBukkit start - Compact storage + if (this.emittedLight == null) { + return this.compactEmitted; + } + // CraftBukkit end return this.emittedLight.a(i, j, k); } public void recalcBlockCounts() { // CraftBukkit start - Optimize for speed - byte[] blkIds = this.blockIds; int cntNonEmpty = 0; int cntTicking = 0; - if (this.extBlockIds == null) { // No extended block IDs? Don't waste time messing with them - for (int off = 0; off < blkIds.length; off++) { - int l = blkIds[off] & 0xFF; - if (l > 0) { - if (Block.e(l) == null) { - blkIds[off] = 0; + + if (this.blockIds == null) { + int id = this.compactId; + if (this.extBlockIds == null) { + id |= this.compactExtId << 8; + if (id > 0) { + Block block = Block.e(id); // Should be getBlockForId + if (block == null) { + this.compactId = 0; + this.compactExtId = 0; } else { - ++cntNonEmpty; - if (Block.e(l).isTicking()) { - ++cntTicking; + cntNonEmpty = 4096; + if (block.isTicking()) { + cntTicking = 4096; } } } + } else { + byte[] ext = this.extBlockIds.a; + for (int off = 0, off2 = 0; off < 4096;) { + byte extid = ext[off2]; + int l = (id & 0xFF) | ((extid & 0xF) << 8); // Even data + if (l > 0) { + Block block = Block.e(l); // Should be getBlockForId + if (block == null) { + this.compactId = 0; + ext[off2] &= 0xF0; + } else { + ++cntNonEmpty; + if (block.isTicking()) { + ++cntTicking; + } + } + } + off++; + l = (id & 0xFF) | ((extid & 0xF0) << 4); // Odd data + if (l > 0) { + Block block = Block.e(l); // Should be getBlockForId + if (block == null) { + this.compactId = 0; + ext[off2] &= 0x0F; + } else { + ++cntNonEmpty; + if (block.isTicking()) { + ++cntTicking; + } + } + } + off++; + off2++; + } } } else { - byte[] ext = this.extBlockIds.a; - for (int off = 0, off2 = 0; off < blkIds.length;) { - byte extid = ext[off2]; - int l = (blkIds[off] & 0xFF) | ((extid & 0xF) << 8); // Even data - if (l > 0) { - if (Block.e(l) == null) { - blkIds[off] = 0; - ext[off2] &= 0xF0; - } else { - ++cntNonEmpty; - if (Block.e(l).isTicking()) { - ++cntTicking; + byte[] blkIds = this.blockIds; + if (this.extBlockIds == null) { // No extended block IDs? Don't waste time messing with them + for (int off = 0; off < blkIds.length; off++) { + int l = blkIds[off] & 0xFF; + if (l > 0) { + if (Block.e(l) == null) { + blkIds[off] = 0; + } else { + ++cntNonEmpty; + if (Block.e(l).isTicking()) { + ++cntTicking; + } } } } - off++; - l = (blkIds[off] & 0xFF) | ((extid & 0xF0) << 4); // Odd data - if (l > 0) { - if (Block.e(l) == null) { - blkIds[off] = 0; - ext[off2] &= 0x0F; - } else { - ++cntNonEmpty; - if (Block.e(l).isTicking()) { - ++cntTicking; + } else { + byte[] ext = this.extBlockIds.a; + for (int off = 0, off2 = 0; off < blkIds.length;) { + byte extid = ext[off2]; + int l = (blkIds[off] & 0xFF) | ((extid & 0xF) << 8); // Even data + if (l > 0) { + if (Block.e(l) == null) { + blkIds[off] = 0; + ext[off2] &= 0xF0; + } else { + ++cntNonEmpty; + if (Block.e(l).isTicking()) { + ++cntTicking; + } } } + off++; + l = (blkIds[off] & 0xFF) | ((extid & 0xF0) << 4); // Odd data + if (l > 0) { + if (Block.e(l) == null) { + blkIds[off] = 0; + ext[off2] &= 0x0F; + } else { + ++cntNonEmpty; + if (Block.e(l).isTicking()) { + ++cntTicking; + } + } + } + off++; + off2++; } - off++; - off2++; } } this.nonEmptyBlockCount = cntNonEmpty; @@ -198,56 +348,119 @@ public class ChunkSection { } public byte[] getIdArray() { + // CraftBukkit start - Compact storage + if (this.blockIds == null) { + byte[] ids = new byte[4096]; + Arrays.fill(ids, this.compactId); + return ids; + } + // CraftBukkit end return this.blockIds; } public NibbleArray getExtendedIdArray() { + // CraftBukkit start - Compact storage + if (this.extBlockIds == null && this.compactExtId != 0) { + return compactPregen[this.compactExtId]; + } + // CraftBukkit end return this.extBlockIds; } public NibbleArray getDataArray() { + // CraftBukkit start - Compact storage + if (this.blockData == null) { + return compactPregen[this.compactData]; + } + // CraftBukkit end return this.blockData; } public NibbleArray getEmittedLightArray() { + // CraftBukkit start - Compact storage + if (this.emittedLight == null) { + return compactPregen[this.compactEmitted]; + } + // CraftBukkit end return this.emittedLight; } public NibbleArray getSkyLightArray() { + // CraftBukkit start - Compact storage + if (this.skyLight == null && this.compactSky != -1) { + return compactPregen[this.compactSky]; + } + // CraftBukkit end return this.skyLight; } public void setIdArray(byte[] abyte) { + // CraftBukkit start - Compact storage + if (abyte == null) { + this.compactId = 0; + this.blockIds = null; + return; + } else if (canBeCompact(abyte)) { + this.compactId = abyte[0]; + return; + } + // CraftBukkit end this.blockIds = this.validateByteArray(abyte); // CraftBukkit - Validate data } public void setExtendedIdArray(NibbleArray nibblearray) { - // CraftBukkit start - Don't hang on to an empty nibble array - boolean empty = true; - for (int i = 0; i < nibblearray.a.length; i++) { - if (nibblearray.a[i] != 0) { - empty = false; - break; - } - } - - if (empty) { + // CraftBukkit start - Compact storage + if (nibblearray == null) { + this.compactExtId = 0; + this.extBlockIds = null; + return; + } else if (canBeCompact(nibblearray.a)) { + this.compactExtId = (byte) (nibblearray.a(0, 0, 0) & 0xF); return; } // CraftBukkit end - this.extBlockIds = this.validateNibbleArray(nibblearray); // CraftBukkit - Validate data } public void setDataArray(NibbleArray nibblearray) { + // CraftBukkit start - Compact storage + if (nibblearray == null) { + this.compactData = 0; + this.blockData = null; + return; + } else if (canBeCompact(nibblearray.a)) { + this.compactData = (byte) (nibblearray.a(0, 0, 0) & 0xF); + return; + } + // CraftBukkit end this.blockData = this.validateNibbleArray(nibblearray); // CraftBukkit - Validate data } public void setEmittedLightArray(NibbleArray nibblearray) { + // CraftBukkit start - Compact storage + if (nibblearray == null) { + this.compactEmitted = 0; + this.emittedLight = null; + return; + } else if (canBeCompact(nibblearray.a)) { + this.compactEmitted = (byte) (nibblearray.a(0, 0, 0) & 0xF); + return; + } + // CraftBukkit end this.emittedLight = this.validateNibbleArray(nibblearray); // CraftBukkit - Validate data } public void setSkyLightArray(NibbleArray nibblearray) { + // CraftBukkit start - Compact storage + if (nibblearray == null) { + this.compactSky = -1; + this.skyLight = null; + return; + } else if (canBeCompact(nibblearray.a)) { + this.compactSky = (byte) (nibblearray.a(0, 0, 0) & 0xF); + return; + } + // CraftBukkit end this.skyLight = this.validateNibbleArray(nibblearray); // CraftBukkit - Validate data } @@ -255,7 +468,7 @@ public class ChunkSection { private NibbleArray validateNibbleArray(NibbleArray nibbleArray) { if (nibbleArray != null && nibbleArray.a.length < 2048) { byte[] newArray = new byte[2048]; - System.arraycopy(nibbleArray.a, 0, newArray, 0, ((nibbleArray.a.length > 2048) ? 2048 : nibbleArray.a.length)); + System.arraycopy(nibbleArray.a, 0, newArray, 0, nibbleArray.a.length); nibbleArray = new NibbleArray(newArray, 4); }