From 8928d554a1a8dde5e4d826afbb809b57358b409f Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Sun, 3 Jan 2021 17:54:26 -0900 Subject: [PATCH] WIP autocrafting using java recipe book work in progress. many edge cases are currently unhandled. will not work at all pre 1.12. (support is planned) --- .../network/session/GeyserSession.java | 3 + .../inventory/InventoryTranslator.java | 86 ++++++++++++++----- .../translators/inventory/click/Click.java | 6 +- .../inventory/click/ClickPlan.java | 16 +++- .../java/JavaDeclareRecipesTranslator.java | 1 + .../java/JavaUnlockRecipesTranslator.java | 48 +++++++++++ 6 files changed, 130 insertions(+), 30 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/JavaUnlockRecipesTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 10a58360c..e2587ee08 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -61,6 +61,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2LongMap; import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectIterator; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import lombok.AccessLevel; import lombok.Getter; import lombok.NonNull; @@ -230,6 +231,7 @@ public class GeyserSession implements CommandSender { @Setter private Int2ObjectMap craftingRecipes; + private final Set unlockedRecipes; /** * Saves a list of all stonecutter recipes, for use in a stonecutter inventory. @@ -382,6 +384,7 @@ public class GeyserSession implements CommandSender { this.openInventory = null; this.inventoryFuture = CompletableFuture.completedFuture(null); this.craftingRecipes = new Int2ObjectOpenHashMap<>(); + this.unlockedRecipes = new ObjectOpenHashSet<>(); this.spawned = false; this.loggedIn = false; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java index ffc8d02fa..bf2fb36cf 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java @@ -27,8 +27,12 @@ package org.geysermc.connector.network.translators.inventory; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; +import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; +import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData; import com.github.steveice10.mc.protocol.data.game.window.WindowType; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientPrepareCraftingGridPacket; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; @@ -552,7 +556,8 @@ public abstract class InventoryTranslator { int recipeId = 0; int resultSize = 0; - boolean autoCraft; + int timesCrafted = 0; + boolean autoCraft = false; CraftState craftState = CraftState.START; int leftover = 0; @@ -566,28 +571,30 @@ public abstract class InventoryTranslator { } craftState = CraftState.RECIPE_ID; recipeId = craftAction.getRecipeNetworkId(); - //System.out.println(session.getCraftingRecipes().get(recipeId)); autoCraft = false; break; } -// case CRAFT_RECIPE_AUTO: { -// AutoCraftRecipeStackRequestActionData autoCraftAction = (AutoCraftRecipeStackRequestActionData) action; -// if (craftState != CraftState.START) { -// return rejectRequest(request); -// } -// craftState = CraftState.RECIPE_ID; -// recipeId = autoCraftAction.getRecipeNetworkId(); -// Recipe recipe = session.getCraftingRecipes().get(recipeId); -// System.out.println(recipe); -// if (recipe == null) { -// return rejectRequest(request); -// } -//// ClientPrepareCraftingGridPacket packet = new ClientPrepareCraftingGridPacket(session.getOpenInventory().getId(), recipe.getIdentifier(), true); -//// session.sendDownstreamPacket(packet); -// autoCraft = true; -// //TODO: reject transaction if crafting grid is not clear -// break; -// } + case CRAFT_RECIPE_AUTO: { + AutoCraftRecipeStackRequestActionData autoCraftAction = (AutoCraftRecipeStackRequestActionData) action; + if (craftState != CraftState.START) { + return rejectRequest(request); + } + craftState = CraftState.RECIPE_ID; + + recipeId = autoCraftAction.getRecipeNetworkId(); + if (!plan.getCursor().isEmpty()) { + return rejectRequest(request); + } + //reject if crafting grid is not clear + int gridSize = inventory.getId() == 0 ? 4 : 9; + for (int i = 1; i <= gridSize; i++) { + if (!inventory.getItem(i).isEmpty()) { + return rejectRequest(request); + } + } + autoCraft = true; + break; + } case CRAFT_RESULTS_DEPRECATED: { CraftResultsDeprecatedStackRequestActionData deprecatedCraftAction = (CraftResultsDeprecatedStackRequestActionData) action; if (craftState != CraftState.RECIPE_ID) { @@ -599,7 +606,8 @@ public abstract class InventoryTranslator { return rejectRequest(request); } resultSize = deprecatedCraftAction.getResultItems()[0].getCount(); - if (resultSize <= 0) { + timesCrafted = deprecatedCraftAction.getTimesCrafted(); + if (resultSize <= 0 || timesCrafted <= 0) { return rejectRequest(request); } break; @@ -628,11 +636,45 @@ public abstract class InventoryTranslator { } int sourceSlot = bedrockSlotToJava(transferAction.getSource()); + int destSlot = bedrockSlotToJava(transferAction.getDestination()); + + if (autoCraft) { + Recipe recipe = session.getCraftingRecipes().get(recipeId); + //cannot use java recipe book if recipe is locked + if (recipe == null || !session.getUnlockedRecipes().contains(recipe.getIdentifier())) { + return rejectRequest(request); + } + + boolean cursorDest = isCursor(transferAction.getDestination()); + boolean makeAll = timesCrafted > 1; + if (cursorDest) { + makeAll = false; + } + + ClientPrepareCraftingGridPacket prepareCraftingPacket = new ClientPrepareCraftingGridPacket(inventory.getId(), recipe.getIdentifier(), makeAll); + session.sendDownstreamPacket(prepareCraftingPacket); + + ItemStack output = null; + switch (recipe.getType()) { + case CRAFTING_SHAPED: + output = ((ShapedRecipeData)recipe.getData()).getResult(); + break; + case CRAFTING_SHAPELESS: + output = ((ShapelessRecipeData)recipe.getData()).getResult(); + break; + } + inventory.setItem(0, GeyserItemStack.from(output), session); + + plan.add(cursorDest ? Click.LEFT : Click.LEFT_SHIFT, 0); + plan.execute(true); + + return acceptRequest(request, makeContainerEntries(session, inventory, Collections.emptySet())); + } + if (isCursor(transferAction.getDestination())) { plan.add(Click.LEFT, sourceSlot); craftState = CraftState.DONE; } else { - int destSlot = bedrockSlotToJava(transferAction.getDestination()); if (leftover != 0) { if (transferAction.getCount() > leftover) { return rejectRequest(request); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/Click.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/Click.java index fe4ac8bf9..d3666a9e9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/Click.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/Click.java @@ -25,16 +25,14 @@ package org.geysermc.connector.network.translators.inventory.click; -import com.github.steveice10.mc.protocol.data.game.window.ClickItemParam; -import com.github.steveice10.mc.protocol.data.game.window.DropItemParam; -import com.github.steveice10.mc.protocol.data.game.window.WindowAction; -import com.github.steveice10.mc.protocol.data.game.window.WindowActionParam; +import com.github.steveice10.mc.protocol.data.game.window.*; import lombok.AllArgsConstructor; @AllArgsConstructor public enum Click { LEFT(WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK), RIGHT(WindowAction.CLICK_ITEM, ClickItemParam.RIGHT_CLICK), + LEFT_SHIFT(WindowAction.SHIFT_CLICK_ITEM, ShiftClickItemParam.LEFT_CLICK), DROP_ONE(WindowAction.DROP_ITEM, DropItemParam.DROP_FROM_SELECTED), DROP_ALL(WindowAction.DROP_ITEM, DropItemParam.DROP_SELECTED_STACK), LEFT_OUTSIDE(WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK), diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java index 830a9ce0b..9268cbf12 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java @@ -166,10 +166,15 @@ public class ClickPlan { GeyserItemStack clicked = simulating ? getItem(action.slot) : inventory.getItem(action.slot); if (translator.getSlotType(action.slot) == SlotType.OUTPUT) { - if (cursor.isEmpty() && !clicked.isEmpty()) { - setCursor(clicked.copy()); - } else if (InventoryUtils.canStack(cursor, clicked)) { - cursor.add(clicked.getAmount()); + switch (action.click) { + case LEFT: + case RIGHT: + if (cursor.isEmpty() && !clicked.isEmpty()) { + setCursor(clicked.copy()); + } else if (InventoryUtils.canStack(cursor, clicked)) { + cursor.add(clicked.getAmount()); + } + break; } } else { switch (action.click) { @@ -195,6 +200,9 @@ public class ClickPlan { clicked.add(1); } break; + case LEFT_SHIFT: + //TODO + break; case DROP_ONE: if (!clicked.isEmpty()) { clicked.sub(1); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java index ed0abe108..31726cc5c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java @@ -183,6 +183,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator { + + @Override + public void translate(ServerUnlockRecipesPacket packet, GeyserSession session) { + if (packet.getAction() == UnlockRecipesAction.REMOVE) { + session.getUnlockedRecipes().removeAll(Arrays.asList(packet.getRecipes())); + } else { + session.getUnlockedRecipes().addAll(Arrays.asList(packet.getRecipes())); + } + } +} +