2022-08-07 00:52:42 +02:00
|
|
|
package com.comphenix.protocol.wrappers;
|
|
|
|
|
|
|
|
import com.comphenix.protocol.injector.StructureCache;
|
|
|
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
|
|
|
import com.comphenix.protocol.reflect.accessors.Accessors;
|
|
|
|
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
|
|
|
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
|
|
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
|
|
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
|
|
|
import com.comphenix.protocol.utility.ZeroBuffer;
|
|
|
|
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
|
|
|
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
|
|
|
import com.google.common.collect.Lists;
|
|
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
|
|
|
|
import java.lang.reflect.Field;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.BitSet;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrapper classes for ClientboundLevelChunkWithLightPacket
|
|
|
|
*
|
|
|
|
* @author Etrayed
|
|
|
|
*/
|
|
|
|
public final class WrappedLevelChunkData {
|
|
|
|
|
|
|
|
private WrappedLevelChunkData() {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrapper for ClientboundLevelChunkPacketData
|
|
|
|
*/
|
|
|
|
public static final class ChunkData extends AbstractWrapper {
|
|
|
|
|
|
|
|
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getLevelChunkPacketDataClass();
|
|
|
|
|
|
|
|
private static final ConstructorAccessor LEVEL_CHUNK_PACKET_DATA_CONSTRUCTOR;
|
|
|
|
|
|
|
|
private static final FieldAccessor BLOCK_ENTITIES_DATA_ACCESSOR;
|
|
|
|
private static final FieldAccessor HEIGHTMAPS_ACCESSOR;
|
|
|
|
private static final FieldAccessor BUFFER_ACCESSOR;
|
|
|
|
|
|
|
|
static {
|
|
|
|
FuzzyReflection reflection = FuzzyReflection.fromClass(HANDLE_TYPE, true);
|
|
|
|
|
|
|
|
LEVEL_CHUNK_PACKET_DATA_CONSTRUCTOR = Accessors.getConstructorAccessor(HANDLE_TYPE,
|
|
|
|
MinecraftReflection.getPacketDataSerializerClass(), int.class, int.class);
|
|
|
|
BLOCK_ENTITIES_DATA_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder()
|
|
|
|
.typeExact(List.class)
|
|
|
|
.build()));
|
|
|
|
HEIGHTMAPS_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder()
|
|
|
|
.typeExact(MinecraftReflection.getNBTCompoundClass())
|
|
|
|
.build()));
|
|
|
|
BUFFER_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder().typeExact(byte[].class).build()));
|
|
|
|
}
|
|
|
|
|
|
|
|
public ChunkData(Object handle) {
|
|
|
|
super(HANDLE_TYPE);
|
|
|
|
|
|
|
|
setHandle(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The heightmap of this chunk.
|
|
|
|
*
|
|
|
|
* @return an NBT-Tag
|
|
|
|
*/
|
|
|
|
public NbtCompound getHeightmapsTag() {
|
|
|
|
return NbtFactory.fromNMSCompound(HEIGHTMAPS_ACCESSOR.get(handle));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the heightmap tag of this chunk.
|
|
|
|
*
|
|
|
|
* @param heightmapsTag the new heightmaps tag.
|
|
|
|
*/
|
|
|
|
public void setHeightmapsTag(NbtCompound heightmapsTag) {
|
|
|
|
HEIGHTMAPS_ACCESSOR.set(handle, NbtFactory.fromBase(heightmapsTag).getHandle());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The actual structural data of this chunk as bytes.
|
|
|
|
*
|
|
|
|
* @return a byte array containing the chunks structural data.
|
|
|
|
*/
|
|
|
|
public byte[] getBuffer() {
|
|
|
|
return (byte[]) BUFFER_ACCESSOR.get(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the structural data of this chunk.
|
|
|
|
*
|
|
|
|
* @param buffer the new buffer.
|
|
|
|
*/
|
|
|
|
public void setBuffer(byte[] buffer) {
|
|
|
|
BUFFER_ACCESSOR.set(handle, buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* All block entities of this chunk. Supports removal and other edits.
|
|
|
|
*
|
|
|
|
* @return a mutable (remove only) list containing {@link BlockEntityInfo}
|
|
|
|
*/
|
|
|
|
public List<BlockEntityInfo> getBlockEntityInfo() {
|
|
|
|
//noinspection StaticPseudoFunctionalStyleMethod
|
|
|
|
return Lists.transform((List<?>) BLOCK_ENTITIES_DATA_ACCESSOR.get(handle), BlockEntityInfo::new);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the block entities of this chunk. Supports removal and other edits.
|
|
|
|
*
|
|
|
|
* @param blockEntityInfo the new list of block entities
|
|
|
|
*/
|
|
|
|
public void setBlockEntityInfo(List<BlockEntityInfo> blockEntityInfo) {
|
|
|
|
List handleList = new ArrayList<>(blockEntityInfo.size());
|
|
|
|
|
|
|
|
for (BlockEntityInfo info : blockEntityInfo) {
|
|
|
|
//noinspection unchecked
|
|
|
|
handleList.add(info.getHandle());
|
|
|
|
}
|
|
|
|
|
|
|
|
BLOCK_ENTITIES_DATA_ACCESSOR.set(handle, handleList);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new wrapper using predefined values.
|
|
|
|
*
|
|
|
|
* @param heightmapsTag the heightmaps tag
|
|
|
|
* @param buffer the buffer
|
|
|
|
* @param blockEntityInfo a list of wrapped block entities
|
|
|
|
* @return a newly created wrapper
|
|
|
|
*/
|
|
|
|
public static ChunkData fromValues(NbtCompound heightmapsTag, byte[] buffer, List<BlockEntityInfo> blockEntityInfo) {
|
2022-12-14 02:38:37 +01:00
|
|
|
ChunkData data = new ChunkData(LEVEL_CHUNK_PACKET_DATA_CONSTRUCTOR.invoke(StructureCache.newNullDataSerializer(), 0, 0));
|
2022-08-07 00:52:42 +02:00
|
|
|
|
|
|
|
data.setHeightmapsTag(heightmapsTag);
|
|
|
|
data.setBuffer(buffer);
|
|
|
|
data.setBlockEntityInfo(blockEntityInfo);
|
|
|
|
|
|
|
|
return new ChunkData(data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrapper for ClientboundLightUpdatePacketData
|
|
|
|
*/
|
|
|
|
public static class LightData extends AbstractWrapper {
|
|
|
|
|
|
|
|
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getLightUpdatePacketDataClass();
|
|
|
|
|
|
|
|
private static final ConstructorAccessor LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR;
|
|
|
|
|
|
|
|
private static final FieldAccessor[] BIT_SET_ACCESSORS;
|
|
|
|
private static final FieldAccessor[] BYTE_ARRAY_LIST_ACCESSORS;
|
|
|
|
|
|
|
|
private static final FieldAccessor TRUST_EDGES_ACCESSOR;
|
|
|
|
|
|
|
|
static {
|
|
|
|
FuzzyReflection reflection = FuzzyReflection.fromClass(HANDLE_TYPE, true);
|
|
|
|
|
|
|
|
LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR = Accessors.getConstructorAccessor(HANDLE_TYPE,
|
|
|
|
MinecraftReflection.getPacketDataSerializerClass(), int.class, int.class);
|
|
|
|
BIT_SET_ACCESSORS = Accessors.getFieldAccessorArray(HANDLE_TYPE, BitSet.class, true);
|
|
|
|
BYTE_ARRAY_LIST_ACCESSORS = Accessors.getFieldAccessorArray(HANDLE_TYPE, List.class, true);
|
|
|
|
TRUST_EDGES_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder()
|
|
|
|
.typeExact(boolean.class)
|
|
|
|
.build()));
|
|
|
|
}
|
|
|
|
|
|
|
|
public LightData(Object handle) {
|
|
|
|
super(HANDLE_TYPE);
|
|
|
|
|
|
|
|
setHandle(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The sky light mask.
|
|
|
|
*
|
|
|
|
* @return a {@link BitSet}
|
|
|
|
*/
|
|
|
|
public BitSet getSkyYMask() {
|
|
|
|
return (BitSet) BIT_SET_ACCESSORS[0].get(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the sky light mask
|
|
|
|
*
|
|
|
|
* @param skyYMask the new mask
|
|
|
|
*/
|
|
|
|
public void setSkyYMask(BitSet skyYMask) {
|
|
|
|
BIT_SET_ACCESSORS[0].set(handle, skyYMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The block light mask.
|
|
|
|
*
|
|
|
|
* @return a {@link BitSet}
|
|
|
|
*/
|
|
|
|
public BitSet getBlockYMask() {
|
|
|
|
return (BitSet) BIT_SET_ACCESSORS[1].get(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the block light mask
|
|
|
|
*
|
|
|
|
* @param blockYMask the new mask
|
|
|
|
*/
|
|
|
|
public void setBlockYMask(BitSet blockYMask) {
|
|
|
|
BIT_SET_ACCESSORS[1].set(handle, blockYMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The empty sky light mask.
|
|
|
|
*
|
|
|
|
* @return a {@link BitSet}
|
|
|
|
*/
|
|
|
|
public BitSet getEmptySkyYMask() {
|
|
|
|
return (BitSet) BIT_SET_ACCESSORS[2].get(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the empty sky light mask
|
|
|
|
*
|
|
|
|
* @param emptySkyYMask the new mask
|
|
|
|
*/
|
|
|
|
public void setEmptySkyYMask(BitSet emptySkyYMask) {
|
|
|
|
BIT_SET_ACCESSORS[2].set(handle, emptySkyYMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The empty block light mask.
|
|
|
|
*
|
|
|
|
* @return a {@link BitSet}
|
|
|
|
*/
|
|
|
|
public BitSet getEmptyBlockYMask() {
|
|
|
|
return (BitSet) BIT_SET_ACCESSORS[3].get(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the empty block light mask
|
|
|
|
*
|
|
|
|
* @param emptyBlockYMask the new mask
|
|
|
|
*/
|
|
|
|
public void setEmptyBlockYMask(BitSet emptyBlockYMask) {
|
|
|
|
BIT_SET_ACCESSORS[3].set(handle, emptyBlockYMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A mutable list of sky light arrays.
|
|
|
|
*
|
|
|
|
* @return a mutable list of byte arrays.
|
|
|
|
*/
|
|
|
|
public List<byte[]> getSkyUpdates() {
|
|
|
|
//noinspection unchecked
|
|
|
|
return (List<byte[]>) BYTE_ARRAY_LIST_ACCESSORS[0].get(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A mutable list of block light arrays.
|
|
|
|
*
|
|
|
|
* @return a mutable list of byte arrays.
|
|
|
|
*/
|
|
|
|
public List<byte[]> getBlockUpdates() {
|
|
|
|
//noinspection unchecked
|
|
|
|
return (List<byte[]>) BYTE_ARRAY_LIST_ACCESSORS[1].get(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether edges can be trusted for light updates or not.
|
|
|
|
*
|
|
|
|
* @return {@code true} if edges can be trusted, {@code false} otherwise.
|
|
|
|
*/
|
|
|
|
public boolean isTrustEdges() {
|
|
|
|
return (boolean) TRUST_EDGES_ACCESSOR.get(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets whether edges can be trusted for light updates or not.
|
|
|
|
*
|
|
|
|
* @param trustEdges the new value
|
|
|
|
*/
|
|
|
|
public void setTrustEdges(boolean trustEdges) {
|
|
|
|
TRUST_EDGES_ACCESSOR.set(handle, trustEdges);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static LightData fromValues(BitSet skyYMask, BitSet blockYMask, BitSet emptySkyYMask, BitSet emptyBlockYMask,
|
|
|
|
List<byte[]> skyUpdates, List<byte[]> blockUpdates, boolean trustEdges) {
|
|
|
|
LightData data = new LightData(LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR.invoke(MinecraftReflection.getPacketDataSerializer(new ZeroBuffer()), 0, 0));
|
|
|
|
|
|
|
|
data.setTrustEdges(trustEdges);
|
|
|
|
data.setSkyYMask(skyYMask);
|
|
|
|
data.setBlockYMask(blockYMask);
|
|
|
|
data.setEmptySkyYMask(emptySkyYMask);
|
|
|
|
data.setEmptyBlockYMask(emptyBlockYMask);
|
|
|
|
data.getSkyUpdates().addAll(skyUpdates);
|
|
|
|
data.getBlockUpdates().addAll(blockUpdates);
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents an immutable BlockEntityInfo in the MAP_CHUNK packet.
|
|
|
|
*
|
|
|
|
* @author Etrayed
|
|
|
|
*/
|
|
|
|
public static class BlockEntityInfo extends AbstractWrapper {
|
|
|
|
|
|
|
|
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getBlockEntityInfoClass();
|
|
|
|
private static final WrappedRegistry REGISTRY = WrappedRegistry.getRegistry(MinecraftReflection.getBlockEntityTypeClass());
|
|
|
|
|
|
|
|
private static final ConstructorAccessor BLOCK_ENTITY_INFO_CONSTRUCTOR;
|
|
|
|
|
|
|
|
private static final FieldAccessor PACKED_XZ_ACCESSOR;
|
|
|
|
private static final FieldAccessor Y_ACCESSOR;
|
|
|
|
private static final FieldAccessor TYPE_ACCESSOR;
|
|
|
|
private static final FieldAccessor TAG_ACCESSOR;
|
|
|
|
|
|
|
|
static {
|
|
|
|
FuzzyReflection reflection = FuzzyReflection.fromClass(HANDLE_TYPE, true);
|
|
|
|
List<Field> posFields = reflection.getFieldList(FuzzyFieldContract.newBuilder().typeExact(int.class).build());
|
|
|
|
|
|
|
|
BLOCK_ENTITY_INFO_CONSTRUCTOR = Accessors.getConstructorAccessor(HANDLE_TYPE, int.class, int.class,
|
|
|
|
MinecraftReflection.getBlockEntityTypeClass(), MinecraftReflection.getNBTCompoundClass());
|
|
|
|
PACKED_XZ_ACCESSOR = Accessors.getFieldAccessor(posFields.get(0));
|
|
|
|
Y_ACCESSOR = Accessors.getFieldAccessor(posFields.get(1));
|
|
|
|
TYPE_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder()
|
|
|
|
.typeExact(MinecraftReflection.getBlockEntityTypeClass())
|
|
|
|
.build()));
|
|
|
|
TAG_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder()
|
|
|
|
.typeExact(MinecraftReflection.getNBTCompoundClass())
|
|
|
|
.build()));
|
|
|
|
}
|
|
|
|
|
|
|
|
public BlockEntityInfo(Object handle) {
|
|
|
|
super(HANDLE_TYPE);
|
|
|
|
|
|
|
|
setHandle(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The section-relative X-coordinate of the block entity.
|
|
|
|
*
|
|
|
|
* @return the unpacked X-coordinate.
|
|
|
|
*/
|
|
|
|
public int getSectionX() {
|
|
|
|
return (int) PACKED_XZ_ACCESSOR.get(handle) >> 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the section-relative X-coordinate of the block entity
|
|
|
|
*
|
|
|
|
* @param sectionX the section-relative x coordinate
|
|
|
|
*/
|
|
|
|
public void setSectionX(int sectionX) {
|
|
|
|
PACKED_XZ_ACCESSOR.set(handle, sectionX << 4 | getSectionZ());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The section-relative Z-coordinate of the block entity.
|
|
|
|
*
|
|
|
|
* @return the unpacked Z-coordinate.
|
|
|
|
*/
|
|
|
|
public int getSectionZ() {
|
|
|
|
return (int) PACKED_XZ_ACCESSOR.get(handle) & 0xF;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the section-relative Z-coordinate of the block entity
|
|
|
|
*
|
|
|
|
* @param sectionZ the section-relative z coordinate
|
|
|
|
*/
|
|
|
|
public void setSectionZ(int sectionZ) {
|
|
|
|
PACKED_XZ_ACCESSOR.set(handle, getSectionX() << 4 | sectionZ);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The Y-coordinate of the block entity.
|
|
|
|
*
|
|
|
|
* @return the Y-coordinate.
|
|
|
|
*/
|
|
|
|
public int getY() {
|
|
|
|
return (int) Y_ACCESSOR.get(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the Y-coordinate of the block entity.
|
|
|
|
*
|
|
|
|
* @param y the new y coordinate
|
|
|
|
*/
|
|
|
|
public void setY(int y) {
|
|
|
|
Y_ACCESSOR.set(handle, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The registry key of the block entity type.
|
|
|
|
*
|
|
|
|
* @return the registry key.
|
|
|
|
*/
|
|
|
|
public MinecraftKey getTypeKey() {
|
|
|
|
return REGISTRY.getKey(TYPE_ACCESSOR.get(handle));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the registry key of the block entity type
|
|
|
|
*
|
|
|
|
* @param typeKey the new block entity type key
|
|
|
|
*/
|
|
|
|
public void setTypeKey(MinecraftKey typeKey) {
|
|
|
|
TYPE_ACCESSOR.set(handle, REGISTRY.get(typeKey));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The NBT-Tag of this block entity containing additional information. (ex. text lines for a sign)
|
|
|
|
*
|
|
|
|
* @return the NBT-Tag or {@code null}.
|
|
|
|
*/
|
|
|
|
@Nullable
|
|
|
|
public NbtCompound getAdditionalData() {
|
|
|
|
Object tagHandle = TAG_ACCESSOR.get(handle);
|
|
|
|
|
|
|
|
return tagHandle == null ? null : NbtFactory.fromNMSCompound(tagHandle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Edits the additional data specified for this block entity.
|
|
|
|
*
|
|
|
|
* @param additionalData the additional data for this block entity, can be {@code null}
|
|
|
|
*/
|
|
|
|
public void setAdditionalData(@Nullable NbtCompound additionalData) {
|
|
|
|
TAG_ACCESSOR.set(handle, additionalData == null ? null : NbtFactory.fromBase(additionalData).getHandle());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a wrapper using raw values
|
|
|
|
*
|
|
|
|
* @param sectionX the section-relative X-coordinate of the block entity.
|
|
|
|
* @param sectionZ the section-relative Z-coordinate of the block entity.
|
|
|
|
* @param y the Y-coordinate of the block entity.
|
|
|
|
* @param typeKey the minecraft key of the block entity type.
|
|
|
|
*/
|
|
|
|
public static BlockEntityInfo fromValues(int sectionX, int sectionZ, int y, MinecraftKey typeKey) {
|
|
|
|
return fromValues(sectionX, sectionZ, y, typeKey, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a wrapper using raw values
|
|
|
|
*
|
|
|
|
* @param sectionX the section-relative X-coordinate of the block entity.
|
|
|
|
* @param sectionZ the section-relative Z-coordinate of the block entity.
|
|
|
|
* @param y the Y-coordinate of the block entity.
|
|
|
|
* @param typeKey the minecraft key of the block entity type.
|
|
|
|
* @param additionalData An NBT-Tag containing additional information. Can be {@code null}.
|
|
|
|
*/
|
|
|
|
public static BlockEntityInfo fromValues(int sectionX, int sectionZ, int y, MinecraftKey typeKey, @Nullable NbtCompound additionalData) {
|
|
|
|
return new BlockEntityInfo(BLOCK_ENTITY_INFO_CONSTRUCTOR.invoke(
|
|
|
|
sectionX << 4 | sectionZ,
|
|
|
|
y,
|
|
|
|
REGISTRY.get(typeKey),
|
|
|
|
additionalData == null ? null : NbtFactory.fromBase(additionalData).getHandle()
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|