mirror of
https://github.com/GeyserMC/Geyser.git
synced 2026-01-06 15:41:50 +00:00
Implement shapeless/complex recipes; fix crawling
This commit is contained in:
@@ -45,6 +45,7 @@ import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.GeyserDirtyMetadata;
|
||||
import org.geysermc.geyser.entity.properties.GeyserEntityPropertyManager;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.scoreboard.Team;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.cloudburstmc.protocol.bedrock.packet.ModalFormResponsePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.NetworkSettingsPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.RequestNetworkSettingsPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackChunkDataPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackChunkRequestPacket;
|
||||
@@ -64,6 +65,7 @@ import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.PendingMicrosoftAuthentication;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.LoginEncryptionUtils;
|
||||
import org.geysermc.geyser.util.MathUtils;
|
||||
@@ -94,6 +96,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
}
|
||||
|
||||
private PacketSignal translateAndDefault(BedrockPacket packet) {
|
||||
if (packet instanceof PlayerAuthInputPacket) {
|
||||
//System.out.println(packet);
|
||||
} else {
|
||||
System.out.println(ChatColor.toANSI(ChatColor.GREEN) + packet + ChatColor.ANSI_RESET);
|
||||
}
|
||||
Registries.BEDROCK_PACKET_TRANSLATORS.translate(packet.getClass(), packet, session);
|
||||
return PacketSignal.HANDLED; // PacketSignal.UNHANDLED will log a WARN publicly
|
||||
}
|
||||
|
||||
@@ -1371,14 +1371,28 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
}
|
||||
|
||||
public void setSwimming(boolean swimming) {
|
||||
if (swimming) {
|
||||
if (!swimming && playerEntity.getFlag(EntityFlag.CRAWLING)) {
|
||||
// Do not update bounding box.
|
||||
playerEntity.setFlag(EntityFlag.SWIMMING, false);
|
||||
playerEntity.updateBedrockMetadata();
|
||||
return;
|
||||
}
|
||||
toggleSwimmingPose(swimming, EntityFlag.SWIMMING);
|
||||
}
|
||||
|
||||
public void setCrawling(boolean crawling) {
|
||||
toggleSwimmingPose(crawling, EntityFlag.CRAWLING);
|
||||
}
|
||||
|
||||
private void toggleSwimmingPose(boolean crawling, EntityFlag flag) {
|
||||
if (crawling) {
|
||||
this.pose = Pose.SWIMMING;
|
||||
playerEntity.setBoundingBoxHeight(0.6f);
|
||||
} else {
|
||||
this.pose = Pose.STANDING;
|
||||
playerEntity.setBoundingBoxHeight(playerEntity.getDefinition().height());
|
||||
}
|
||||
playerEntity.setFlag(EntityFlag.SWIMMING, swimming);
|
||||
playerEntity.setFlag(flag, crawling);
|
||||
playerEntity.updateBedrockMetadata();
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@ public class BedrockAnimateTranslator extends PacketTranslator<AnimatePacket> {
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("wewewewewewewewewewewe");
|
||||
if (packet.getAction() == AnimatePacket.Action.SWING_ARM) {
|
||||
session.armSwingPending();
|
||||
// Delay so entity damage can be processed first
|
||||
|
||||
@@ -31,14 +31,10 @@ import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.PlayerActionType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.PlayerBlockActionData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
@@ -49,7 +45,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.SkullCache;
|
||||
import org.geysermc.geyser.translator.item.CustomItemTranslator;
|
||||
import org.geysermc.geyser.util.BlockUtils;
|
||||
import org.geysermc.geyser.util.CooldownUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
@@ -57,24 +52,21 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractActio
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
final class BedrockBlockActions {
|
||||
|
||||
static void translate(GeyserSession session, List<PlayerBlockActionData> playerActions) {
|
||||
SessionPlayerEntity entity = session.getPlayerEntity();
|
||||
|
||||
// Send book update before any player action
|
||||
session.getBookEditCache().checkForSend();
|
||||
|
||||
for (PlayerBlockActionData blockActionData : playerActions) {
|
||||
handle(session, entity, blockActionData);
|
||||
handle(session, blockActionData);
|
||||
}
|
||||
}
|
||||
|
||||
private static void handle(GeyserSession session, SessionPlayerEntity entity, PlayerBlockActionData blockActionData) {
|
||||
private static void handle(GeyserSession session, PlayerBlockActionData blockActionData) {
|
||||
PlayerActionType action = blockActionData.getAction();
|
||||
Vector3i vector = blockActionData.getBlockPosition();
|
||||
int blockFace = blockActionData.getFace();
|
||||
@@ -198,34 +190,6 @@ final class BedrockBlockActions {
|
||||
// Handled in BedrockInventoryTransactionTranslator
|
||||
case STOP_BREAK -> {
|
||||
}
|
||||
case DIMENSION_CHANGE_SUCCESS -> {
|
||||
//sometimes the client doesn't feel like loading
|
||||
PlayStatusPacket spawnPacket = new PlayStatusPacket();
|
||||
spawnPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
|
||||
session.sendUpstreamPacket(spawnPacket);
|
||||
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
attributesPacket.getAttributes().addAll(entity.getAttributes().values());
|
||||
session.sendUpstreamPacket(attributesPacket);
|
||||
}
|
||||
case MISSED_SWING -> {
|
||||
// Java edition sends a cooldown when hitting air.
|
||||
// Normally handled by BedrockLevelSoundEventTranslator, but there is no sound on Java for this.
|
||||
CooldownUtils.sendCooldown(session);
|
||||
|
||||
// TODO Re-evaluate after pre-1.20.10 is no longer supported?
|
||||
if (session.getArmAnimationTicks() == -1) {
|
||||
session.sendDownstreamGamePacket(new ServerboundSwingPacket(Hand.MAIN_HAND));
|
||||
session.activateArmAnimationTicking();
|
||||
|
||||
// Send packet to Bedrock so it knows
|
||||
AnimatePacket animatePacket = new AnimatePacket();
|
||||
animatePacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
||||
animatePacket.setAction(AnimatePacket.Action.SWING_ARM);
|
||||
session.sendUpstreamPacket(animatePacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ package org.geysermc.geyser.translator.protocol.bedrock.entity.player;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayerActionPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
@@ -103,6 +104,18 @@ public class BedrockPlayerActionTranslator extends PacketTranslator<PlayerAction
|
||||
session.sendDownstreamGamePacket(interactPacket);
|
||||
}
|
||||
}
|
||||
case DIMENSION_CHANGE_SUCCESS -> {
|
||||
SessionPlayerEntity entity = session.getPlayerEntity();
|
||||
// Sometimes the client doesn't feel like loading
|
||||
PlayStatusPacket spawnPacket = new PlayStatusPacket();
|
||||
spawnPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
|
||||
session.sendUpstreamPacket(spawnPacket);
|
||||
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
attributesPacket.getAttributes().addAll(entity.getAttributes().values());
|
||||
session.sendUpstreamPacket(attributesPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.geyser.translator.protocol.bedrock.BedrockInventoryTransactionTranslator;
|
||||
import org.geysermc.geyser.util.CooldownUtils;
|
||||
import org.geysermc.geyser.util.MathUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
@@ -65,18 +66,23 @@ import java.util.Set;
|
||||
|
||||
@Translator(packet = PlayerAuthInputPacket.class)
|
||||
public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<PlayerAuthInputPacket> {
|
||||
private Set<PlayerAuthInputData> data = Set.of();
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, PlayerAuthInputPacket packet) {
|
||||
if (!data.equals(packet.getInputData())) {
|
||||
System.out.println(data);
|
||||
this.data = packet.getInputData();
|
||||
}
|
||||
SessionPlayerEntity entity = session.getPlayerEntity();
|
||||
|
||||
boolean wasJumping = session.getInputCache().wasJumping();
|
||||
session.getInputCache().processInputs(packet);
|
||||
|
||||
processVehicleInput(session, packet, wasJumping);
|
||||
|
||||
BedrockMovePlayerTranslator.translate(session, packet);
|
||||
|
||||
processVehicleInput(session, packet, wasJumping);
|
||||
|
||||
Set<PlayerAuthInputData> inputData = packet.getInputData();
|
||||
for (PlayerAuthInputData input : inputData) {
|
||||
switch (input) {
|
||||
@@ -110,6 +116,8 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
}
|
||||
case START_SWIMMING -> session.setSwimming(true);
|
||||
case STOP_SWIMMING -> session.setSwimming(false);
|
||||
case START_CRAWLING -> session.setCrawling(true);
|
||||
case STOP_CRAWLING -> session.setCrawling(false);
|
||||
case START_FLYING -> { // Since 1.20.30
|
||||
if (session.isCanFly()) {
|
||||
if (session.getGameMode() == GameMode.SPECTATOR) {
|
||||
@@ -151,6 +159,7 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
sendPlayerGlideToggle(session, entity);
|
||||
}
|
||||
case STOP_GLIDING -> sendPlayerGlideToggle(session, entity);
|
||||
case MISSED_SWING -> CooldownUtils.sendCooldown(session); // Java edition sends a cooldown when hitting air.
|
||||
}
|
||||
}
|
||||
if (entity.getVehicle() instanceof BoatEntity) {
|
||||
@@ -249,7 +258,7 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
if (currentJumpingTicks < 0) {
|
||||
session.getInputCache().setJumpingTicks(++currentJumpingTicks);
|
||||
if (currentJumpingTicks == 0) {
|
||||
session.getPlayerEntity().setVehicleJumpStrength(0);
|
||||
session.getInputCache().setJumpScale(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import net.kyori.adventure.key.Key;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.RecipeUnlockingRequirement;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapedRecipeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapelessRecipeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.SmithingTransformRecipeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.DefaultDescriptor;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount;
|
||||
@@ -81,8 +82,6 @@ public class JavaRecipeBookAddTranslator extends PacketTranslator<ClientboundRec
|
||||
int netId = session.getLastRecipeNetId().get();
|
||||
Int2ObjectMap<List<String>> javaToBedrockRecipeIds = session.getJavaToBedrockRecipeIds();
|
||||
CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
|
||||
// Check if we should set cleanRecipes here or not.
|
||||
|
||||
|
||||
UnlockedRecipesPacket recipesPacket = new UnlockedRecipesPacket();
|
||||
recipesPacket.setAction(packet.isReplace() ? UnlockedRecipesPacket.ActionType.INITIALLY_UNLOCKED : UnlockedRecipesPacket.ActionType.NEWLY_UNLOCKED);
|
||||
@@ -94,78 +93,42 @@ public class JavaRecipeBookAddTranslator extends PacketTranslator<ClientboundRec
|
||||
switch (display.getType()) {
|
||||
case CRAFTING_SHAPED -> {
|
||||
ShapedCraftingRecipeDisplay shapedRecipe = (ShapedCraftingRecipeDisplay) display;
|
||||
Pair<Item, ItemData> pair = translateToOutput(session, shapedRecipe.result());
|
||||
if (pair == null || !pair.right().isValid()) {
|
||||
// Likely modded item Bedrock will complain about
|
||||
var bedrockRecipes = combinations(session, display, shapedRecipe.ingredients());
|
||||
if (bedrockRecipes == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ItemData output = pair.right();
|
||||
if (!(pair.left() instanceof BedrockRequiresTagItem)) {
|
||||
// Strip NBT - tools won't appear in the recipe book otherwise
|
||||
output = output.toBuilder().tag(null).build();
|
||||
List<String> bedrockRecipeIds = new ArrayList<>();
|
||||
ItemData output = bedrockRecipes.right();
|
||||
List<List<ItemDescriptorWithCount>> left = bedrockRecipes.left();
|
||||
for (int i = 0; i < left.size(); i++) {
|
||||
List<ItemDescriptorWithCount> inputs = left.get(i);
|
||||
String recipeId = contents.id() + "_" + i;
|
||||
craftingDataPacket.getCraftingData().add(ShapedRecipeData.shaped(recipeId,
|
||||
shapedRecipe.width(), shapedRecipe.height(), inputs,
|
||||
Collections.singletonList(output), UUID.randomUUID(), "crafting_table", 0, netId++, false, RecipeUnlockingRequirement.INVALID));
|
||||
recipesPacket.getUnlockedRecipes().add(recipeId);
|
||||
bedrockRecipeIds.add(recipeId);
|
||||
}
|
||||
|
||||
boolean empty = true;
|
||||
boolean complexInputs = false;
|
||||
List<List<ItemDescriptorWithCount>> inputs = new ArrayList<>(shapedRecipe.ingredients().size());
|
||||
for (SlotDisplay input : shapedRecipe.ingredients()) {
|
||||
List<ItemDescriptorWithCount> translated = translateToInput(session, input);
|
||||
if (translated == null) {
|
||||
continue;
|
||||
}
|
||||
inputs.add(translated);
|
||||
if (translated.size() != 1 || translated.get(0) != ItemDescriptorWithCount.EMPTY) {
|
||||
empty = false;
|
||||
}
|
||||
complexInputs |= translated.size() > 1;
|
||||
}
|
||||
if (empty) {
|
||||
// Crashes Bedrock 1.19.70 otherwise
|
||||
// Fixes https://github.com/GeyserMC/Geyser/issues/3549
|
||||
continue;
|
||||
}
|
||||
|
||||
if (complexInputs) {
|
||||
System.out.println(inputs);
|
||||
if (true) continue;
|
||||
List<List<ItemDescriptorWithCount>> processedInputs = Lists.cartesianProduct(inputs);
|
||||
System.out.println(processedInputs.size());
|
||||
if (processedInputs.size() <= 500) { // Do not let us process giant lists.
|
||||
List<String> bedrockRecipeIds = new ArrayList<>();
|
||||
for (int i = 0; i < processedInputs.size(); i++) {
|
||||
List<ItemDescriptorWithCount> possibleInput = processedInputs.get(i);
|
||||
String recipeId = contents.id() + "_" + i;
|
||||
craftingDataPacket.getCraftingData().add(ShapedRecipeData.shaped(recipeId,
|
||||
shapedRecipe.width(), shapedRecipe.height(), possibleInput,
|
||||
Collections.singletonList(output), UUID.randomUUID(), "crafting_table", 0, netId++, false, RecipeUnlockingRequirement.INVALID));
|
||||
recipesPacket.getUnlockedRecipes().add(recipeId);
|
||||
bedrockRecipeIds.add(recipeId);
|
||||
}
|
||||
javaToBedrockRecipeIds.put(contents.id(), bedrockRecipeIds);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
String recipeId = Integer.toString(contents.id());
|
||||
craftingDataPacket.getCraftingData().add(ShapedRecipeData.shaped(recipeId,
|
||||
shapedRecipe.width(), shapedRecipe.height(), inputs.stream().map(descriptors -> descriptors.get(0)).toList(),
|
||||
Collections.singletonList(output), UUID.randomUUID(), "crafting_table", 0, netId++, false, RecipeUnlockingRequirement.INVALID));
|
||||
recipesPacket.getUnlockedRecipes().add(recipeId);
|
||||
javaToBedrockRecipeIds.put(contents.id(), Collections.singletonList(recipeId));
|
||||
javaToBedrockRecipeIds.put(contents.id(), List.copyOf(bedrockRecipeIds));
|
||||
}
|
||||
case CRAFTING_SHAPELESS -> {
|
||||
ShapelessCraftingRecipeDisplay shapelessRecipe = (ShapelessCraftingRecipeDisplay) display;
|
||||
Pair<Item, ItemData> pair = translateToOutput(session, shapelessRecipe.result());
|
||||
if (pair == null || !pair.right().isValid()) {
|
||||
// Likely modded item Bedrock will complain about
|
||||
var bedrockRecipes = combinations(session, display, shapelessRecipe.ingredients());
|
||||
if (bedrockRecipes == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ItemData output = pair.right();
|
||||
if (!(pair.left() instanceof BedrockRequiresTagItem)) {
|
||||
// Strip NBT - tools won't appear in the recipe book otherwise
|
||||
output = output.toBuilder().tag(null).build();
|
||||
List<String> bedrockRecipeIds = new ArrayList<>();
|
||||
ItemData output = bedrockRecipes.right();
|
||||
List<List<ItemDescriptorWithCount>> left = bedrockRecipes.left();
|
||||
for (int i = 0; i < left.size(); i++) {
|
||||
List<ItemDescriptorWithCount> inputs = left.get(i);
|
||||
String recipeId = contents.id() + "_" + i;
|
||||
craftingDataPacket.getCraftingData().add(ShapelessRecipeData.shapeless(recipeId,
|
||||
inputs, Collections.singletonList(output), UUID.randomUUID(), "crafting_table", 0, netId++, RecipeUnlockingRequirement.INVALID));
|
||||
recipesPacket.getUnlockedRecipes().add(recipeId);
|
||||
bedrockRecipeIds.add(recipeId);
|
||||
}
|
||||
javaToBedrockRecipeIds.put(contents.id(), List.copyOf(bedrockRecipeIds));
|
||||
}
|
||||
case SMITHING -> {
|
||||
if (true) {
|
||||
@@ -299,4 +262,59 @@ public class JavaRecipeBookAddTranslator extends PacketTranslator<ClientboundRec
|
||||
ItemMapping mapping = session.getItemMappings().getMapping(item);
|
||||
return new ItemDescriptorWithCount(new DefaultDescriptor(mapping.getBedrockDefinition(), mapping.getBedrockData()), 1); // Need to check count
|
||||
}
|
||||
|
||||
private Pair<List<List<ItemDescriptorWithCount>>, ItemData> combinations(GeyserSession session, RecipeDisplay display, List<SlotDisplay> ingredients) {
|
||||
Pair<Item, ItemData> pair = translateToOutput(session, display.result());
|
||||
if (pair == null || !pair.right().isValid()) {
|
||||
// Likely modded item Bedrock will complain about
|
||||
return null;
|
||||
}
|
||||
|
||||
ItemData output = pair.right();
|
||||
if (!(pair.left() instanceof BedrockRequiresTagItem)) {
|
||||
// Strip NBT - tools won't appear in the recipe book otherwise
|
||||
output = output.toBuilder().tag(null).build();
|
||||
}
|
||||
|
||||
boolean empty = true;
|
||||
boolean complexInputs = false;
|
||||
List<List<ItemDescriptorWithCount>> inputs = new ArrayList<>(ingredients.size());
|
||||
for (SlotDisplay input : ingredients) {
|
||||
List<ItemDescriptorWithCount> translated = translateToInput(session, input);
|
||||
if (translated == null) {
|
||||
continue;
|
||||
}
|
||||
inputs.add(translated);
|
||||
if (translated.size() != 1 || translated.get(0) != ItemDescriptorWithCount.EMPTY) {
|
||||
empty = false;
|
||||
}
|
||||
complexInputs |= translated.size() > 1;
|
||||
}
|
||||
if (empty) {
|
||||
// Crashes Bedrock 1.19.70 otherwise
|
||||
// Fixes https://github.com/GeyserMC/Geyser/issues/3549
|
||||
return null;
|
||||
}
|
||||
|
||||
if (complexInputs) {
|
||||
System.out.println(inputs);
|
||||
long size = 1;
|
||||
// See how big a cartesian product will get without creating one (Guava throws an error; not really ideal)
|
||||
for (List<ItemDescriptorWithCount> list : inputs) {
|
||||
size *= list.size();
|
||||
if (size > 500) {
|
||||
// Too much. No.
|
||||
complexInputs = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (complexInputs) {
|
||||
return Pair.of(Lists.cartesianProduct(inputs), output);
|
||||
}
|
||||
}
|
||||
return Pair.of(
|
||||
Collections.singletonList(inputs.stream().map(descriptors -> descriptors.get(0)).toList()),
|
||||
output
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user