mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00: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)
This commit is contained in:
parent
528a9a4431
commit
8928d554a1
6 changed files with 130 additions and 30 deletions
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -166,11 +166,16 @@ 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) {
|
||||||
|
switch (action.click) {
|
||||||
|
case LEFT:
|
||||||
|
case RIGHT:
|
||||||
if (cursor.isEmpty() && !clicked.isEmpty()) {
|
if (cursor.isEmpty() && !clicked.isEmpty()) {
|
||||||
setCursor(clicked.copy());
|
setCursor(clicked.copy());
|
||||||
} else if (InventoryUtils.canStack(cursor, clicked)) {
|
} else if (InventoryUtils.canStack(cursor, clicked)) {
|
||||||
cursor.add(clicked.getAmount());
|
cursor.add(clicked.getAmount());
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (action.click) {
|
switch (action.click) {
|
||||||
case LEFT:
|
case LEFT:
|
||||||
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue