Progress on processing chunks on most versions, still need to test lower than 1.16.3 and 1.17+;

Flying packet mappings corrected;
This commit is contained in:
retrooper committed 2021-10-27 10:54:47 +02:00
1 parent c6442cdab8
commit 348acdedcc
33 files changed
+1246 -42

No files matched your search

@@ -0,0 +1,26 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.blockstate;
public interface BaseBlockState {
//TODO Make some item type abstraction
int getID();
int getCombinedID();
}
@@ -0,0 +1,57 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.blockstate;
public class FlatBlockState implements BaseBlockState {
//TODO Check https://github.com/MWHunter/Grim/blob/4b037ddbba1348c3383caf80c9b94a6e9489cfd4/src/main/java/ac/grim/grimac/utils/latency/CompensatedWorldFlat.java#L33
private int blockData;
private int globalID;
public FlatBlockState(int globalID) {
//this.blockData = CompensatedWorldFlat.globalPaletteToBlockData.get(globalID);
this.globalID = globalID;
}
@Override
public int getID() {
return -1;
//TODO Implement return blockData.getMaterial() //.getID();
}
public int getCombinedID() {
return globalID;
}
/*
public BlockData getBlockData() {
return blockData;
}
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof FlatBlockState)) return false;
FlatBlockState that = (FlatBlockState) o;
return getCombinedID() == that.getCombinedID();
}
}
@@ -0,0 +1,58 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.blockstate;
public class MagicBlockState implements BaseBlockState {
private final int id;
private final int data;
public MagicBlockState(int combinedID) {
this.id = combinedID & 0xFF;
this.data = combinedID >> 12;
}
public MagicBlockState(int id, int data) {
this.id = id;
this.data = data;
}
@Override
public int getID() {
return id;
}
@Override
public int getCombinedID() {
return id + (data << 12);
}
public int getBlockData() {
return data;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MagicBlockState)) return false;
MagicBlockState that = (MagicBlockState) o;
return this.id == that.getID() &&
this.data == that.getBlockData();
}
}
@@ -0,0 +1,29 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.chunk;
import com.github.retrooper.packetevents.protocol.data.world.blockstate.BaseBlockState;
public interface BaseChunk {
BaseBlockState get(int x, int y, int z);
void set(int x, int y, int z, int combinedID);
boolean isKnownEmpty();
}
@@ -0,0 +1,49 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.chunk;
import java.util.Arrays;
public class ByteArray3d {
private final byte[] data;
public ByteArray3d(int size) {
this.data = new byte[size];
}
public ByteArray3d(byte[] array) {
this.data = array;
}
public byte[] getData() {
return this.data;
}
public int get(int x, int y, int z) {
return this.data[y << 8 | z << 4 | x] & 0xFF;
}
public void set(int x, int y, int z, int val) {
this.data[y << 8 | z << 4 | x] = (byte) val;
}
public void fill(int val) {
Arrays.fill(this.data, (byte) val);
}
}
@@ -30,6 +30,7 @@ import com.github.retrooper.packetevents.protocol.data.world.chunk.palette.MapPa
import com.github.retrooper.packetevents.protocol.data.world.chunk.palette.Palette;
import com.github.retrooper.packetevents.protocol.data.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.data.stream.NetStreamOutput;
import com.github.retrooper.packetevents.protocol.data.world.chunk.storage.BitStorage;
import java.io.IOException;
@@ -32,14 +32,14 @@ public class Column {
private final int x;
private final int z;
private final boolean fullChunk;
private final Chunk[] chunks;
private final BaseChunk[] chunks;
private final NBTCompound[] tileEntities;
private final boolean hasHeightMaps;
private final NBTCompound heightMaps;
private final boolean hasBiomeData;
private final int[] biomeData;
public Column(int x, int z, boolean fullChunk, Chunk[] chunks, NBTCompound[] tileEntities, int[] biomeData) {
public Column(int x, int z, boolean fullChunk, BaseChunk[] chunks, NBTCompound[] tileEntities, int[] biomeData) {
this.x = x;
this.z = z;
this.fullChunk = fullChunk;
@@ -51,7 +51,7 @@ public class Column {
this.biomeData = biomeData != null ? Arrays.copyOf(biomeData, biomeData.length) : null;
}
public Column(int x, int z, boolean fullChunk, Chunk[] chunks, NBTCompound[] tileEntities) {
public Column(int x, int z, boolean fullChunk, BaseChunk[] chunks, NBTCompound[] tileEntities) {
this.x = x;
this.z = z;
this.fullChunk = fullChunk;
@@ -63,7 +63,7 @@ public class Column {
this.biomeData = new int[1024];
}
public Column(int x, int z, boolean fullChunk, Chunk[] chunks, NBTCompound[] tileEntities, NBTCompound heightMaps) {
public Column(int x, int z, boolean fullChunk, BaseChunk[] chunks, NBTCompound[] tileEntities, NBTCompound heightMaps) {
this.x = x;
this.z = z;
this.fullChunk = fullChunk;
@@ -75,7 +75,7 @@ public class Column {
this.biomeData = new int[1024];
}
public Column(int x, int z, boolean fullChunk, Chunk[] chunks, NBTCompound[] tileEntities, NBTCompound heightMaps, int[] biomeData) {
public Column(int x, int z, boolean fullChunk, BaseChunk[] chunks, NBTCompound[] tileEntities, NBTCompound heightMaps, int[] biomeData) {
this.x = x;
this.z = z;
this.fullChunk = fullChunk;
@@ -99,7 +99,7 @@ public class Column {
return fullChunk;
}
public Chunk[] getChunks() {
public BaseChunk[] getChunks() {
return chunks;
}
@@ -0,0 +1,100 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.chunk;
import java.util.Arrays;
public class LegacyFlexibleStorage {
private final long[] data;
private final int bitsPerEntry;
private final int size;
private final long maxEntryValue;
public LegacyFlexibleStorage(int bitsPerEntry, int size) {
this(bitsPerEntry, new long[roundToNearest(size * bitsPerEntry, 64) / 64]);
}
public LegacyFlexibleStorage(int bitsPerEntry, long[] data) {
if (bitsPerEntry < 4) {
bitsPerEntry = 4;
}
this.bitsPerEntry = bitsPerEntry;
this.data = Arrays.copyOf(data, data.length);
this.size = this.data.length * 64 / this.bitsPerEntry;
this.maxEntryValue = (1L << this.bitsPerEntry) - 1;
}
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;
}
}
public int get(int index) {
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) {
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;
}
}
public long[] getData() {
return data;
}
public int getBitsPerEntry() {
return bitsPerEntry;
}
public int getSize() {
return size;
}
public long getMaxEntryValue() {
return maxEntryValue;
}
}
@@ -0,0 +1,170 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.chunk.impl.v1_12;
import com.github.retrooper.packetevents.protocol.data.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.data.stream.NetStreamOutput;
import com.github.retrooper.packetevents.protocol.data.world.blockstate.MagicBlockState;
import com.github.retrooper.packetevents.protocol.data.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.data.world.chunk.LegacyFlexibleStorage;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Chunk_v1_12 implements BaseChunk {
private static final MagicBlockState AIR = new MagicBlockState(0, 0);
private final List<MagicBlockState> states;
private int bitsPerEntry;
private LegacyFlexibleStorage storage;
public Chunk_v1_12(NetStreamInput in) {
this.bitsPerEntry = in.readUnsignedByte();
this.states = new ArrayList<>();
int stateCount = in.readVarInt();
for (int i = 0; i < stateCount; i++) {
this.states.add(readBlockState(in));
}
this.storage = new LegacyFlexibleStorage(this.bitsPerEntry, in.readLongs(in.readVarInt()));
}
public Chunk_v1_12(ShortBuffer in) {
Map<Integer, Integer> reversePalette = new HashMap<>(32);
states = new ArrayList<>();
states.add(AIR);
reversePalette.put(0, 0);
this.bitsPerEntry = 4;
this.storage = new LegacyFlexibleStorage(bitsPerEntry, 4096);
int lastNext = -1;
int lastID = -1;
for (int i = 0; i < 4096; i++) {
int next = in.get();
if (next != lastNext) {
lastNext = next;
next = ((next & 15) << 12) | (next >> 4);
lastID = this.bitsPerEntry <= 8 ? reversePalette.getOrDefault(next, -1) : next;
if (lastID == -1) {
reversePalette.put(next, reversePalette.size());
states.add(new MagicBlockState(next));
if (reversePalette.size() > 1 << this.bitsPerEntry) {
this.bitsPerEntry++;
List<MagicBlockState> oldStates = this.states;
if (this.bitsPerEntry > 8) {
oldStates = new ArrayList<>(this.states);
this.states.clear();
reversePalette.clear();
this.bitsPerEntry = 16;
}
LegacyFlexibleStorage oldStorage = this.storage;
this.storage = new LegacyFlexibleStorage(this.bitsPerEntry, this.storage.getSize());
for (int index = 0; index < this.storage.getSize(); index++) {
this.storage.set(index, this.bitsPerEntry <= 8 ? oldStorage.get(index) : oldStates.get(oldStorage.get(index)).getCombinedID());
}
}
lastID = this.bitsPerEntry <= 8 ? reversePalette.getOrDefault(next, -1) : next;
}
}
this.storage.set(i, lastID);
}
}
public Chunk_v1_12() {
this.bitsPerEntry = 4;
this.states = new ArrayList<>();
this.states.add(AIR);
this.storage = new LegacyFlexibleStorage(this.bitsPerEntry, 4096);
}
private static int index(int x, int y, int z) {
return y << 8 | z << 4 | x;
}
private static MagicBlockState rawToState(int raw) {
return new MagicBlockState(raw & 0xFF, raw >> 12);
}
public static MagicBlockState readBlockState(NetStreamInput in) {
int rawId = in.readVarInt();
return new MagicBlockState(rawId >> 4, rawId & 0xF);
}
public static void writeBlockState(NetStreamOutput out, MagicBlockState blockState) {
out.writeVarInt((blockState.getID() << 4) | (blockState.getBlockData() & 0xF));
}
public MagicBlockState 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) : AIR) : rawToState(id);
}
// This method only works post-flattening
// This is due to the palette system
@Override
public boolean isKnownEmpty() {
return false;
}
public void set(int x, int y, int z, int combinedID) {
set(x, y, z, new MagicBlockState(combinedID));
}
public void set(int x, int y, int z, MagicBlockState state) {
int id = this.bitsPerEntry <= 8 ? this.states.indexOf(state) : state.getCombinedID();
if (id == -1) {
this.states.add(state);
if (this.states.size() > 1 << this.bitsPerEntry) {
this.bitsPerEntry++;
List<MagicBlockState> oldStates = this.states;
if (this.bitsPerEntry > 8) {
oldStates = new ArrayList<>(this.states);
this.states.clear();
this.bitsPerEntry = 16;
}
LegacyFlexibleStorage oldStorage = this.storage;
this.storage = new LegacyFlexibleStorage(this.bitsPerEntry, this.storage.getSize());
for (int index = 0; index < this.storage.getSize(); index++) {
this.storage.set(index, this.bitsPerEntry <= 8 ? oldStorage.get(index) : oldStates.get(oldStorage.get(index)).getCombinedID());
}
}
id = this.bitsPerEntry <= 8 ? this.states.indexOf(state) : state.getCombinedID();
}
this.storage.set(index(x, y, z), id);
}
}
@@ -0,0 +1,42 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.chunk.impl.v1_15;
import com.github.retrooper.packetevents.protocol.data.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.data.stream.NetStreamOutput;
public class BlockState {
private final int id;
public BlockState(int id) {
this.id = id;
}
public static BlockState read(NetStreamInput in) {
return new BlockState(in.readVarInt());
}
public static void write(NetStreamOutput out, BlockState blockState) {
out.writeVarInt(blockState.getID());
}
public int getID() {
return id;
}
}
@@ -0,0 +1,188 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.chunk.impl.v1_15;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.data.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.data.stream.NetStreamOutput;
import com.github.retrooper.packetevents.protocol.data.world.blockstate.BaseBlockState;
import com.github.retrooper.packetevents.protocol.data.world.blockstate.FlatBlockState;
import com.github.retrooper.packetevents.protocol.data.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.data.world.chunk.LegacyFlexibleStorage;
import java.util.ArrayList;
import java.util.List;
public class Chunk_v1_15 implements BaseChunk {
private static final BlockState AIR = new BlockState(0);
private static final int AIR_ID = 0;
private int blockCount;
private int bitsPerEntry;
private List<BlockState> states;
private LegacyFlexibleStorage storage;
public Chunk_v1_15() {
this.bitsPerEntry = 4;
this.states = new ArrayList<>();
this.states.add(AIR);
this.storage = new LegacyFlexibleStorage(this.bitsPerEntry, 4096);
}
public Chunk_v1_15(int blockCount, int bitsPerEntry, List<BlockState> states, LegacyFlexibleStorage storage) {
this.blockCount = blockCount;
this.bitsPerEntry = bitsPerEntry;
this.states = states;
this.storage = storage;
}
public static Chunk_v1_15 read(NetStreamInput in) {
int blockCount = 0;
// 1.14 and 1.15 include block count in chunk data
// In 1.13 we don't send that, so there is no need to keep track of it
//TODO Confirm if 1.13.2 counts too
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.v_1_14)) {
blockCount = in.readShort();
}
int bitsPerEntry = in.readUnsignedByte();
List<BlockState> states = new ArrayList<>();
int stateCount = bitsPerEntry > 8 ? 0 : in.readVarInt();
for (int i = 0; i < stateCount; i++) {
states.add(BlockState.read(in));
}
LegacyFlexibleStorage storage = new LegacyFlexibleStorage(bitsPerEntry, in.readLongs(in.readVarInt()));
return new Chunk_v1_15(blockCount, bitsPerEntry, states, storage);
}
public static void write(NetStreamOutput out, Chunk_v1_15 chunk) {
// ViaVersion should handle not writing block count in 1.13, as vanilla doesn't include it
// It would probably crash the client if we tried writing it
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.v_1_14)) {
out.writeShort(chunk.getBlockCount());
}
out.writeByte(chunk.getBitsPerEntry());
if (chunk.getBitsPerEntry() <= 8) {
out.writeVarInt(chunk.getStates().size());
for (BlockState state : chunk.getStates()) {
BlockState.write(out, state);
}
}
long[] data = chunk.getStorage().getData();
out.writeVarInt(data.length);
out.writeLongs(data);
}
private static int index(int x, int y, int z) {
return y << 8 | z << 4 | x;
}
public BaseBlockState get(int x, int y, int z) {
return new FlatBlockState(getInt(x, y, z));
}
public int getInt(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).getID() : AIR_ID) : id;
}
public void set(int x, int y, int z, int state) {
set(x, y, z, new BlockState(state));
}
public void set(int x, int y, int z, BlockState state) {
int id = this.bitsPerEntry <= 8 ? this.states.indexOf(state) : state.getID();
if (id == -1) {
this.states.add(state);
if (this.states.size() > 1 << this.bitsPerEntry) {
this.bitsPerEntry++;
List<BlockState> oldStates = this.states;
if (this.bitsPerEntry > 8) {
oldStates = new ArrayList<>(this.states);
this.states.clear();
this.bitsPerEntry = 13;
}
LegacyFlexibleStorage oldStorage = this.storage;
this.storage = new LegacyFlexibleStorage(this.bitsPerEntry, this.storage.getSize());
for (int index = 0; index < this.storage.getSize(); index++) {
this.storage.set(index, this.bitsPerEntry <= 8 ? oldStorage.get(index) : oldStates.get(oldStorage.get(index)).getID());
}
}
id = this.bitsPerEntry <= 8 ? this.states.indexOf(state) : state.getID();
}
int ind = index(x, y, z);
int curr = this.storage.get(ind);
if (state.getID() != AIR.getID() && curr == AIR.getID()) {
this.blockCount++;
} else if (state.getID() == AIR.getID() && curr != AIR.getID()) {
this.blockCount--;
}
this.storage.set(ind, id);
}
public boolean isKnownEmpty() {
return blockCount == 0 && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.v_1_14);
}
public int getBlockCount() {
return blockCount;
}
public void setBlockCount(int blockCount) {
this.blockCount = blockCount;
}
public int getBitsPerEntry() {
return bitsPerEntry;
}
public void setBitsPerEntry(int bitsPerEntry) {
this.bitsPerEntry = bitsPerEntry;
}
public List<BlockState> getStates() {
return states;
}
public void setStates(List<BlockState> states) {
this.states = states;
}
public LegacyFlexibleStorage getStorage() {
return storage;
}
public void setStorage(LegacyFlexibleStorage storage) {
this.storage = storage;
}
}
@@ -0,0 +1,151 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.chunk.impl.v1_16;
import com.github.retrooper.packetevents.protocol.data.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.data.stream.NetStreamOutput;
import com.github.retrooper.packetevents.protocol.data.world.blockstate.BaseBlockState;
import com.github.retrooper.packetevents.protocol.data.world.blockstate.FlatBlockState;
import com.github.retrooper.packetevents.protocol.data.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.data.world.chunk.palette.GlobalPalette;
import com.github.retrooper.packetevents.protocol.data.world.chunk.palette.ListPalette;
import com.github.retrooper.packetevents.protocol.data.world.chunk.palette.MapPalette;
import com.github.retrooper.packetevents.protocol.data.world.chunk.palette.Palette;
import com.github.retrooper.packetevents.protocol.data.world.chunk.storage.BitStorage;
public class Chunk_v1_16 implements BaseChunk {
private static final int CHUNK_SIZE = 4096;
private static final int MIN_PALETTE_BITS_PER_ENTRY = 4;
private static final int MAX_PALETTE_BITS_PER_ENTRY = 8;
private static final int GLOBAL_PALETTE_BITS_PER_ENTRY = 14;
private static final int AIR = 0;
private int blockCount;
private Palette palette;
private BitStorage storage;
public Chunk_v1_16() {
this(0, new ListPalette(4), new BitStorage(4, 4096));
}
public Chunk_v1_16(int blockCount, Palette palette, BitStorage storage) {
this.blockCount = blockCount;
this.palette = palette;
this.storage = storage;
}
public static Chunk_v1_16 read(NetStreamInput in) {
int blockCount = in.readShort();
int bitsPerEntry = in.readUnsignedByte();
Palette palette = readPalette(bitsPerEntry, in);
BitStorage storage = new BitStorage(bitsPerEntry, 4096, in.readLongs(in.readVarInt()));
return new Chunk_v1_16(blockCount, palette, storage);
}
public static void write(NetStreamOutput out, Chunk_v1_16 chunk) {
out.writeShort(chunk.blockCount);
out.writeByte(chunk.storage.getBitsPerEntry());
if (!(chunk.palette instanceof GlobalPalette)) {
int paletteLength = chunk.palette.size();
out.writeVarInt(paletteLength);
for (int i = 0; i < paletteLength; ++i) {
out.writeVarInt(chunk.palette.idToState(i));
}
}
long[] data = chunk.storage.getData();
out.writeVarInt(data.length);
out.writeLongs(data);
}
private static Palette createPalette(int bitsPerEntry) {
if (bitsPerEntry <= 4) {
return new ListPalette(bitsPerEntry);
} else {
return bitsPerEntry <= 8 ? new MapPalette(bitsPerEntry) : new GlobalPalette();
}
}
private static Palette readPalette(int bitsPerEntry, NetStreamInput in) {
if (bitsPerEntry <= 4) {
return new ListPalette(bitsPerEntry, in);
} else {
return bitsPerEntry <= 8 ? new MapPalette(bitsPerEntry, in) : new GlobalPalette();
}
}
private static int index(int x, int y, int z) {
return y << 8 | z << 4 | x;
}
public BaseBlockState get(int x, int y, int z) {
int id = this.storage.get(index(x, y, z));
return new FlatBlockState(this.palette.idToState(id));
}
public void set(int x, int y, int z, int state) {
int id = this.palette.stateToId(state);
if (id == -1) {
this.resizePalette();
id = this.palette.stateToId(state);
}
int index = index(x, y, z);
int curr = this.storage.get(index);
if (state != 0 && curr == 0) {
++this.blockCount;
} else if (state == 0 && curr != 0) {
--this.blockCount;
}
this.storage.set(index, id);
}
public boolean isKnownEmpty() {
return this.blockCount == 0;
}
private int sanitizeBitsPerEntry(int bitsPerEntry) {
return bitsPerEntry <= 8 ? Math.max(4, bitsPerEntry) : 14;
}
private void resizePalette() {
Palette oldPalette = this.palette;
BitStorage oldData = this.storage;
int bitsPerEntry = this.sanitizeBitsPerEntry(oldData.getBitsPerEntry() + 1);
this.palette = createPalette(bitsPerEntry);
this.storage = new BitStorage(bitsPerEntry, 4096);
for (int i = 0; i < 4096; ++i) {
this.storage.set(i, this.palette.stateToId(oldPalette.idToState(oldData.get(i))));
}
}
public int getBlockCount() {
return this.blockCount;
}
public Palette getPalette() {
return this.palette;
}
public BitStorage getStorage() {
return this.storage;
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.chunk.impl.v1_7;
import com.github.retrooper.packetevents.protocol.data.world.blockstate.BaseBlockState;
import com.github.retrooper.packetevents.protocol.data.world.blockstate.MagicBlockState;
import com.github.retrooper.packetevents.protocol.data.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.data.world.chunk.ByteArray3d;
import com.github.retrooper.packetevents.protocol.data.world.chunk.NibbleArray3d;
import com.github.retrooper.packetevents.util.Vector3i;
public class Chunk_v1_7 implements BaseChunk {
private final ByteArray3d blocks;
private final NibbleArray3d extendedBlocks;
public Chunk_v1_7() {
blocks = new ByteArray3d(4096);
extendedBlocks = new NibbleArray3d(4096);
}
@Override
public BaseBlockState get(int x, int y, int z) {
return new MagicBlockState(blocks.get(x, y, z), extendedBlocks.get(x, y, z));
}
@Override
public void set(int x, int y, int z, int combinedID) {
blocks.set(x, y, z, combinedID & 0xFF);
extendedBlocks.set(x, y, z, combinedID >> 12);
}
@Override
public boolean isKnownEmpty() {
return false;
}
public ByteArray3d getBlocks() {
return blocks;
}
public NibbleArray3d getMetadata() {
return extendedBlocks;
}
}
@@ -44,7 +44,7 @@ public class ListPalette implements Palette {
this.data = new int[this.maxId + 1];
}
public ListPalette(int bitsPerEntry, NetStreamInput in) throws IOException {
public ListPalette(int bitsPerEntry, NetStreamInput in) {
this(bitsPerEntry);
int paletteLength = in.readVarInt();
@@ -0,0 +1,27 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.chunk.reader;
import com.github.retrooper.packetevents.protocol.data.world.chunk.BaseChunk;
import java.util.BitSet;
public interface ChunkReader {
BaseChunk[] read(BitSet set, int chunkSize, byte[] data);
}
@@ -0,0 +1,41 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.chunk.reader.impl;
import com.github.retrooper.packetevents.protocol.data.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.data.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.data.world.chunk.impl.v1_16.Chunk_v1_16;
import com.github.retrooper.packetevents.protocol.data.world.chunk.reader.ChunkReader;
import java.io.ByteArrayInputStream;
import java.util.BitSet;
public class ChunkReader_v1_16 implements ChunkReader {
@Override
public BaseChunk[] read(BitSet set, int chunkSize, byte[] data) {
NetStreamInput dataIn = new NetStreamInput(new ByteArrayInputStream(data));
BaseChunk[] chunks = new BaseChunk[chunkSize];
for (int index = 0; index < chunks.length; ++index) {
if (set.get(index)) {
chunks[index] = Chunk_v1_16.read(dataIn);
}
}
return chunks;
}
}
@@ -0,0 +1,54 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.chunk.reader.impl;
import com.github.retrooper.packetevents.protocol.data.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.data.world.chunk.ByteArray3d;
import com.github.retrooper.packetevents.protocol.data.world.chunk.NibbleArray3d;
import com.github.retrooper.packetevents.protocol.data.world.chunk.impl.v1_7.Chunk_v1_7;
import com.github.retrooper.packetevents.protocol.data.world.chunk.reader.ChunkReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.BitSet;
public class ChunkReader_v1_7 implements ChunkReader {
@Override
public BaseChunk[] read(BitSet primarySet, int chunkSize, byte[] data) {
Chunk_v1_7[] chunks = new Chunk_v1_7[chunkSize];
ByteBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
for (int pass = 1; pass < 3; pass++) {
for (int ind = 0; ind < 16; ind++) {
if (primarySet.get(ind)) {
if (pass == 1) {
chunks[ind] = new Chunk_v1_7();
ByteArray3d blocks = chunks[ind].getBlocks();
buf.get(blocks.getData(), 0, blocks.getData().length);
}
if (pass == 2) {
NibbleArray3d metadata = chunks[ind].getMetadata();
buf.get(metadata.getData(), 0, metadata.getData().length);
}
}
}
}
return chunks;
}
}
@@ -0,0 +1,43 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.chunk.reader.impl;
import com.github.retrooper.packetevents.protocol.data.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.data.world.chunk.impl.v1_12.Chunk_v1_12;
import com.github.retrooper.packetevents.protocol.data.world.chunk.reader.ChunkReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.BitSet;
public class ChunkReader_v1_8 implements ChunkReader {
@Override
public BaseChunk[] read(BitSet primarySet, int chunkSize, byte[] data) {
BaseChunk[] chunks = new BaseChunk[chunkSize];
ShortBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
for (int ind = 0; ind < 16; ind++) {
if (primarySet.get(ind)) {
Chunk_v1_12 compressed = new Chunk_v1_12(buf);
chunks[ind] = compressed;
}
}
return chunks;
}
}
@@ -0,0 +1,64 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.data.world.chunk.reader.impl;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.data.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.data.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.data.world.chunk.impl.v1_12.Chunk_v1_12;
import com.github.retrooper.packetevents.protocol.data.world.chunk.impl.v1_15.Chunk_v1_15;
import com.github.retrooper.packetevents.protocol.data.world.chunk.reader.ChunkReader;
import java.io.ByteArrayInputStream;
import java.util.BitSet;
public class ChunkReader_v1_9 implements ChunkReader {
private static boolean V_1_13_OR_NEWER;
private static boolean OLDER_THAN_V_1_14;
private static boolean CHECKED_VERSION;
@Override
public BaseChunk[] read(BitSet set, int chunkSize, byte[] data) {
if (!CHECKED_VERSION) {
V_1_13_OR_NEWER = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.v_1_13);
OLDER_THAN_V_1_14 = PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.v_1_14);
CHECKED_VERSION = true;
}
boolean isFlattened = V_1_13_OR_NEWER;
NetStreamInput dataIn = new NetStreamInput(new ByteArrayInputStream(data));
BaseChunk[] chunks = new BaseChunk[chunkSize];
for (int index = 0; index < chunks.length; ++index) {
if (set.get(index)) {
chunks[index] = isFlattened ? Chunk_v1_15.read(dataIn) : new Chunk_v1_12(dataIn);
// Advance the data past the blocklight and skylight bytes
if (OLDER_THAN_V_1_14) {
dataIn.readBytes(4096);
}
}
}
return chunks;
}
}
@@ -22,7 +22,7 @@
* https://github.com/Steveice10/MCProtocolLib
*/
package com.github.retrooper.packetevents.protocol.data.world.chunk;
package com.github.retrooper.packetevents.protocol.data.world.chunk.storage;
//TODO Equals & hashcode
public class BitStorage {
@@ -32,7 +32,7 @@ public enum ServerboundPacketType_1_12 {
PLUGIN_MESSAGE,
INTERACT_ENTITY,
KEEP_ALIVE,
FLYING,
PLAYER_FLYING,
PLAYER_POSITION,
PLAYER_POSITION_AND_ROTATION,
PLAYER_ROTATION,
@@ -31,7 +31,7 @@ public enum ServerboundPacketType_1_12_1 {
PLUGIN_MESSAGE,
INTERACT_ENTITY,
KEEP_ALIVE,
FLYING,
PLAYER_FLYING,
PLAYER_POSITION,
PLAYER_POSITION_AND_ROTATION,
PLAYER_ROTATION,
@@ -34,7 +34,7 @@ public enum ServerboundPacketType_1_13 {
QUERY_ENTITY_NBT,
INTERACT_ENTITY,
KEEP_ALIVE,
FLYING,
PLAYER_FLYING,
PLAYER_POSITION,
PLAYER_POSITION_AND_ROTATION,
PLAYER_ROTATION,
@@ -39,7 +39,7 @@ public enum ServerboundPacketType_1_14 {
PLAYER_POSITION,
PLAYER_POSITION_AND_ROTATION,
PLAYER_ROTATION,
FLYING,
PLAYER_FLYING,
VEHICLE_MOVE,
STEER_BOAT,
PICK_ITEM,
@@ -39,7 +39,7 @@ public enum ServerboundPacketType_1_15_2 {
PLAYER_POSITION,
PLAYER_POSITION_AND_ROTATION,
PLAYER_ROTATION,
FLYING,
PLAYER_FLYING,
VEHICLE_MOVE,
STEER_BOAT,
PICK_ITEM,
@@ -40,7 +40,7 @@ public enum ServerboundPacketType_1_16 {
PLAYER_POSITION,
PLAYER_POSITION_AND_ROTATION,
PLAYER_ROTATION,
FLYING,
PLAYER_FLYING,
VEHICLE_MOVE,
STEER_BOAT,
PICK_ITEM,
@@ -40,7 +40,7 @@ public enum ServerboundPacketType_1_16_2 {
PLAYER_POSITION,
PLAYER_POSITION_AND_ROTATION,
PLAYER_ROTATION,
FLYING,
PLAYER_FLYING,
VEHICLE_MOVE,
STEER_BOAT,
PICK_ITEM,
@@ -39,7 +39,7 @@ public enum ServerboundPacketType_1_17 {
PLAYER_POSITION,
PLAYER_POSITION_AND_ROTATION,
PLAYER_ROTATION,
FLYING,
PLAYER_FLYING,
VEHICLE_MOVE,
STEER_BOAT,
PICK_ITEM,
@@ -22,7 +22,7 @@ public enum ServerboundPacketType_1_7_10 {
KEEP_ALIVE,
CHAT_MESSAGE,
INTERACT_ENTITY,
FLYING,
PLAYER_FLYING,
PLAYER_POSITION,
PLAYER_ROTATION,
PLAYER_POSITION_AND_ROTATION,
@@ -22,7 +22,7 @@ public enum ServerboundPacketType_1_8 {
KEEP_ALIVE,
CHAT_MESSAGE,
INTERACT_ENTITY,
FLYING,
PLAYER_FLYING,
PLAYER_POSITION,
PLAYER_ROTATION,
PLAYER_POSITION_AND_ROTATION,
@@ -34,7 +34,7 @@ public enum ServerboundPacketType_1_9 {
PLAYER_POSITION,
PLAYER_POSITION_AND_ROTATION,
PLAYER_ROTATION,
FLYING,
PLAYER_FLYING,
VEHICLE_MOVE,
STEER_BOAT,
PLAYER_ABILITIES,
@@ -20,12 +20,20 @@ package com.github.retrooper.packetevents.wrapper.play.server;
import com.github.retrooper.packetevents.event.impl.PacketSendEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.data.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.data.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.data.stream.NetStreamOutput;
import com.github.retrooper.packetevents.protocol.data.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.data.world.chunk.Chunk;
import com.github.retrooper.packetevents.protocol.data.world.chunk.Column;
import com.github.retrooper.packetevents.protocol.data.world.chunk.impl.v1_15.Chunk_v1_15;
import com.github.retrooper.packetevents.protocol.data.world.chunk.impl.v1_16.Chunk_v1_16;
import com.github.retrooper.packetevents.protocol.data.world.chunk.reader.ChunkReader;
import com.github.retrooper.packetevents.protocol.data.world.chunk.reader.impl.ChunkReader_v1_16;
import com.github.retrooper.packetevents.protocol.data.world.chunk.reader.impl.ChunkReader_v1_7;
import com.github.retrooper.packetevents.protocol.data.world.chunk.reader.impl.ChunkReader_v1_8;
import com.github.retrooper.packetevents.protocol.data.world.chunk.reader.impl.ChunkReader_v1_9;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import java.io.ByteArrayInputStream;
@@ -34,7 +42,13 @@ import java.util.BitSet;
//TODO Finish
public class WrapperPlayServerChunkData extends PacketWrapper<WrapperPlayServerChunkData> {
private static ChunkReader_v1_7 chunkReader_v1_7 = new ChunkReader_v1_7();
private static ChunkReader_v1_8 chunkReader_v1_8 = new ChunkReader_v1_8();
private static ChunkReader_v1_9 chunkReader_v1_9 = new ChunkReader_v1_9();
private static ChunkReader_v1_16 chunkReader_v1_16 = new ChunkReader_v1_16();
private Column column;
//TODO Make accessible??
private boolean ignoreOldData;
public WrapperPlayServerChunkData(PacketSendEvent event) {
super(event);
@@ -97,6 +111,7 @@ public class WrapperPlayServerChunkData extends PacketWrapper<WrapperPlayServerC
int chunkZ = readInt();
boolean v1_17 = serverVersion.isNewerThanOrEquals(ServerVersion.v_1_17);
boolean v1_16_2 = serverVersion.isNewerThanOrEquals(ServerVersion.v_1_16_2);
boolean v1_13_2 = serverVersion.isNewerThanOrEquals(ServerVersion.v_1_13_2);
boolean fullChunk = readBoolean();
@@ -114,7 +129,6 @@ public class WrapperPlayServerChunkData extends PacketWrapper<WrapperPlayServerC
}
boolean ignoreOldData = false;
if (serverVersion == ServerVersion.v_1_16 ||
serverVersion == ServerVersion.v_1_16_1) {
ignoreOldData = readBoolean();
@@ -130,14 +144,14 @@ public class WrapperPlayServerChunkData extends PacketWrapper<WrapperPlayServerC
int[] biomeData = new int[0];
if (hasBiomeData && v1_13_2 && serverVersion.isNewerThanOrEquals(ServerVersion.v_1_15)) {
int biomesLength;
if (serverVersion.isNewerThanOrEquals(ServerVersion.v_1_16_2)) {
if (v1_16_2) {
biomesLength = readVarInt();
} else {
biomesLength = 1024;//Always 1024
}
biomeData = new int[biomesLength];
for (int index = 0; index < biomeData.length; index++) {
if (v1_17) {
if (v1_16_2) {
biomeData[index] = readVarInt();
} else {
biomeData[index] = readInt();
@@ -146,17 +160,9 @@ public class WrapperPlayServerChunkData extends PacketWrapper<WrapperPlayServerC
hasReadBiomeData = true;
}
//TODO Decompress on 1.7.10
//https://github.com/retrooper/packetevents/blob/794ad6b042c1c89a931d322f4f83317b573e891a/src/main/java/io/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerChunkData.java
int dataLength = readVarInt();
byte[] data = readByteArray(dataLength);
NetStreamInput dataIn = new NetStreamInput(new ByteArrayInputStream(data));
Chunk[] chunks = new Chunk[chunkMask.size()];
for (int index = 0; index < chunks.length; index++) {
if (chunkMask.get(index)) {
chunks[index] = Chunk.read(dataIn);
}
}
BaseChunk[] chunks = getChunkReader().read(chunkMask, chunkMask.size(), data);
if (hasBiomeData && !hasReadBiomeData) {
byte[] biomeDataBytes = readByteArray(256);
@@ -213,7 +219,6 @@ public class WrapperPlayServerChunkData extends PacketWrapper<WrapperPlayServerC
}
boolean hasHeightMaps = serverVersion.isNewerThanOrEquals(ServerVersion.v_1_14);
NBTCompound heightMaps = null;
if (hasHeightMaps) {
writeNBTTab(column.getHeightMaps());
}
@@ -224,7 +229,6 @@ public class WrapperPlayServerChunkData extends PacketWrapper<WrapperPlayServerC
int biomesLength = biomeData.length;
if (serverVersion.isNewerThanOrEquals(ServerVersion.v_1_16_2)) {
writeVarInt(biomesLength);
biomesLength = readVarInt();
}
for (int index = 0; index < column.getBiomeData().length; index++) {
if (v1_17) {
@@ -243,12 +247,12 @@ public class WrapperPlayServerChunkData extends PacketWrapper<WrapperPlayServerC
NetStreamOutput dataOut = new NetStreamOutput(dataBytes);
BitSet chunkMask = new BitSet();
Chunk[] chunks = column.getChunks();
BaseChunk[] chunks = column.getChunks();
for (int index = 0; index < chunks.length; index++) {
Chunk chunk = chunks[index];
if (chunk != null && !chunk.isEmpty()) {
BaseChunk chunk = chunks[index];
if (chunk != null && !chunk.isKnownEmpty()) {
chunkMask.set(index);
Chunk.write(dataOut, chunk);
// Chunk_v1_15.write(dataOut, chunk);
}
}
@@ -281,4 +285,19 @@ public class WrapperPlayServerChunkData extends PacketWrapper<WrapperPlayServerC
public void setColumn(Column column) {
this.column = column;
}
private ChunkReader getChunkReader() {
if (serverVersion.isNewerThanOrEquals(ServerVersion.v_1_16)) {
return chunkReader_v1_16;
}
else if (serverVersion.isNewerThanOrEquals(ServerVersion.v_1_9)) {
return chunkReader_v1_9;
}
else if (serverVersion.isNewerThanOrEquals(ServerVersion.v_1_8)) {
return chunkReader_v1_8;
}
else {
return chunkReader_v1_7;
}
}
}
@@ -24,30 +24,55 @@ import com.github.retrooper.packetevents.event.impl.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.impl.PacketSendEvent;
import com.github.retrooper.packetevents.manager.player.PlayerManager;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import com.github.retrooper.packetevents.protocol.data.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.data.world.blockstate.BaseBlockState;
import com.github.retrooper.packetevents.protocol.data.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.data.world.chunk.Column;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.data.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.handshaking.client.WrapperHandshakingClientHandshake;
import com.github.retrooper.packetevents.wrapper.login.client.WrapperLoginClientLoginStart;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerChatMessage;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerChunkData;
import org.bukkit.entity.Player;
import java.util.Arrays;
public class InternalPacketListener implements PacketListener {
//Make this specific event be at MONITOR priority
@Override
public void onPacketSend(PacketSendEvent event) {
Player player = (Player) event.getPlayer();
if (event.getPacketType() == PacketType.Login.Server.LOGIN_SUCCESS) {
//Transition into the PLAY connection state
PacketEvents.getAPI().getPlayerManager().changeConnectionState(event.getChannel(), ConnectionState.PLAY);
} /*else if (event.getPacketType() == PacketType.Play.Server.CHUNK_DATA) {
} else if (event.getPacketType() == PacketType.Play.Server.CHUNK_DATA) {
WrapperPlayServerChunkData chunkData = new WrapperPlayServerChunkData(event);
Column column = chunkData.getColumn();
int x = column.getX();
int z = column.getZ();
NBTCompound heightMaps = column.getHeightMaps();
event.getPlayer().sendMessage("X: " + x + ", Z: " + z + ", BIOME DATA: " + Arrays.toString(column.getBiomeData()));
event.getPlayer().sendMessage("HEIGHT MAPS: " + heightMaps.getTagNames());
}*/
if (player != null) {
player.sendMessage("X: " + x + ", Z: " + z);
player.sendMessage("HEIGHT MAPS: " + heightMaps.getTagNames());
player.sendMessage("CHUNKS:");
//TODO Credit in all chunk related classes
for (BaseChunk chunk : column.getChunks()) {
try {
BaseBlockState state = chunk.get(column.getX(), player.getLocation().getBlockY(), column.getZ());
if (state != null) {
player.sendMessage("Jackpot!");
player.sendMessage("Block type: " + state.getCombinedID());
}
}
catch (Exception ignored) {
}
}
}
event.setLastUsedWrapper(null);
}
if (event.getPacketType() == PacketType.Play.Server.CHAT_MESSAGE) {
WrapperPlayServerChatMessage msg = new WrapperPlayServerChatMessage(event);
//System.out.println("msg: " + msg.getJSONMessage());