Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.cloudburstmc.protocol.bedrock.codec.v860_netease;

import io.netty.buffer.ByteBuf;
import org.cloudburstmc.protocol.bedrock.codec.EntityDataTypeMap;
import org.cloudburstmc.protocol.bedrock.codec.v776.BedrockCodecHelper_v776;
import org.cloudburstmc.protocol.bedrock.data.Ability;
import org.cloudburstmc.protocol.bedrock.data.PlayerAuthInputData;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.TextProcessingEventOrigin;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ItemStackRequestActionType;
import org.cloudburstmc.protocol.bedrock.util.TypeMap;
import org.cloudburstmc.protocol.bedrock.util.VarInts;

import java.math.BigInteger;
import java.util.Set;

public class BedrockCodecHelper_v860_NetEase extends BedrockCodecHelper_v776 {
private static final int PLAYER_AUTH_INPUT_DATA_NETEASE = PlayerAuthInputData.RECEIVED_SERVER_DATA.ordinal() + 1;

public BedrockCodecHelper_v860_NetEase(EntityDataTypeMap entityData, TypeMap<Class<?>> gameRulesTypes,
TypeMap<ItemStackRequestActionType> stackRequestActionTypes,
TypeMap<ContainerSlotType> containerSlotTypes, TypeMap<Ability> abilities,
TypeMap<TextProcessingEventOrigin> textProcessingEventOrigins) {
super(entityData, gameRulesTypes, stackRequestActionTypes, containerSlotTypes, abilities, textProcessingEventOrigins);
}

@Override
public <T extends Enum<?>> void readLargeVarIntFlags(ByteBuf buffer, Set<T> flags, Class<T> clazz) {
boolean adaptNetEase = clazz == PlayerAuthInputData.class;
BigInteger flagsInt = VarInts.readUnsignedBigVarInt(buffer, clazz.getEnumConstants().length);
for (T flag : clazz.getEnumConstants()) {
int ordinal = flag.ordinal();
if (adaptNetEase) {
if (ordinal == PLAYER_AUTH_INPUT_DATA_NETEASE) {
continue;
}
if (ordinal > PLAYER_AUTH_INPUT_DATA_NETEASE) {
ordinal -= 1;
}
}
if (flagsInt.testBit(ordinal)) {
flags.add(flag);
}
}
}

@Override
public <T extends Enum<?>> void writeLargeVarIntFlags(ByteBuf buffer, Set<T> flags, Class<T> clazz) {
boolean adaptNetEase = clazz == PlayerAuthInputData.class;
BigInteger flagsInt = BigInteger.ZERO;
for (T flag : flags) {
int ordinal = flag.ordinal();
if (adaptNetEase && ordinal >= PLAYER_AUTH_INPUT_DATA_NETEASE) {
ordinal += 1;
}
flagsInt = flagsInt.setBit(ordinal);
}
VarInts.writeUnsignedBigVarInt(buffer, flagsInt);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.cloudburstmc.protocol.bedrock.codec.v860_netease;

import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.cloudburstmc.protocol.bedrock.codec.netease.serializer.ConfirmSkinSerializer;
import org.cloudburstmc.protocol.bedrock.codec.netease.serializer.NetEaseJsonSerializer;
import org.cloudburstmc.protocol.bedrock.codec.netease.serializer.PlayerEnchantOptionsSerializer_v407_NetEase;
import org.cloudburstmc.protocol.bedrock.codec.netease.serializer.PyRpcSerializer;
import org.cloudburstmc.protocol.bedrock.codec.netease.serializer.StoreBuySuccessSerializer;
import org.cloudburstmc.protocol.bedrock.codec.v686_netease.serializer.TextSerializer_v686_NetEase;
import org.cloudburstmc.protocol.bedrock.codec.v712.Bedrock_v712;
import org.cloudburstmc.protocol.bedrock.codec.v766_netease.serializer.PlayerAuthInputSerializer_v766_NetEase;
import org.cloudburstmc.protocol.bedrock.codec.v860.Bedrock_v860;
import org.cloudburstmc.protocol.bedrock.codec.v860_netease.serializer.BiomeDefinitionListSerializer_v860_NetEase;
import org.cloudburstmc.protocol.bedrock.codec.v860_netease.serializer.ContainerOpenSerializer_v860_NetEase;
import org.cloudburstmc.protocol.bedrock.data.PacketRecipient;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.packet.BiomeDefinitionListPacket;
import org.cloudburstmc.protocol.bedrock.packet.ConfirmSkinPacket;
import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket;
import org.cloudburstmc.protocol.bedrock.packet.NetEaseJsonPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerEnchantOptionsPacket;
import org.cloudburstmc.protocol.bedrock.packet.PyRpcPacket;
import org.cloudburstmc.protocol.bedrock.packet.StoreBuySuccessPacket;
import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
import org.cloudburstmc.protocol.bedrock.util.TypeMap;

public class Bedrock_v860_NetEase extends Bedrock_v860 {
protected static final TypeMap<ContainerSlotType> CONTAINER_SLOT_TYPES = Bedrock_v712.CONTAINER_SLOT_TYPES
.toBuilder()
.shift(17, 1)
.insert(17, ContainerSlotType.RECIPE_CUSTOM)
.build();

public static final BedrockCodec CODEC = Bedrock_v860.CODEC.toBuilder()
.raknetProtocolVersion(8)
.helper(() -> new BedrockCodecHelper_v860_NetEase(ENTITY_DATA, GAME_RULE_TYPES, ITEM_STACK_REQUEST_TYPES, CONTAINER_SLOT_TYPES, PLAYER_ABILITIES, TEXT_PROCESSING_ORIGINS))
.updateSerializer(PlayerAuthInputPacket.class, PlayerAuthInputSerializer_v766_NetEase.INSTANCE)
.updateSerializer(TextPacket.class, TextSerializer_v686_NetEase.INSTANCE)
.updateSerializer(PlayerEnchantOptionsPacket.class, PlayerEnchantOptionsSerializer_v407_NetEase.INSTANCE)
.updateSerializer(BiomeDefinitionListPacket.class, BiomeDefinitionListSerializer_v860_NetEase.INSTANCE)
.updateSerializer(ContainerOpenPacket.class, ContainerOpenSerializer_v860_NetEase.INSTANCE)
.registerPacket(PyRpcPacket::new, PyRpcSerializer.INSTANCE, 200, PacketRecipient.BOTH)
.registerPacket(StoreBuySuccessPacket::new, StoreBuySuccessSerializer.INSTANCE, 202, PacketRecipient.BOTH)
.registerPacket(NetEaseJsonPacket::new, NetEaseJsonSerializer.INSTANCE, 203, PacketRecipient.BOTH)
.registerPacket(ConfirmSkinPacket::new, ConfirmSkinSerializer.INSTANCE, 228, PacketRecipient.CLIENT)
.build();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package org.cloudburstmc.protocol.bedrock.codec.v860_netease.serializer;

import io.netty.buffer.ByteBuf;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodecHelper;
import org.cloudburstmc.protocol.bedrock.codec.v859.serializer.BiomeDefinitionListSerializer_v859;
import org.cloudburstmc.protocol.bedrock.data.biome.BiomeDefinitionChunkGenData;
import org.cloudburstmc.protocol.bedrock.data.biome.BiomeDefinitionData;
import org.cloudburstmc.protocol.bedrock.util.Preconditions;
import org.cloudburstmc.protocol.bedrock.util.SequencedHashSet;
import org.cloudburstmc.protocol.bedrock.util.VarInts;
import org.cloudburstmc.protocol.bedrock.util.index.IndexedList;

import java.awt.Color;
import java.util.List;

/**
* NetEase {@code v860} BiomeDefinitionList serializer.
*
* <p>The NetEase {@code v860} fork uses the vanilla {@code v844}/{@code v859} biome definition layout (it carries
* {@code foliageSnow} and {@code BiomeReplacementData}, not the legacy spore/ash fields) but inserts two
* NetEase-only fields between {@code rain} and {@code tags}:</p>
* <ul>
* <li>{@code dimension} (LInt) — biome dimension id</li>
* <li>{@code vanilla} (String) — the vanilla biome identifier this entry is derived from</li>
* </ul>
* <p>Both fields were introduced in {@code 1.21.90-netease} and are confirmed by both the Nukkit-MOT and
* SynapseAPI ({@code BiomeDefinitionListPacket121111}) China client implementations. Since
* {@link BiomeDefinitionData} has no components for them, the values are <b>read and discarded</b> on decode
* and written as defaults ({@code 0} / {@code ""}) on encode — the same strategy used by
* {@link org.cloudburstmc.protocol.bedrock.codec.v819_netease.serializer.BiomeDefinitionListSerializer_v819_NetEase}.</p>
*
* <p>{@link BiomeDefinitionListSerializer_v859} is the base class because it already provides the
* {@code foliageSnow} definition layout (inherited from {@code v844}) and the {@code BiomeReplacementData}
* handling in {@code readDefinitionChunkGen}/{@code writeDefinitionChunkGen}.</p>
*
* <p><b>Known limitation</b> (out of scope here, tracked separately): the inherited {@code v859}
* {@code chunkGenData} trailer serializes {@code BiomeReplacementData} as a single optional object with
* {@code short} biome/dimension fields, whereas the NetEase {@code 860} client (per the SynapseAPI
* {@code BiomeDefinitionListPacket121111} implementation) writes a {@code List<BiomeReplacementData>} with
* string-pooled biome/dimension fields. This only affects biomes that carry a non-null {@code chunkGenData}
* containing replacement data; biomes without {@code chunkGenData} (the common case) are unaffected.</p>
*/
public class BiomeDefinitionListSerializer_v860_NetEase extends BiomeDefinitionListSerializer_v859 {
public static final BiomeDefinitionListSerializer_v860_NetEase INSTANCE = new BiomeDefinitionListSerializer_v860_NetEase();

@Override
protected BiomeDefinitionData readDefinition(ByteBuf buffer, BedrockCodecHelper helper, List<String> strings) {
Integer id = this.readDefinitionId(buffer, helper);
float temperature = buffer.readFloatLE();
float downfall = buffer.readFloatLE();
float foliageSnow = buffer.readFloatLE();
float depth = buffer.readFloatLE();
float scale = buffer.readFloatLE();
Color mapWaterColor = new Color(buffer.readIntLE(), true);
boolean rain = buffer.readBoolean();

// NetEase-only fields (since 1.21.90-netease): dimension (LInt) + vanilla (String).
buffer.readIntLE();
helper.readString(buffer);

IndexedList<String> tags = helper.readOptional(buffer, null, byteBuf -> {
int length = VarInts.readUnsignedInt(byteBuf);
Preconditions.checkArgument(byteBuf.isReadable(length * 2), "Not enough readable bytes for tags");
int[] array = new int[length];
for (int i = 0; i < length; i++) {
array[i] = byteBuf.readUnsignedShortLE();
}
return new IndexedList<>(strings, array);
});

BiomeDefinitionChunkGenData chunkGenData = helper.readOptional(buffer, null,
(buf, aHelper) -> this.readDefinitionChunkGen(buf, aHelper, strings));

return new BiomeDefinitionData(id, temperature, downfall, 0, 0, 0, 0, foliageSnow, depth, scale,
mapWaterColor, rain, tags, chunkGenData);
}

@Override
protected void writeDefinition(ByteBuf buffer, BedrockCodecHelper helper, BiomeDefinitionData definition, SequencedHashSet<String> strings) {
this.writeDefinitionId(buffer, helper, definition);
buffer.writeFloatLE(definition.temperature());
buffer.writeFloatLE(definition.downfall());
buffer.writeFloatLE(definition.foliageSnow());
buffer.writeFloatLE(definition.depth());
buffer.writeFloatLE(definition.scale());
buffer.writeIntLE(definition.mapWaterColor().getRGB());
buffer.writeBoolean(definition.rain());

// NetEase-only fields (since 1.21.90-netease): dimension (LInt) + vanilla (String).
buffer.writeIntLE(0);
helper.writeString(buffer, "");

helper.writeOptionalNull(buffer, definition.tags(), (byteBuf, aHelper, tags) -> {
List<String> values = tags.get();
VarInts.writeUnsignedInt(byteBuf, values.size());
for (String tag : values) {
byteBuf.writeShortLE(strings.addAndGetIndex(tag));
}
});
helper.writeOptionalNull(buffer, definition.chunkGenData(),
(buf, aHelper, data) -> writeDefinitionChunkGen(buf, aHelper, data, strings));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.cloudburstmc.protocol.bedrock.codec.v860_netease.serializer;

import io.netty.buffer.ByteBuf;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodecHelper;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.ContainerOpenSerializer_v291;
import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket;

public class ContainerOpenSerializer_v860_NetEase extends ContainerOpenSerializer_v291 {
public static final ContainerOpenSerializer_v860_NetEase INSTANCE = new ContainerOpenSerializer_v860_NetEase();

@Override
public void serialize(ByteBuf buffer, BedrockCodecHelper helper, ContainerOpenPacket packet) {
super.serialize(buffer, helper, packet);
buffer.writeBoolean(packet.isIgnoreBlock());
}

@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, ContainerOpenPacket packet) {
super.deserialize(buffer, helper, packet);
packet.setIgnoreBlock(buffer.readBoolean());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.annotation.NetEaseOnly;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;

/**
Expand Down Expand Up @@ -34,6 +35,12 @@ public class ContainerOpenPacket implements BedrockPacket {
* used if the ContainerType is one that points to an entity, for example a horse.
*/
private long uniqueEntityId = -1;
/**
* NetEase-only. When true, the client opens the container even when the backing block is not
* physically present. Appended at the end of the packet since {@code 1.21.120-netease}.
*/
@NetEaseOnly
private boolean ignoreBlock;

@Override
public final PacketSignal handle(BedrockPacketHandler handler) {
Expand Down
Loading