forked from GeyserMC/Geyser
Rename palette to BitArray and fix various chunk issues.
No idea if this works since I have no Java Edition server to test on.
This commit is contained in:
parent
383429d71b
commit
72589fabcd
6 changed files with 96 additions and 84 deletions
|
@ -1,6 +1,7 @@
|
||||||
package org.geysermc.connector.world;
|
package org.geysermc.connector.world;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
|
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||||
|
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
@ -10,8 +11,8 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
*/
|
*/
|
||||||
public class GlobalBlockPalette {
|
public class GlobalBlockPalette {
|
||||||
|
|
||||||
private static final Int2IntArrayMap legacyToRuntimeId = new Int2IntArrayMap();
|
private static final Int2IntMap legacyToRuntimeId = new Int2IntOpenHashMap();
|
||||||
private static final Int2IntArrayMap runtimeIdToLegacy = new Int2IntArrayMap();
|
private static final Int2IntMap runtimeIdToLegacy = new Int2IntOpenHashMap();
|
||||||
private static final AtomicInteger runtimeIdAllocator = new AtomicInteger(0);
|
private static final AtomicInteger runtimeIdAllocator = new AtomicInteger(0);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -39,4 +40,8 @@ public class GlobalBlockPalette {
|
||||||
legacyToRuntimeId.put(legacyId, runtimeId);
|
legacyToRuntimeId.put(legacyId, runtimeId);
|
||||||
return runtimeId;
|
return runtimeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getLegacyId(int runtimeId) {
|
||||||
|
return runtimeIdToLegacy.get(runtimeId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ import com.nukkitx.network.VarInts;
|
||||||
import gnu.trove.list.array.TIntArrayList;
|
import gnu.trove.list.array.TIntArrayList;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import org.geysermc.connector.world.GlobalBlockPalette;
|
import org.geysermc.connector.world.GlobalBlockPalette;
|
||||||
import org.geysermc.connector.world.chunk.palette.Palette;
|
import org.geysermc.connector.world.chunk.bitarray.BitArray;
|
||||||
import org.geysermc.connector.world.chunk.palette.PaletteVersion;
|
import org.geysermc.connector.world.chunk.bitarray.BitArrayVersion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapted from NukkitX: https://github.com/NukkitX/Nukkit
|
* Adapted from NukkitX: https://github.com/NukkitX/Nukkit
|
||||||
|
@ -14,76 +14,76 @@ public class BlockStorage {
|
||||||
|
|
||||||
private static final int SIZE = 4096;
|
private static final int SIZE = 4096;
|
||||||
|
|
||||||
private final TIntArrayList ids;
|
private final TIntArrayList palette;
|
||||||
private Palette palette;
|
private BitArray bitArray;
|
||||||
|
|
||||||
public BlockStorage() {
|
public BlockStorage() {
|
||||||
this(PaletteVersion.V2);
|
this(BitArrayVersion.V2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockStorage(PaletteVersion version) {
|
public BlockStorage(BitArrayVersion version) {
|
||||||
this.palette = version.createPalette(SIZE);
|
this.bitArray = version.createPalette(SIZE);
|
||||||
this.ids = new TIntArrayList(16, -1);
|
this.palette = new TIntArrayList(16, -1);
|
||||||
this.ids.add(0); // Air is at the start of every palette.
|
this.palette.add(0); // Air is at the start of every palette.
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlockStorage(Palette palette, TIntArrayList ids) {
|
private BlockStorage(BitArray bitArray, TIntArrayList palette) {
|
||||||
this.ids = ids;
|
|
||||||
this.palette = palette;
|
this.palette = palette;
|
||||||
|
this.bitArray = bitArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized int getFullBlock(int xzy) {
|
private static int getPaletteHeader(BitArrayVersion version, boolean runtime) {
|
||||||
return this.palette.get(xzy);
|
return (version.getId() << 1) | (runtime ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BitArrayVersion getVersionFromHeader(byte header) {
|
||||||
|
return BitArrayVersion.get(header >> 1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized int getFullBlock(int index) {
|
||||||
|
return this.legacyIdFor(this.bitArray.get(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setFullBlock(int index, int legacyId) {
|
public synchronized void setFullBlock(int index, int legacyId) {
|
||||||
this.palette.set(index, this.idFor(legacyId));
|
int idx = this.idFor(legacyId);
|
||||||
|
this.bitArray.set(index, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void writeToNetwork(ByteBuf buffer) {
|
public synchronized void writeToNetwork(ByteBuf buffer) {
|
||||||
buffer.writeByte(getPaletteHeader(palette.getVersion(), true));
|
buffer.writeByte(getPaletteHeader(bitArray.getVersion(), true));
|
||||||
|
|
||||||
for (int word : palette.getWords()) {
|
for (int word : bitArray.getWords()) {
|
||||||
buffer.writeIntLE(word);
|
buffer.writeIntLE(word);
|
||||||
}
|
}
|
||||||
|
|
||||||
VarInts.writeUnsignedInt(buffer, ids.size());
|
VarInts.writeUnsignedInt(buffer, palette.size());
|
||||||
ids.forEach(id -> {
|
palette.forEach(id -> {
|
||||||
VarInts.writeUnsignedInt(buffer, id);
|
VarInts.writeUnsignedInt(buffer, id);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void writeToStorage(ByteBuf buffer) {
|
private void onResize(BitArrayVersion version) {
|
||||||
buffer.writeByte(getPaletteHeader(palette.getVersion(), false));
|
BitArray newBitArray = version.createPalette(SIZE);
|
||||||
for (int word : palette.getWords()) {
|
|
||||||
buffer.writeIntLE(word);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Write persistent NBT tags
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void onResize(PaletteVersion version) {
|
|
||||||
Palette oldPalette = this.palette;
|
|
||||||
this.palette = version.createPalette(SIZE);
|
|
||||||
|
|
||||||
for (int i = 0; i < SIZE; i++) {
|
for (int i = 0; i < SIZE; i++) {
|
||||||
this.palette.set(i, oldPalette.get(i));
|
newBitArray.set(i, this.bitArray.get(i));
|
||||||
}
|
}
|
||||||
|
this.bitArray = newBitArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int idFor(int legacyId) {
|
private int idFor(int legacyId) {
|
||||||
int runtimeId = GlobalBlockPalette.getOrCreateRuntimeId(legacyId);
|
int runtimeId = GlobalBlockPalette.getOrCreateRuntimeId(legacyId);
|
||||||
int index = this.ids.indexOf(runtimeId);
|
int index = this.palette.indexOf(runtimeId);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
index = this.ids.size();
|
index = this.palette.size();
|
||||||
this.ids.add(runtimeId);
|
this.palette.add(runtimeId);
|
||||||
PaletteVersion version = this.palette.getVersion();
|
BitArrayVersion version = this.bitArray.getVersion();
|
||||||
if (index > version.getMaxEntryValue()) {
|
if (index > version.getMaxEntryValue()) {
|
||||||
PaletteVersion next = version.next();
|
BitArrayVersion next = version.next();
|
||||||
if (next != null) {
|
if (next != null) {
|
||||||
this.onResize(next);
|
this.onResize(next);
|
||||||
}
|
}
|
||||||
|
@ -91,13 +91,17 @@ public class BlockStorage {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getPaletteHeader(PaletteVersion version, boolean runtime) {
|
private int legacyIdFor(int index) {
|
||||||
return (version.getVersion() << 1) | (runtime ? 1 : 0);
|
int runtimeId = this.palette.get(index);
|
||||||
|
return GlobalBlockPalette.getLegacyId(runtimeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
for (int word : this.palette.getWords()) {
|
if (this.palette.size() == 1) {
|
||||||
if (word != 0) {
|
return true;
|
||||||
|
}
|
||||||
|
for (int word : this.bitArray.getWords()) {
|
||||||
|
if (Integer.toUnsignedLong(word) != 0L) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,6 +109,6 @@ public class BlockStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockStorage copy() {
|
public BlockStorage copy() {
|
||||||
return new BlockStorage(this.palette.copy(), new TIntArrayList(this.ids));
|
return new BlockStorage(this.bitArray.copy(), new TIntArrayList(this.palette));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
package org.geysermc.connector.world.chunk.palette;
|
package org.geysermc.connector.world.chunk.bitarray;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapted from NukkitX: https://github.com/NukkitX/Nukkit
|
* Adapted from NukkitX: https://github.com/NukkitX/Nukkit
|
||||||
*/
|
*/
|
||||||
public interface Palette {
|
public interface BitArray {
|
||||||
|
|
||||||
void set(int index, int value);
|
void set(int index, int value);
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ public interface Palette {
|
||||||
|
|
||||||
int[] getWords();
|
int[] getWords();
|
||||||
|
|
||||||
PaletteVersion getVersion();
|
BitArrayVersion getVersion();
|
||||||
|
|
||||||
Palette copy();
|
BitArray copy();
|
||||||
}
|
}
|
|
@ -1,12 +1,11 @@
|
||||||
package org.geysermc.connector.world.chunk.palette;
|
package org.geysermc.connector.world.chunk.bitarray;
|
||||||
|
|
||||||
import org.geysermc.connector.utils.MathUtils;
|
import org.geysermc.connector.utils.MathUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapted from NukkitX: https://github.com/NukkitX/Nukkit
|
* Adapted from NukkitX: https://github.com/NukkitX/Nukkit
|
||||||
*/
|
*/
|
||||||
public enum PaletteVersion {
|
public enum BitArrayVersion {
|
||||||
|
|
||||||
V16(16, 2, null),
|
V16(16, 2, null),
|
||||||
V8(8, 4, V16),
|
V8(8, 4, V16),
|
||||||
V6(6, 5, V8), // 2 bit padding
|
V6(6, 5, V8), // 2 bit padding
|
||||||
|
@ -19,20 +18,29 @@ public enum PaletteVersion {
|
||||||
final byte bits;
|
final byte bits;
|
||||||
final byte entriesPerWord;
|
final byte entriesPerWord;
|
||||||
final int maxEntryValue;
|
final int maxEntryValue;
|
||||||
final PaletteVersion next;
|
final BitArrayVersion next;
|
||||||
|
|
||||||
PaletteVersion(int bits, int entriesPerWord, PaletteVersion next) {
|
BitArrayVersion(int bits, int entriesPerWord, BitArrayVersion next) {
|
||||||
this.bits = (byte) bits;
|
this.bits = (byte) bits;
|
||||||
this.entriesPerWord = (byte) entriesPerWord;
|
this.entriesPerWord = (byte) entriesPerWord;
|
||||||
this.maxEntryValue = (1 << this.bits) - 1;
|
this.maxEntryValue = (1 << this.bits) - 1;
|
||||||
this.next = next;
|
this.next = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Palette createPalette(int size) {
|
public static BitArrayVersion get(int version, boolean read) {
|
||||||
|
for (BitArrayVersion ver : values()) {
|
||||||
|
if ((!read && ver.entriesPerWord <= version) || (read && ver.bits == version)) {
|
||||||
|
return ver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Invalid palette version: " + version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitArray createPalette(int size) {
|
||||||
return this.createPalette(size, new int[MathUtils.ceil((float) size / entriesPerWord)]);
|
return this.createPalette(size, new int[MathUtils.ceil((float) size / entriesPerWord)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte getVersion() {
|
public byte getId() {
|
||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,25 +48,20 @@ public enum PaletteVersion {
|
||||||
return maxEntryValue;
|
return maxEntryValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PaletteVersion next() {
|
public int getWordsForSize(int size) {
|
||||||
|
return MathUtils.ceil((float) size / entriesPerWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitArrayVersion next() {
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Palette createPalette(int size, int[] words) {
|
public BitArray createPalette(int size, int[] words) {
|
||||||
if (this == V3 || this == V5 || this == V6) {
|
if (this == V3 || this == V5 || this == V6) {
|
||||||
// Padded palettes aren't able to use bitwise operations due to their padding.
|
// Padded palettes aren't able to use bitwise operations due to their padding.
|
||||||
return new PaddedPalette(this, size, words);
|
return new PaddedBitArray(this, size, words);
|
||||||
} else {
|
} else {
|
||||||
return new Pow2Palette(this, size, words);
|
return new Pow2BitArray(this, size, words);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PaletteVersion getVersion(int version, boolean read) {
|
|
||||||
for (PaletteVersion ver : values()) {
|
|
||||||
if ( ( !read && ver.entriesPerWord <= version ) || ( read && ver.bits == version ) ) {
|
|
||||||
return ver;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Invalid palette version: " + version);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package org.geysermc.connector.world.chunk.palette;
|
package org.geysermc.connector.world.chunk.bitarray;
|
||||||
|
|
||||||
import com.nukkitx.network.util.Preconditions;
|
import com.nukkitx.network.util.Preconditions;
|
||||||
import org.geysermc.connector.utils.MathUtils;
|
import org.geysermc.connector.utils.MathUtils;
|
||||||
|
@ -8,7 +8,7 @@ import java.util.Arrays;
|
||||||
/**
|
/**
|
||||||
* Adapted from NukkitX: https://github.com/NukkitX/Nukkit
|
* Adapted from NukkitX: https://github.com/NukkitX/Nukkit
|
||||||
*/
|
*/
|
||||||
public class PaddedPalette implements Palette {
|
public class PaddedBitArray implements BitArray {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array used to store data
|
* Array used to store data
|
||||||
|
@ -18,14 +18,14 @@ public class PaddedPalette implements Palette {
|
||||||
/**
|
/**
|
||||||
* Palette version information
|
* Palette version information
|
||||||
*/
|
*/
|
||||||
private final PaletteVersion version;
|
private final BitArrayVersion version;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of entries in this palette (<b>not</b> the length of the words array that internally backs this palette)
|
* Number of entries in this palette (<b>not</b> the length of the words array that internally backs this palette)
|
||||||
*/
|
*/
|
||||||
private final int size;
|
private final int size;
|
||||||
|
|
||||||
PaddedPalette(PaletteVersion version, int size, int[] words) {
|
PaddedBitArray(BitArrayVersion version, int size, int[] words) {
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.words = words;
|
this.words = words;
|
||||||
|
@ -66,12 +66,12 @@ public class PaddedPalette implements Palette {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PaletteVersion getVersion() {
|
public BitArrayVersion getVersion() {
|
||||||
return this.version;
|
return this.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Palette copy() {
|
public BitArray copy() {
|
||||||
return new PaddedPalette(this.version, this.size, Arrays.copyOf(this.words, this.words.length));
|
return new PaddedBitArray(this.version, this.size, Arrays.copyOf(this.words, this.words.length));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package org.geysermc.connector.world.chunk.palette;
|
package org.geysermc.connector.world.chunk.bitarray;
|
||||||
|
|
||||||
import com.nukkitx.network.util.Preconditions;
|
import com.nukkitx.network.util.Preconditions;
|
||||||
import org.geysermc.connector.utils.MathUtils;
|
import org.geysermc.connector.utils.MathUtils;
|
||||||
|
@ -8,7 +8,7 @@ import java.util.Arrays;
|
||||||
/**
|
/**
|
||||||
* Adapted from NukkitX: https://github.com/NukkitX/Nukkit
|
* Adapted from NukkitX: https://github.com/NukkitX/Nukkit
|
||||||
*/
|
*/
|
||||||
public class Pow2Palette implements Palette {
|
public class Pow2BitArray implements BitArray {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array used to store data
|
* Array used to store data
|
||||||
|
@ -18,14 +18,14 @@ public class Pow2Palette implements Palette {
|
||||||
/**
|
/**
|
||||||
* Palette version information
|
* Palette version information
|
||||||
*/
|
*/
|
||||||
private final PaletteVersion version;
|
private final BitArrayVersion version;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of entries in this palette (<b>not</b> the length of the words array that internally backs this palette)
|
* Number of entries in this palette (<b>not</b> the length of the words array that internally backs this palette)
|
||||||
*/
|
*/
|
||||||
private final int size;
|
private final int size;
|
||||||
|
|
||||||
Pow2Palette(PaletteVersion version, int size, int[] words) {
|
Pow2BitArray(BitArrayVersion version, int size, int[] words) {
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.words = words;
|
this.words = words;
|
||||||
|
@ -41,7 +41,7 @@ public class Pow2Palette implements Palette {
|
||||||
*/
|
*/
|
||||||
public void set(int index, int value) {
|
public void set(int index, int value) {
|
||||||
Preconditions.checkElementIndex(index, this.size);
|
Preconditions.checkElementIndex(index, this.size);
|
||||||
Preconditions.checkArgument(value >= 0 && value <= this.version.maxEntryValue, "Invalid value");
|
Preconditions.checkArgument(value >= 0 && value <= this.version.maxEntryValue, "Invalid value %s", value);
|
||||||
int bitIndex = index * this.version.bits;
|
int bitIndex = index * this.version.bits;
|
||||||
int arrayIndex = bitIndex >> 5;
|
int arrayIndex = bitIndex >> 5;
|
||||||
int offset = bitIndex & 31;
|
int offset = bitIndex & 31;
|
||||||
|
@ -75,12 +75,12 @@ public class Pow2Palette implements Palette {
|
||||||
return this.words;
|
return this.words;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PaletteVersion getVersion() {
|
public BitArrayVersion getVersion() {
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Palette copy() {
|
public BitArray copy() {
|
||||||
return new Pow2Palette(this.version, this.size, Arrays.copyOf(this.words, this.words.length));
|
return new Pow2BitArray(this.version, this.size, Arrays.copyOf(this.words, this.words.length));
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue