Initial support for 1.19.30 Bedrock

This commit is contained in:
Camotoy 2022-09-14 21:17:08 -04:00
parent 77827d5cf5
commit e64e12ff98
No known key found for this signature in database
GPG key ID: 7EEFB66FE798081F
10 changed files with 66 additions and 41 deletions

View file

@ -31,7 +31,7 @@ object Versions {
const val gsonVersion = "2.3.1" // Provided by Spigot 1.8.8
const val nbtVersion = "2.1.0"
const val websocketVersion = "1.5.1"
const val protocolVersion = "0bd459f"
const val protocolVersion = "f0feacd"
const val raknetVersion = "1.6.28-20220125.214016-6"
const val mcauthlibVersion = "d9d773e"
const val mcprotocollibversion = "9f78bd5"

View file

@ -30,7 +30,7 @@ dependencies {
// Network libraries
implementation("org.java-websocket", "Java-WebSocket", Versions.websocketVersion)
api("com.github.CloudburstMC.Protocol", "bedrock-v544", Versions.protocolVersion) {
api("com.github.CloudburstMC.Protocol", "bedrock-v553", Versions.protocolVersion) {
exclude("com.nukkitx.network", "raknet")
exclude("com.nukkitx", "nbt")
}

View file

@ -28,6 +28,7 @@ package org.geysermc.geyser.network;
import com.nukkitx.protocol.bedrock.BedrockPong;
import com.nukkitx.protocol.bedrock.BedrockServerEventHandler;
import com.nukkitx.protocol.bedrock.BedrockServerSession;
import com.nukkitx.protocol.bedrock.v553.Bedrock_v553;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.DefaultEventLoopGroup;
@ -171,7 +172,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
@Override
public void onSessionCreation(@Nonnull BedrockServerSession bedrockServerSession) {
try {
bedrockServerSession.setPacketCodec(GameProtocol.DEFAULT_BEDROCK_CODEC);
bedrockServerSession.setPacketCodec(Bedrock_v553.V553_CODEC); // Has the RequestNetworkSettingsPacket
bedrockServerSession.setLogging(true);
bedrockServerSession.setCompressionLevel(geyser.getConfig().getBedrock().getCompressionLevel());
bedrockServerSession.setPacketHandler(new UpstreamPacketHandler(geyser, new GeyserSession(geyser, bedrockServerSession, eventLoopGroup.next())));

View file

@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
import com.nukkitx.protocol.bedrock.v527.Bedrock_v527;
import com.nukkitx.protocol.bedrock.v534.Bedrock_v534;
import com.nukkitx.protocol.bedrock.v544.Bedrock_v544;
import com.nukkitx.protocol.bedrock.v553.Bedrock_v553;
import org.geysermc.geyser.session.GeyserSession;
import java.util.ArrayList;
@ -71,6 +72,7 @@ public final class GameProtocol {
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder()
.minecraftVersion("1.19.21/1.19.22")
.build());
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v553.V553_CODEC);
}
/**

View file

@ -856,4 +856,11 @@ public class LoggingPacketHandler implements BedrockPacketHandler {
public boolean handle(FilterTextPacket packet) {
return defaultHandler(packet);
}
// 1.19.30 new packet
@Override
public boolean handle(RequestNetworkSettingsPacket packet) {
return defaultHandler(packet);
}
}

View file

@ -28,6 +28,7 @@ package org.geysermc.geyser.network;
import com.nukkitx.protocol.bedrock.BedrockPacket;
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
import com.nukkitx.protocol.bedrock.data.ExperimentData;
import com.nukkitx.protocol.bedrock.data.PacketCompressionAlgorithm;
import com.nukkitx.protocol.bedrock.data.ResourcePackType;
import com.nukkitx.protocol.bedrock.packet.*;
import org.geysermc.geyser.GeyserImpl;
@ -61,6 +62,20 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
return translateAndDefault(packet);
}
@Override
public boolean handle(RequestNetworkSettingsPacket packet) {
// New since 1.19.30 - sent before login packet
PacketCompressionAlgorithm algorithm = PacketCompressionAlgorithm.ZLIB;
NetworkSettingsPacket responsePacket = new NetworkSettingsPacket();
responsePacket.setCompressionAlgorithm(algorithm);
responsePacket.setCompressionThreshold(512);
session.sendUpstreamPacketImmediately(responsePacket);
session.getUpstream().getSession().setCompression(algorithm);
return true;
}
@Override
public boolean handle(LoginPacket loginPacket) {
if (geyser.isShuttingDown()) {

View file

@ -33,6 +33,7 @@ import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.protocol.bedrock.data.inventory.CraftingData;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import com.nukkitx.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
@ -171,7 +172,7 @@ public class RecipeRegistryPopulator {
/* Convert end */
return CraftingData.fromShaped(uuid.toString(), shape.get(0).length(), shape.size(),
inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId);
inputs.stream().map(ItemDescriptorWithCount::fromItem).toList(), Collections.singletonList(output), uuid, "crafting_table", 0, netId);
}
List<ItemData> inputs = new ObjectArrayList<>();
for (JsonNode entry : node.get("inputs")) {
@ -191,10 +192,10 @@ public class RecipeRegistryPopulator {
if (type == 5) {
// Shulker box
return CraftingData.fromShulkerBox(uuid.toString(),
inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId);
inputs.stream().map(ItemDescriptorWithCount::fromItem).toList(), Collections.singletonList(output), uuid, "crafting_table", 0, netId);
}
return CraftingData.fromShapeless(uuid.toString(),
inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId);
inputs.stream().map(ItemDescriptorWithCount::fromItem).toList(), Collections.singletonList(output), uuid, "crafting_table", 0, netId);
}
private static ItemData getBedrockItemFromIdentifierJson(ItemMapping mapping, JsonNode itemNode) {

View file

@ -62,7 +62,8 @@ public final class SessionManager {
}
public void removeSession(GeyserSession session) {
if (sessions.remove(session.getPlayerEntity().getUuid()) == null) {
UUID uuid = session.getPlayerEntity().getUuid();
if (uuid == null || sessions.remove(uuid) == null) {
// Connection was likely pending
pendingSessions.remove(session);
}

View file

@ -34,9 +34,10 @@ import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeDa
import com.github.steveice10.mc.protocol.data.game.recipe.data.SmithingRecipeData;
import com.github.steveice10.mc.protocol.data.game.recipe.data.StoneCuttingRecipeData;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundUpdateRecipesPacket;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.protocol.bedrock.data.inventory.CraftingData;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import com.nukkitx.protocol.bedrock.data.inventory.descriptor.DefaultDescriptor;
import com.nukkitx.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount;
import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@ -99,8 +100,8 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
}
// Strip NBT - tools won't appear in the recipe book otherwise
output = output.toBuilder().tag(null).build();
ItemData[][] inputCombinations = combinations(session, shapelessRecipeData.getIngredients());
for (ItemData[] inputs : inputCombinations) {
ItemDescriptorWithCount[][] inputCombinations = combinations(session, shapelessRecipeData.getIngredients());
for (ItemDescriptorWithCount[] inputs : inputCombinations) {
UUID uuid = UUID.randomUUID();
craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
Arrays.asList(inputs), Collections.singletonList(output), uuid, "crafting_table", 0, netId));
@ -116,8 +117,8 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
}
// See above
output = output.toBuilder().tag(null).build();
ItemData[][] inputCombinations = combinations(session, shapedRecipeData.getIngredients());
for (ItemData[] inputs : inputCombinations) {
ItemDescriptorWithCount[][] inputCombinations = combinations(session, shapedRecipeData.getIngredients());
for (ItemDescriptorWithCount[] inputs : inputCombinations) {
UUID uuid = UUID.randomUUID();
craftingDataPacket.getCraftingData().add(CraftingData.fromShaped(uuid.toString(),
shapedRecipeData.getWidth(), shapedRecipeData.getHeight(), Arrays.asList(inputs),
@ -141,14 +142,14 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
SmithingRecipeData recipeData = (SmithingRecipeData) recipe.getData();
ItemData output = ItemTranslator.translateToBedrock(session, recipeData.getResult());
for (ItemStack base : recipeData.getBase().getOptions()) {
ItemData bedrockBase = ItemTranslator.translateToBedrock(session, base);
ItemDescriptorWithCount bedrockBase = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, base));
for (ItemStack addition : recipeData.getAddition().getOptions()) {
ItemData bedrockAddition = ItemTranslator.translateToBedrock(session, addition);
ItemDescriptorWithCount bedrockAddition = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, addition));
UUID uuid = UUID.randomUUID();
craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
Arrays.asList(bedrockBase, bedrockAddition),
List.of(bedrockBase, bedrockAddition),
Collections.singletonList(output), uuid, "smithing_table", 2, netId++));
}
}
@ -178,6 +179,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
// As of 1.16.4, all stonecutter recipes have one ingredient option
ItemStack ingredient = stoneCuttingData.getIngredient().getOptions()[0];
ItemData input = ItemTranslator.translateToBedrock(session, ingredient);
ItemDescriptorWithCount descriptor = ItemDescriptorWithCount.fromItem(input);
ItemStack javaOutput = stoneCuttingData.getResult();
ItemData output = ItemTranslator.translateToBedrock(session, javaOutput);
if (input.equals(ItemData.AIR) || output.equals(ItemData.AIR)) {
@ -188,7 +190,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
// We need to register stonecutting recipes so they show up on Bedrock
craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
Collections.singletonList(input), Collections.singletonList(output), uuid, "stonecutter", 0, netId));
Collections.singletonList(descriptor), Collections.singletonList(output), uuid, "stonecutter", 0, netId));
// Save the recipe list for reference when crafting
// Add the net ID as the key and the button required + output for the value
@ -209,19 +211,19 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
*
* @return the Java ingredient list as an array that Bedrock can understand
*/
private ItemData[][] combinations(GeyserSession session, Ingredient[] ingredients) {
Map<Set<ItemData>, IntSet> squashedOptions = new HashMap<>();
private ItemDescriptorWithCount[][] combinations(GeyserSession session, Ingredient[] ingredients) {
Map<Set<ItemDescriptorWithCount>, IntSet> squashedOptions = new HashMap<>();
for (int i = 0; i < ingredients.length; i++) {
if (ingredients[i].getOptions().length == 0) {
squashedOptions.computeIfAbsent(Collections.singleton(ItemData.AIR), k -> new IntOpenHashSet()).add(i);
squashedOptions.computeIfAbsent(Collections.singleton(ItemDescriptorWithCount.EMPTY), k -> new IntOpenHashSet()).add(i);
continue;
}
Ingredient ingredient = ingredients[i];
Map<GroupedItem, List<ItemData>> groupedByIds = Arrays.stream(ingredient.getOptions())
.map(item -> ItemTranslator.translateToBedrock(session, item))
.collect(Collectors.groupingBy(item -> new GroupedItem(item.getId(), item.getCount(), item.getTag())));
Set<ItemData> optionSet = new HashSet<>(groupedByIds.size());
for (Map.Entry<GroupedItem, List<ItemData>> entry : groupedByIds.entrySet()) {
Map<GroupedItem, List<ItemDescriptorWithCount>> groupedByIds = Arrays.stream(ingredient.getOptions())
.map(item -> ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, item)))
.collect(Collectors.groupingBy(item -> new GroupedItem(((DefaultDescriptor) item.getDescriptor()).getItemId(), item.getCount())));
Set<ItemDescriptorWithCount> optionSet = new HashSet<>(groupedByIds.size());
for (Map.Entry<GroupedItem, List<ItemDescriptorWithCount>> entry : groupedByIds.entrySet()) {
if (entry.getValue().size() > 1) {
GroupedItem groupedItem = entry.getKey();
int idCount = 0;
@ -234,42 +236,38 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
if (entry.getValue().size() < idCount) {
optionSet.addAll(entry.getValue());
} else {
optionSet.add(ItemData.builder()
.id(groupedItem.id)
.damage(Short.MAX_VALUE)
.count(groupedItem.count)
.tag(groupedItem.tag).build());
optionSet.add(new ItemDescriptorWithCount(new DefaultDescriptor(groupedItem.id, Short.MAX_VALUE), groupedItem.count));
}
} else {
ItemData item = entry.getValue().get(0);
ItemDescriptorWithCount item = entry.getValue().get(0);
optionSet.add(item);
}
}
squashedOptions.computeIfAbsent(optionSet, k -> new IntOpenHashSet()).add(i);
}
int totalCombinations = 1;
for (Set<ItemData> optionSet : squashedOptions.keySet()) {
for (Set<ItemDescriptorWithCount> optionSet : squashedOptions.keySet()) {
totalCombinations *= optionSet.size();
}
if (totalCombinations > 500) {
ItemData[] translatedItems = new ItemData[ingredients.length];
ItemDescriptorWithCount[] translatedItems = new ItemDescriptorWithCount[ingredients.length];
for (int i = 0; i < ingredients.length; i++) {
if (ingredients[i].getOptions().length > 0) {
translatedItems[i] = ItemTranslator.translateToBedrock(session, ingredients[i].getOptions()[0]);
translatedItems[i] = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, ingredients[i].getOptions()[0]));
} else {
translatedItems[i] = ItemData.AIR;
translatedItems[i] = ItemDescriptorWithCount.EMPTY;
}
}
return new ItemData[][]{translatedItems};
return new ItemDescriptorWithCount[][]{translatedItems};
}
List<Set<ItemData>> sortedSets = new ArrayList<>(squashedOptions.keySet());
List<Set<ItemDescriptorWithCount>> sortedSets = new ArrayList<>(squashedOptions.keySet());
sortedSets.sort(Comparator.comparing(Set::size, Comparator.reverseOrder()));
ItemData[][] combinations = new ItemData[totalCombinations][ingredients.length];
ItemDescriptorWithCount[][] combinations = new ItemDescriptorWithCount[totalCombinations][ingredients.length];
int x = 1;
for (Set<ItemData> set : sortedSets) {
for (Set<ItemDescriptorWithCount> set : sortedSets) {
IntSet slotSet = squashedOptions.get(set);
int i = 0;
for (ItemData item : set) {
for (ItemDescriptorWithCount item : set) {
for (int j = 0; j < totalCombinations / set.size(); j++) {
final int comboIndex = (i * x) + (j % x) + ((j / x) * set.size() * x);
for (int slot : slotSet) {
@ -288,6 +286,5 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
private static class GroupedItem {
int id;
int count;
NbtMap tag;
}
}

View file

@ -31,6 +31,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.inventory.Cli
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
import com.nukkitx.protocol.bedrock.data.inventory.CraftingData;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import com.nukkitx.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount;
import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.geyser.GeyserImpl;
@ -186,7 +187,7 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
uuid.toString(),
width,
height,
Arrays.asList(ingredients),
Arrays.stream(ingredients).map(ItemDescriptorWithCount::fromItem).toList(),
Collections.singletonList(ItemTranslator.translateToBedrock(session, item)),
uuid,
"crafting_table",