This commit is contained in:
EOT3000 2019-10-28 20:49:45 -04:00
parent a55784ea50
commit 5511ef8da8
7 changed files with 490 additions and 8 deletions

View file

@ -0,0 +1,91 @@
package org.geysermc.connector.utils;
/**
* This file property the NukkitX project
* https://github.com/NukkitX/Nukkit
* @author https://github.com/boy0001/
*/
public final class BitArray256 {
private final int bitsPerEntry;
public final long[] data;
public BitArray256(int bitsPerEntry) {
this.bitsPerEntry = bitsPerEntry;
int longLen = (this.bitsPerEntry * 256) >> 6;
this.data = new long[longLen];
}
public BitArray256(BitArray256 other) {
this.bitsPerEntry = other.bitsPerEntry;
this.data = other.data.clone();
}
public final void setAt(int index, int value) {
int bitIndexStart = index * bitsPerEntry;
int longIndexStart = bitIndexStart >> 6;
int localBitIndexStart = bitIndexStart & 63;
this.data[longIndexStart] = this.data[longIndexStart] & ~((long) ((1 << bitsPerEntry) - 1) << localBitIndexStart) | ((long) value) << localBitIndexStart;
if(localBitIndexStart > 64 - bitsPerEntry) {
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) {
int bitIndexStart = index * bitsPerEntry;
int longIndexStart = bitIndexStart >> 6;
int localBitIndexStart = bitIndexStart & 63;
if(localBitIndexStart <= 64 - bitsPerEntry) {
return (int)(this.data[longIndexStart] >>> localBitIndexStart & ((1 << bitsPerEntry) - 1));
} else {
int localShift = 64 - localBitIndexStart;
return (int) ((this.data[longIndexStart] >>> localBitIndexStart | this.data[longIndexStart + 1] << localShift) & ((1 << bitsPerEntry) - 1));
}
}
public final void fromRaw(int[] arr) {
for (int i = 0; i < arr.length; i++) {
setAt(i, arr[i]);
}
}
public BitArray256 grow(int newBitsPerEntry) {
int amtGrow = newBitsPerEntry - this.bitsPerEntry;
if (amtGrow <= 0) return this;
BitArray256 newBitArray = new BitArray256(newBitsPerEntry);
int[] buffer = ThreadCache.intCache256.get();
toRaw(buffer);
newBitArray.fromRaw(buffer);
return newBitArray;
}
public BitArray256 growSlow(int bitsPerEntry) {
BitArray256 newBitArray = new BitArray256(bitsPerEntry);
for (int i = 0; i < 256; i++) {
newBitArray.setAt(i, getAt(i));
}
return newBitArray;
}
public final int[] toRaw(int[] buffer) {
for (int i = 0; i < buffer.length; i++) {
buffer[i] = getAt(i);
}
return buffer;
}
public final int[] toRaw() {
return toRaw(new int[256]);
}
public BitArray256 clone() {
return new BitArray256(this);
}
}

View file

@ -6,6 +6,7 @@ import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.geysermc.connector.network.translators.TranslatorsInit; import org.geysermc.connector.network.translators.TranslatorsInit;
import org.geysermc.connector.network.translators.block.BlockEntry; import org.geysermc.connector.network.translators.block.BlockEntry;
import org.geysermc.connector.world.BiomePalette;
import org.geysermc.connector.world.chunk.ChunkSection; import org.geysermc.connector.world.chunk.ChunkSection;
public class ChunkUtils { public class ChunkUtils {
@ -19,17 +20,27 @@ public class ChunkUtils {
chunkData.sections = new ChunkSection[chunkSectionCount]; chunkData.sections = new ChunkSection[chunkSectionCount];
for(int biome = 0; biome < 256; biome++) { int[] biomesConverted = new int[256];
if (column.getBiomeData()[biome] <= Byte.MAX_VALUE) {
chunkData.biomes[biome] = (byte) (column.getBiomeData()[biome]); try {
} else { for (int biomeX = 0; biomeX < 16; biomeX++) {
chunkData.biomes[biome] = (byte) (column.getBiomeData()[biome] - 255); for (int biomeZ = 0; biomeZ < 16; biomeZ++) {
biomesConverted[(biomeX << 4) | biomeZ] = column.getBiomeData()[(biomeZ * 4) | biomeX];
}
} }
}
BiomePalette palette = new BiomePalette(biomesConverted);
for (int biomeX = 0; biomeX < 16; biomeX++) {
for (int biomeZ = 0; biomeZ < 16; biomeZ++) {
chunkData.biomes[(biomeX << 4) | biomeZ] = (byte) (palette.get(biomeX, biomeZ));
}
}
} catch (Exception e) {}
for(CompoundTag tag : column.getTileEntities()) { for(CompoundTag tag : column.getTileEntities()) {
System.out.println(tag.toString()); //TODO: Tiles
//System.out.println(tag.toString());
} }
for (int chunkY = 0; chunkY < chunkSectionCount; chunkY++) { for (int chunkY = 0; chunkY < chunkSectionCount; chunkY++) {

View file

@ -0,0 +1,106 @@
package org.geysermc.connector.utils;
import java.util.Arrays;
/**
* This file property the NukkitX project
* https://github.com/NukkitX/Nukkit
* @author https://github.com/boy0001/
*/
public class IntPalette {
private static int[] INT0 = new int[0];
private int[] keys = INT0;
private int lastIndex = Integer.MIN_VALUE;
public void add(int key) {
keys = insert(key);
lastIndex = Integer.MIN_VALUE;
}
protected void set(int[] keys) {
this.keys = keys;
lastIndex = Integer.MIN_VALUE;
}
private int[] insert(int val) {
lastIndex = Integer.MIN_VALUE;
if (keys.length == 0) {
return new int[] { val };
}
else if (val < keys[0]) {
int[] s = new int[keys.length + 1];
System.arraycopy(keys, 0, s, 1, keys.length);
s[0] = val;
return s;
} else if (val > keys[keys.length - 1]) {
int[] s = Arrays.copyOf(keys, keys.length + 1);
s[keys.length] = val;
return s;
}
int[] s = Arrays.copyOf(keys, keys.length + 1);
for (int i = 0; i < s.length; i++) {
if (keys[i] < val) {
continue;
}
System.arraycopy(keys, i, s, i + 1, s.length - i - 1);
s[i] = val;
break;
}
return s;
}
public int getKey(int index) {
return keys[index];
}
public int getValue(int key) {
int lastTmp = lastIndex;
boolean hasLast = lastTmp != Integer.MIN_VALUE;
int index;
if (hasLast) {
int lastKey = keys[lastTmp];
if (lastKey == key) return lastTmp;
if (lastKey > key) {
index = binarySearch0(0, lastTmp, key);
} else {
index = binarySearch0(lastTmp + 1, keys.length, key);
}
} else {
index = binarySearch0(0, keys.length, key);
}
if (index >= keys.length || index < 0) {
return lastIndex = Integer.MIN_VALUE;
} else {
return lastIndex = index;
}
}
private int binarySearch0(int fromIndex, int toIndex, int key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = keys[mid];
if (midVal < key)
low = mid + 1;
else if (midVal > key)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found.
}
public int length() {
return keys.length;
}
public IntPalette clone() {
IntPalette p = new IntPalette();
p.keys = this.keys != INT0 ? this.keys.clone() : INT0;
p.lastIndex = this.lastIndex;
return p;
}
}

View file

@ -0,0 +1,123 @@
package org.geysermc.connector.utils;
import java.lang.ref.Reference;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedDeque;
public abstract class IterableThreadLocal<T> extends ThreadLocal<T> implements Iterable<T> {
private ThreadLocal<T> flag;
private ConcurrentLinkedDeque<T> allValues = new ConcurrentLinkedDeque<>();
public IterableThreadLocal() {
}
@Override
protected final T initialValue() {
T value = init();
if (value != null) {
allValues.add(value);
}
return value;
}
@Override
public final Iterator<T> iterator() {
return getAll().iterator();
}
public T init() {
return null;
}
public void clean() {
IterableThreadLocal.clean(this);
}
public static void clean(ThreadLocal instance) {
try {
ThreadGroup rootGroup = Thread.currentThread( ).getThreadGroup( );
ThreadGroup parentGroup;
while ( ( parentGroup = rootGroup.getParent() ) != null ) {
rootGroup = parentGroup;
}
Thread[] threads = new Thread[ rootGroup.activeCount() ];
if (threads.length != 0) {
while (rootGroup.enumerate(threads, true) == threads.length) {
threads = new Thread[threads.length * 2];
}
}
Field tl = Thread.class.getDeclaredField("threadLocals");
tl.setAccessible(true);
Method methodRemove = null;
for (Thread thread : threads) {
if (thread != null) {
Object tlm = tl.get(thread);
if (tlm != null) {
if (methodRemove == null) {
methodRemove = tlm.getClass().getDeclaredMethod("remove", ThreadLocal.class);
methodRemove.setAccessible(true);
}
if (methodRemove != null) {
try {
methodRemove.invoke(tlm, instance);
} catch (Throwable ignore) {}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void cleanAll() {
try {
// Get a reference to the thread locals table of the current thread
Thread thread = Thread.currentThread();
Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
threadLocalsField.setAccessible(true);
Object threadLocalTable = threadLocalsField.get(thread);
// Get a reference to the array holding the thread local variables inside the
// ThreadLocalMap of the current thread
Class threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
Field tableField = threadLocalMapClass.getDeclaredField("table");
tableField.setAccessible(true);
Object table = tableField.get(threadLocalTable);
// The key to the ThreadLocalMap is a WeakReference object. The referent field of this object
// is a reference to the actual ThreadLocal variable
Field referentField = Reference.class.getDeclaredField("referent");
referentField.setAccessible(true);
for (int i = 0; i < Array.getLength(table); i++) {
// Each entry in the table array of ThreadLocalMap is an Entry object
// representing the thread local reference and its value
Object entry = Array.get(table, i);
if (entry != null) {
// Get a reference to the thread local object and remove it from the table
ThreadLocal threadLocal = (ThreadLocal)referentField.get(entry);
clean(threadLocal);
}
}
} catch(Exception e) {
// We will tolerate an exception here and just log it
throw new IllegalStateException(e);
}
}
public final Collection<T> getAll() {
return Collections.unmodifiableCollection(allValues);
}
@Override
protected void finalize() throws Throwable {
clean(this);
super.finalize();
}
}

View file

@ -0,0 +1,11 @@
package org.geysermc.connector.utils;
/**
* This file property the NukkitX project
* https://github.com/NukkitX/Nukkit
*/
public class MathHelper {
public static int log2(int bits) {
return Integer.SIZE - Integer.numberOfLeadingZeros(bits);
}
}

View file

@ -0,0 +1,10 @@
package org.geysermc.connector.utils;
public class ThreadCache {
public static final IterableThreadLocal<int[]> intCache256 = new IterableThreadLocal<int[]>() {
@Override
public int[] init() {
return new int[256];
}
};
}

View file

@ -0,0 +1,130 @@
package org.geysermc.connector.world;
import org.geysermc.connector.utils.BitArray256;
import org.geysermc.connector.utils.IntPalette;
import org.geysermc.connector.utils.MathHelper;
import org.geysermc.connector.utils.ThreadCache;
import java.util.Arrays;
/**
* This file property the NukkitX project
* https://github.com/NukkitX/Nukkit
*/
public class BiomePalette {
private int biome;
private BitArray256 encodedData;
private IntPalette palette;
private BiomePalette(BiomePalette clone) {
this.biome = clone.biome;
if (clone.encodedData != null) {
this.encodedData = clone.encodedData.clone();
this.palette = clone.palette.clone();
}
}
public BiomePalette(int[] biomeColors) {
for (int i = 0; i < 256; i++) {
set(i, biomeColors[i]);
}
}
public BiomePalette() {
this.biome = Integer.MIN_VALUE;
}
public int get(int x, int z) {
return get(getIndex(x, z));
}
public synchronized int get(int index) {
if (encodedData == null) return biome;
return palette.getKey(encodedData.getAt(index));
}
public void set(int x, int z, int value) {
set(getIndex(x, z), value);
}
public synchronized void set(int index, int value) {
if (encodedData == null) {
if (value == biome) return;
if (biome == Integer.MIN_VALUE) {
biome = value;
return;
}
synchronized (this) {
palette = new IntPalette();
palette.add(biome);
palette.add(value);
encodedData = new BitArray256(1);
if (value < biome) {
Arrays.fill(encodedData.data, -1);
encodedData.setAt(index, 0);
} else {
encodedData.setAt(index, 1);
}
return;
}
}
int encodedValue = palette.getValue(value);
if (encodedValue != Integer.MIN_VALUE) {
encodedData.setAt(index, encodedValue);
} else {
synchronized (this) {
int[] raw = encodedData.toRaw(ThreadCache.intCache256.get());
// TODO skip remapping of raw data and use grow instead if `remap`
// boolean remap = value < palette.getValue(palette.length() - 1);
for (int i = 0; i < 256; i++) {
raw[i] = palette.getKey(raw[i]);
}
int oldRaw = raw[4];
raw[index] = value;
palette.add(value);
int oldBits = MathHelper.log2(palette.length() - 2);
int newBits = MathHelper.log2(palette.length() - 1);
if (oldBits != newBits) {
encodedData = new BitArray256(newBits);
}
for (int i = 0; i < raw.length; i++) {
raw[i] = palette.getValue(raw[i]);
}
encodedData.fromRaw(raw);
}
}
}
public synchronized int[] toRaw() {
int[] buffer = ThreadCache.intCache256.get();
if (encodedData == null) {
Arrays.fill(buffer, biome);
} else {
synchronized (this) {
buffer = encodedData.toRaw(buffer);
for (int i = 0; i < 256; i++) {
buffer[i] = palette.getKey(buffer[i]);
}
}
}
return buffer;
}
public int getIndex(int x, int z) {
return (z << 4) | x;
}
public synchronized BiomePalette clone() {
return new BiomePalette(this);
}
}