Mirror von
https://github.com/ViaVersion/ViaVersion.git
synchronisiert 2024-12-25 15:50:10 +01:00
Commit
669ec3f453
@ -1,126 +0,0 @@
|
||||
package org.spacehq.mc.protocol.data.game.chunk;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class BlockStorage {
|
||||
private int bitsPerEntry;
|
||||
|
||||
private List<Integer> states;
|
||||
private FlexibleStorage storage;
|
||||
|
||||
public BlockStorage() {
|
||||
this.bitsPerEntry = 4;
|
||||
|
||||
this.states = new ArrayList<>();
|
||||
this.states.add(0);
|
||||
|
||||
this.storage = new FlexibleStorage(this.bitsPerEntry, 4096);
|
||||
}
|
||||
|
||||
public BlockStorage(ByteBuf in) throws IOException {
|
||||
this.bitsPerEntry = in.readUnsignedByte();
|
||||
|
||||
this.states = new ArrayList<>();
|
||||
int stateCount = PacketUtil.readVarInt(in);
|
||||
for (int i = 0; i < stateCount; i++) {
|
||||
this.states.add(PacketUtil.readVarInt(in));
|
||||
}
|
||||
|
||||
this.storage = new FlexibleStorage(this.bitsPerEntry, PacketUtil.readLongs(PacketUtil.readVarInt(in), in));
|
||||
}
|
||||
|
||||
private static int index(int x, int y, int z) {
|
||||
return y << 8 | z << 4 | x;
|
||||
}
|
||||
|
||||
public void write(ByteBuf out) throws IOException {
|
||||
out.writeByte(this.bitsPerEntry);
|
||||
|
||||
PacketUtil.writeVarInt(this.states.size(), out);
|
||||
for (int state : this.states) {
|
||||
PacketUtil.writeVarInt(state, out);
|
||||
}
|
||||
|
||||
long[] data = this.storage.getData();
|
||||
PacketUtil.writeVarInt(data.length, out);
|
||||
PacketUtil.writeLongs(data, out);
|
||||
}
|
||||
|
||||
public int getBitsPerEntry() {
|
||||
return this.bitsPerEntry;
|
||||
}
|
||||
|
||||
public List<Integer> getStates() {
|
||||
return Collections.unmodifiableList(this.states);
|
||||
}
|
||||
|
||||
public FlexibleStorage getStorage() {
|
||||
return this.storage;
|
||||
}
|
||||
|
||||
public int get(int x, int y, int z) {
|
||||
int id = this.storage.get(index(x, y, z));
|
||||
return this.bitsPerEntry <= 8 ? (id >= 0 && id < this.states.size() ? this.states.get(id) : 0) : id;
|
||||
}
|
||||
|
||||
public void set(int x, int y, int z, int state) {
|
||||
set(index(x, y, z), state);
|
||||
}
|
||||
|
||||
public void set(int ind, int state) {
|
||||
int id = this.bitsPerEntry <= 8 ? this.states.indexOf(state) : state;
|
||||
if (id == -1) {
|
||||
this.states.add(state);
|
||||
if (this.states.size() > 1 << this.bitsPerEntry) {
|
||||
this.bitsPerEntry++;
|
||||
|
||||
List<Integer> oldStates = this.states;
|
||||
if (this.bitsPerEntry > 8) {
|
||||
oldStates = new ArrayList<>(this.states);
|
||||
this.states.clear();
|
||||
this.bitsPerEntry = 13;
|
||||
}
|
||||
|
||||
FlexibleStorage oldStorage = this.storage;
|
||||
this.storage = new FlexibleStorage(this.bitsPerEntry, this.storage.getSize());
|
||||
for (int index = 0; index < this.storage.getSize(); index++) {
|
||||
int value = oldStorage.get(index);
|
||||
this.storage.set(index, this.bitsPerEntry <= 8 ? value : oldStates.get(value));
|
||||
}
|
||||
}
|
||||
|
||||
id = this.bitsPerEntry <= 8 ? this.states.indexOf(state) : state;
|
||||
}
|
||||
|
||||
this.storage.set(ind, id);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
for (int index = 0; index < this.storage.getSize(); index++) {
|
||||
if (this.storage.get(index) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this == o || (o instanceof BlockStorage && this.bitsPerEntry == ((BlockStorage) o).bitsPerEntry && this.states.equals(((BlockStorage) o).states) && this.storage.equals(((BlockStorage) o).storage));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.bitsPerEntry;
|
||||
result = 31 * result + this.states.hashCode();
|
||||
result = 31 * result + this.storage.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package org.spacehq.mc.protocol.data.game.chunk;
|
||||
|
||||
public class Chunk {
|
||||
private BlockStorage blocks;
|
||||
private NibbleArray3d blocklight;
|
||||
private NibbleArray3d skylight;
|
||||
|
||||
public Chunk(boolean skylight) {
|
||||
this(new BlockStorage(), new NibbleArray3d(2048), skylight ? new NibbleArray3d(2048) : null);
|
||||
}
|
||||
|
||||
public Chunk(BlockStorage blocks, NibbleArray3d blocklight, NibbleArray3d skylight) {
|
||||
this.blocks = blocks;
|
||||
this.blocklight = blocklight;
|
||||
this.skylight = skylight;
|
||||
}
|
||||
|
||||
public BlockStorage getBlocks() {
|
||||
return this.blocks;
|
||||
}
|
||||
|
||||
public NibbleArray3d getBlockLight() {
|
||||
return this.blocklight;
|
||||
}
|
||||
|
||||
public NibbleArray3d getSkyLight() {
|
||||
return this.skylight;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.blocks.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this == o || (o instanceof Chunk && this.blocks.equals(((Chunk) o).blocks) && this.blocklight.equals(((Chunk) o).blocklight) && ((this.skylight == null && (((Chunk) o).skylight == null)) || (this.skylight != null && this.skylight.equals(((Chunk) o).skylight))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.blocks.hashCode();
|
||||
result = 31 * result + this.blocklight.hashCode();
|
||||
result = 31 * result + (this.skylight != null ? this.skylight.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
package org.spacehq.mc.protocol.data.game.chunk;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class Column {
|
||||
private int x;
|
||||
private int z;
|
||||
private Chunk chunks[];
|
||||
private byte biomeData[];
|
||||
|
||||
private boolean skylight;
|
||||
|
||||
public Column(int x, int z, Chunk chunks[]) {
|
||||
this(x, z, chunks, null);
|
||||
}
|
||||
|
||||
public Column(int x, int z, Chunk chunks[], byte biomeData[]) {
|
||||
if(chunks.length != 16) {
|
||||
throw new IllegalArgumentException("Chunk array length must be 16.");
|
||||
}
|
||||
|
||||
if(biomeData != null && biomeData.length != 256) {
|
||||
throw new IllegalArgumentException("Biome data array length must be 256.");
|
||||
}
|
||||
|
||||
this.skylight = false;
|
||||
boolean noSkylight = false;
|
||||
for (Chunk chunk : chunks) {
|
||||
if (chunk != null) {
|
||||
if (chunk.getSkyLight() == null) {
|
||||
noSkylight = true;
|
||||
} else {
|
||||
this.skylight = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(noSkylight && this.skylight) {
|
||||
throw new IllegalArgumentException("Either all chunks must have skylight values or none must have them.");
|
||||
}
|
||||
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
this.chunks = chunks;
|
||||
this.biomeData = biomeData;
|
||||
}
|
||||
|
||||
|
||||
public boolean hasBiomeData() {
|
||||
return this.biomeData != null;
|
||||
}
|
||||
|
||||
public boolean hasSkylight() {
|
||||
return this.skylight;
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
package org.spacehq.mc.protocol.data.game.chunk;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class FlexibleStorage {
|
||||
private final long[] data;
|
||||
private final int bitsPerEntry;
|
||||
private final int size;
|
||||
private final long maxEntryValue;
|
||||
|
||||
public FlexibleStorage(int bitsPerEntry, int size) {
|
||||
this(bitsPerEntry, new long[roundToNearest(size * bitsPerEntry, 64) / 64]);
|
||||
}
|
||||
|
||||
public FlexibleStorage(int bitsPerEntry, long[] data) {
|
||||
if(bitsPerEntry < 1 || bitsPerEntry > 32) {
|
||||
throw new IllegalArgumentException("BitsPerEntry cannot be outside of accepted range.");
|
||||
}
|
||||
|
||||
this.bitsPerEntry = bitsPerEntry;
|
||||
this.data = data;
|
||||
|
||||
this.size = this.data.length * 64 / this.bitsPerEntry;
|
||||
this.maxEntryValue = (1L << this.bitsPerEntry) - 1;
|
||||
}
|
||||
|
||||
public long[] getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public int getBitsPerEntry() {
|
||||
return this.bitsPerEntry;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public int get(int index) {
|
||||
if(index < 0 || index > this.size - 1) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
int bitIndex = index * this.bitsPerEntry;
|
||||
int startIndex = bitIndex / 64;
|
||||
int endIndex = ((index + 1) * this.bitsPerEntry - 1) / 64;
|
||||
int startBitSubIndex = bitIndex % 64;
|
||||
if(startIndex == endIndex) {
|
||||
return (int) (this.data[startIndex] >>> startBitSubIndex & this.maxEntryValue);
|
||||
} else {
|
||||
int endBitSubIndex = 64 - startBitSubIndex;
|
||||
return (int) ((this.data[startIndex] >>> startBitSubIndex | this.data[endIndex] << endBitSubIndex) & this.maxEntryValue);
|
||||
}
|
||||
}
|
||||
|
||||
public void set(int index, int value) {
|
||||
if(index < 0 || index > this.size - 1) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
if(value < 0 || value > this.maxEntryValue) {
|
||||
throw new IllegalArgumentException("Value cannot be outside of accepted range.");
|
||||
}
|
||||
|
||||
int bitIndex = index * this.bitsPerEntry;
|
||||
int startIndex = bitIndex / 64;
|
||||
int endIndex = ((index + 1) * this.bitsPerEntry - 1) / 64;
|
||||
int startBitSubIndex = bitIndex % 64;
|
||||
this.data[startIndex] = this.data[startIndex] & ~(this.maxEntryValue << startBitSubIndex) | ((long) value & this.maxEntryValue) << startBitSubIndex;
|
||||
if(startIndex != endIndex) {
|
||||
int endBitSubIndex = 64 - startBitSubIndex;
|
||||
this.data[endIndex] = this.data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & this.maxEntryValue) >> endBitSubIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private static int roundToNearest(int value, int roundTo) {
|
||||
if(roundTo == 0) {
|
||||
return 0;
|
||||
} else if(value == 0) {
|
||||
return roundTo;
|
||||
} else {
|
||||
if(value < 0) {
|
||||
roundTo *= -1;
|
||||
}
|
||||
|
||||
int remainder = value % roundTo;
|
||||
return remainder != 0 ? value + roundTo - remainder : value;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this == o || (o instanceof FlexibleStorage && Arrays.equals(this.data, ((FlexibleStorage) o).data) && this.bitsPerEntry == ((FlexibleStorage) o).bitsPerEntry && this.size == ((FlexibleStorage) o).size && this.maxEntryValue == ((FlexibleStorage) o).maxEntryValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Arrays.hashCode(this.data);
|
||||
result = 31 * result + this.bitsPerEntry;
|
||||
result = 31 * result + this.size;
|
||||
result = 31 * result + (int) this.maxEntryValue;
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
package org.spacehq.mc.protocol.data.game.chunk;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class NibbleArray3d {
|
||||
private byte[] data;
|
||||
|
||||
public NibbleArray3d(int size) {
|
||||
this.data = new byte[size];
|
||||
}
|
||||
|
||||
public NibbleArray3d(byte[] array) {
|
||||
this.data = array;
|
||||
}
|
||||
|
||||
public NibbleArray3d(ByteBuf in, int size) throws IOException {
|
||||
this.data = new byte[size];
|
||||
in.readBytes(this.data);
|
||||
}
|
||||
|
||||
public void write(ByteBuf out) throws IOException {
|
||||
out.writeBytes(this.data);
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public int get(int x, int y, int z) {
|
||||
int key = y << 8 | z << 4 | x;
|
||||
int index = key >> 1;
|
||||
int part = key & 1;
|
||||
return part == 0 ? this.data[index] & 15 : this.data[index] >> 4 & 15;
|
||||
}
|
||||
|
||||
public void set(int x, int y, int z, int val) {
|
||||
int key = y << 8 | z << 4 | x;
|
||||
int index = key >> 1;
|
||||
int part = key & 1;
|
||||
if(part == 0) {
|
||||
this.data[index] = (byte) (this.data[index] & 240 | val & 15);
|
||||
} else {
|
||||
this.data[index] = (byte) (this.data[index] & 15 | (val & 15) << 4);
|
||||
}
|
||||
}
|
||||
|
||||
public void fill(int val) {
|
||||
for(int index = 0; index < this.data.length << 1; index++) {
|
||||
int ind = index >> 1;
|
||||
int part = index & 1;
|
||||
if(part == 0) {
|
||||
this.data[ind] = (byte) (this.data[ind] & 240 | val & 15);
|
||||
} else {
|
||||
this.data[ind] = (byte) (this.data[ind] & 15 | (val & 15) << 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this == o || (o instanceof NibbleArray3d && Arrays.equals(this.data, ((NibbleArray3d) o).data));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(this.data);
|
||||
}
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
package org.spacehq.mc.protocol.util;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.spacehq.mc.protocol.data.game.chunk.Chunk;
|
||||
import org.spacehq.mc.protocol.data.game.chunk.Column;
|
||||
import org.spacehq.mc.protocol.data.game.chunk.NibbleArray3d;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
/* From https://github.com/Steveice10/MCProtocolLib/ */
|
||||
/* No credit taken for writing this code, and used accordance to it's license
|
||||
Original by Steveice10, modified to suit this plugin.
|
||||
*/
|
||||
public class NetUtil {
|
||||
public static int writeNewColumn(ByteBuf out, Column column, boolean fullChunk, boolean hasSkylight) throws IOException {
|
||||
int mask = 0;
|
||||
Chunk chunks[] = column.getChunks();
|
||||
for (int index = 0; index < chunks.length; index++) {
|
||||
Chunk chunk = chunks[index];
|
||||
if (chunk != null && (!fullChunk || !chunk.isEmpty())) {
|
||||
mask |= 1 << index;
|
||||
chunk.getBlocks().write(out);
|
||||
chunk.getBlockLight().write(out);
|
||||
if (hasSkylight || column.hasSkylight()) {
|
||||
chunk.getSkyLight().write(out); // TODO: Make a PR to original lib to correct this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fullChunk && column.getBiomeData() != null) {
|
||||
out.writeBytes(column.getBiomeData());
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
public static Column readOldChunkData(int x, int z, boolean isFullChunk, int bitmask, byte[] input, boolean checkForSky, boolean hasSkyLight) {
|
||||
int pos = 0;
|
||||
int expected = isFullChunk ? 256 : 0;
|
||||
boolean sky = false;
|
||||
ShortBuffer buf = ByteBuffer.wrap(input).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
||||
// 0 = Calculate expected length and determine if the packet has skylight.
|
||||
// 1 = Create chunks from mask and get blocks.
|
||||
// 2 = Get block light.
|
||||
// 3 = Get sky light.
|
||||
Chunk[] chunks = new Chunk[16];
|
||||
int chunkCount = 0;
|
||||
for (int pass = 0; pass < 4; pass++) {
|
||||
if(pass == 1){
|
||||
if(chunkCount == 0) return null;
|
||||
}
|
||||
for (int ind = 0; ind < 16; ind++) {
|
||||
if ((bitmask & 1 << ind) != 0) {
|
||||
if (pass == 0) {
|
||||
chunkCount++;
|
||||
// Block length + Blocklight length
|
||||
expected += (4096 * 2) + 2048;
|
||||
}
|
||||
|
||||
if (pass == 1) {
|
||||
chunks[ind] = new Chunk(sky || hasSkyLight);
|
||||
buf.position(pos / 2);
|
||||
int buffPos = buf.position();
|
||||
// convert short array to new one
|
||||
|
||||
for (int index = 0; index < 4096; index++) {
|
||||
short ss = buf.get(buffPos + index);
|
||||
// s is 16 bits, 12 bits id and 4 bits data
|
||||
int data = ss & 0xF;
|
||||
int id = (ss >> 4) << 4 | data;
|
||||
|
||||
int newCombined = id; // test
|
||||
|
||||
chunks[ind].getBlocks().set(index, newCombined);
|
||||
}
|
||||
pos += 4096 * 2;
|
||||
|
||||
}
|
||||
|
||||
if (pass == 2) {
|
||||
NibbleArray3d blocklight = chunks[ind].getBlockLight();
|
||||
System.arraycopy(input, pos, blocklight.getData(), 0, blocklight.getData().length);
|
||||
pos += blocklight.getData().length;
|
||||
}
|
||||
|
||||
if (pass == 3) {
|
||||
if (sky) {
|
||||
NibbleArray3d skylight = chunks[ind].getSkyLight();
|
||||
System.arraycopy(input, pos, skylight.getData(), 0, skylight.getData().length);
|
||||
pos += skylight.getData().length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pass == 0) {
|
||||
// If we have more data than blocks and blocklight combined, there must be skylight data as well.
|
||||
if (input.length > expected) {
|
||||
sky = checkForSky;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte biomeData[] = null;
|
||||
if (isFullChunk && (pos + 256 <= input.length)) {
|
||||
|
||||
biomeData = new byte[256];
|
||||
System.arraycopy(input, pos, biomeData, 0, biomeData.length);
|
||||
}
|
||||
|
||||
Column column = new Column(x, z, chunks, biomeData);
|
||||
return column;
|
||||
}
|
||||
}
|
@ -6,9 +6,6 @@ import org.bukkit.entity.EntityType;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.spacehq.mc.protocol.data.game.chunk.Column;
|
||||
import org.spacehq.mc.protocol.util.NetUtil;
|
||||
import org.spacehq.opennbt.tag.builtin.ByteTag;
|
||||
import org.spacehq.opennbt.tag.builtin.CompoundTag;
|
||||
import org.spacehq.opennbt.tag.builtin.StringTag;
|
||||
import us.myles.ViaVersion.CancelException;
|
||||
@ -29,7 +26,6 @@ import us.myles.ViaVersion.slot.ItemSlotRewriter;
|
||||
import us.myles.ViaVersion.sounds.SoundEffect;
|
||||
import us.myles.ViaVersion.util.EntityUtil;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
@ -768,7 +764,7 @@ public class OutgoingTransformer {
|
||||
// Read chunk
|
||||
ChunkManager chunkManager = info.getChunkManager();
|
||||
Chunk chunk = chunkManager.readChunk(input);
|
||||
if(chunk == null) {
|
||||
if (chunk == null) {
|
||||
throw new CancelException();
|
||||
}
|
||||
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren