3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-12-27 08:30:12 +01:00

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)
Dieser Commit ist enthalten in:
AJ Ferguson 2021-01-03 17:54:26 -09:00
Ursprung 528a9a4431
Commit 8928d554a1
6 geänderte Dateien mit 130 neuen und 30 gelöschten Zeilen

Datei anzeigen

@ -61,6 +61,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2LongMap; import it.unimi.dsi.fastutil.objects.Object2LongMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator; import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
@ -230,6 +231,7 @@ public class GeyserSession implements CommandSender {
@Setter @Setter
private Int2ObjectMap<Recipe> craftingRecipes; private Int2ObjectMap<Recipe> craftingRecipes;
private final Set<String> unlockedRecipes;
/** /**
* Saves a list of all stonecutter recipes, for use in a stonecutter inventory. * 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.openInventory = null;
this.inventoryFuture = CompletableFuture.completedFuture(null); this.inventoryFuture = CompletableFuture.completedFuture(null);
this.craftingRecipes = new Int2ObjectOpenHashMap<>(); this.craftingRecipes = new Int2ObjectOpenHashMap<>();
this.unlockedRecipes = new ObjectOpenHashSet<>();
this.spawned = false; this.spawned = false;
this.loggedIn = false; this.loggedIn = false;

Datei anzeigen

@ -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.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; 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.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.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.ContainerSlotType;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
@ -552,7 +556,8 @@ public abstract class InventoryTranslator {
int recipeId = 0; int recipeId = 0;
int resultSize = 0; int resultSize = 0;
boolean autoCraft; int timesCrafted = 0;
boolean autoCraft = false;
CraftState craftState = CraftState.START; CraftState craftState = CraftState.START;
int leftover = 0; int leftover = 0;
@ -566,28 +571,30 @@ public abstract class InventoryTranslator {
} }
craftState = CraftState.RECIPE_ID; craftState = CraftState.RECIPE_ID;
recipeId = craftAction.getRecipeNetworkId(); recipeId = craftAction.getRecipeNetworkId();
//System.out.println(session.getCraftingRecipes().get(recipeId));
autoCraft = false; autoCraft = false;
break; break;
} }
// case CRAFT_RECIPE_AUTO: { case CRAFT_RECIPE_AUTO: {
// AutoCraftRecipeStackRequestActionData autoCraftAction = (AutoCraftRecipeStackRequestActionData) action; AutoCraftRecipeStackRequestActionData autoCraftAction = (AutoCraftRecipeStackRequestActionData) action;
// if (craftState != CraftState.START) { if (craftState != CraftState.START) {
// return rejectRequest(request); return rejectRequest(request);
// } }
// craftState = CraftState.RECIPE_ID; craftState = CraftState.RECIPE_ID;
// recipeId = autoCraftAction.getRecipeNetworkId();
// Recipe recipe = session.getCraftingRecipes().get(recipeId); recipeId = autoCraftAction.getRecipeNetworkId();
// System.out.println(recipe); if (!plan.getCursor().isEmpty()) {
// if (recipe == null) { return rejectRequest(request);
// return rejectRequest(request); }
// } //reject if crafting grid is not clear
//// ClientPrepareCraftingGridPacket packet = new ClientPrepareCraftingGridPacket(session.getOpenInventory().getId(), recipe.getIdentifier(), true); int gridSize = inventory.getId() == 0 ? 4 : 9;
//// session.sendDownstreamPacket(packet); for (int i = 1; i <= gridSize; i++) {
// autoCraft = true; if (!inventory.getItem(i).isEmpty()) {
// //TODO: reject transaction if crafting grid is not clear return rejectRequest(request);
// break; }
// } }
autoCraft = true;
break;
}
case CRAFT_RESULTS_DEPRECATED: { case CRAFT_RESULTS_DEPRECATED: {
CraftResultsDeprecatedStackRequestActionData deprecatedCraftAction = (CraftResultsDeprecatedStackRequestActionData) action; CraftResultsDeprecatedStackRequestActionData deprecatedCraftAction = (CraftResultsDeprecatedStackRequestActionData) action;
if (craftState != CraftState.RECIPE_ID) { if (craftState != CraftState.RECIPE_ID) {
@ -599,7 +606,8 @@ public abstract class InventoryTranslator {
return rejectRequest(request); return rejectRequest(request);
} }
resultSize = deprecatedCraftAction.getResultItems()[0].getCount(); resultSize = deprecatedCraftAction.getResultItems()[0].getCount();
if (resultSize <= 0) { timesCrafted = deprecatedCraftAction.getTimesCrafted();
if (resultSize <= 0 || timesCrafted <= 0) {
return rejectRequest(request); return rejectRequest(request);
} }
break; break;
@ -628,11 +636,45 @@ public abstract class InventoryTranslator {
} }
int sourceSlot = bedrockSlotToJava(transferAction.getSource()); 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())) { if (isCursor(transferAction.getDestination())) {
plan.add(Click.LEFT, sourceSlot); plan.add(Click.LEFT, sourceSlot);
craftState = CraftState.DONE; craftState = CraftState.DONE;
} else { } else {
int destSlot = bedrockSlotToJava(transferAction.getDestination());
if (leftover != 0) { if (leftover != 0) {
if (transferAction.getCount() > leftover) { if (transferAction.getCount() > leftover) {
return rejectRequest(request); return rejectRequest(request);

Datei anzeigen

@ -25,16 +25,14 @@
package org.geysermc.connector.network.translators.inventory.click; 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.*;
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 lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
public enum Click { public enum Click {
LEFT(WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK), LEFT(WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK),
RIGHT(WindowAction.CLICK_ITEM, ClickItemParam.RIGHT_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_ONE(WindowAction.DROP_ITEM, DropItemParam.DROP_FROM_SELECTED),
DROP_ALL(WindowAction.DROP_ITEM, DropItemParam.DROP_SELECTED_STACK), DROP_ALL(WindowAction.DROP_ITEM, DropItemParam.DROP_SELECTED_STACK),
LEFT_OUTSIDE(WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK), LEFT_OUTSIDE(WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK),

Datei anzeigen

@ -166,10 +166,15 @@ public class ClickPlan {
GeyserItemStack clicked = simulating ? getItem(action.slot) : inventory.getItem(action.slot); GeyserItemStack clicked = simulating ? getItem(action.slot) : inventory.getItem(action.slot);
if (translator.getSlotType(action.slot) == SlotType.OUTPUT) { if (translator.getSlotType(action.slot) == SlotType.OUTPUT) {
if (cursor.isEmpty() && !clicked.isEmpty()) { switch (action.click) {
setCursor(clicked.copy()); case LEFT:
} else if (InventoryUtils.canStack(cursor, clicked)) { case RIGHT:
cursor.add(clicked.getAmount()); if (cursor.isEmpty() && !clicked.isEmpty()) {
setCursor(clicked.copy());
} else if (InventoryUtils.canStack(cursor, clicked)) {
cursor.add(clicked.getAmount());
}
break;
} }
} else { } else {
switch (action.click) { switch (action.click) {
@ -195,6 +200,9 @@ public class ClickPlan {
clicked.add(1); clicked.add(1);
} }
break; break;
case LEFT_SHIFT:
//TODO
break;
case DROP_ONE: case DROP_ONE:
if (!clicked.isEmpty()) { if (!clicked.isEmpty()) {
clicked.sub(1); clicked.sub(1);

Datei anzeigen

@ -183,6 +183,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
session.sendUpstreamPacket(craftingDataPacket); session.sendUpstreamPacket(craftingDataPacket);
session.setCraftingRecipes(recipeMap); session.setCraftingRecipes(recipeMap);
session.getUnlockedRecipes().clear();
session.setStonecutterRecipes(stonecutterRecipeMap); session.setStonecutterRecipes(stonecutterRecipeMap);
} }

Datei anzeigen

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.network.translators.java;
import com.github.steveice10.mc.protocol.data.game.UnlockRecipesAction;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerUnlockRecipesPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import java.util.Arrays;
@Translator(packet = ServerUnlockRecipesPacket.class)
public class JavaUnlockRecipesTranslator extends PacketTranslator<ServerUnlockRecipesPacket> {
@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()));
}
}
}