mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Biomes
This commit is contained in:
parent
a55784ea50
commit
5511ef8da8
7 changed files with 490 additions and 8 deletions
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
|
|||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import org.geysermc.connector.network.translators.TranslatorsInit;
|
||||
import org.geysermc.connector.network.translators.block.BlockEntry;
|
||||
import org.geysermc.connector.world.BiomePalette;
|
||||
import org.geysermc.connector.world.chunk.ChunkSection;
|
||||
|
||||
public class ChunkUtils {
|
||||
|
@ -19,17 +20,27 @@ public class ChunkUtils {
|
|||
|
||||
chunkData.sections = new ChunkSection[chunkSectionCount];
|
||||
|
||||
for(int biome = 0; biome < 256; biome++) {
|
||||
if (column.getBiomeData()[biome] <= Byte.MAX_VALUE) {
|
||||
chunkData.biomes[biome] = (byte) (column.getBiomeData()[biome]);
|
||||
} else {
|
||||
chunkData.biomes[biome] = (byte) (column.getBiomeData()[biome] - 255);
|
||||
int[] biomesConverted = new int[256];
|
||||
|
||||
try {
|
||||
for (int biomeX = 0; biomeX < 16; biomeX++) {
|
||||
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()) {
|
||||
System.out.println(tag.toString());
|
||||
//TODO: Tiles
|
||||
//System.out.println(tag.toString());
|
||||
}
|
||||
|
||||
for (int chunkY = 0; chunkY < chunkSectionCount; chunkY++) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue