mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Merge 9ff6c7da5d
into 4f7e9fca9c
This commit is contained in:
commit
2c2fe16434
4 changed files with 135 additions and 13 deletions
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.inventory.click;
|
package org.geysermc.geyser.inventory.click;
|
||||||
|
|
||||||
|
import org.geysermc.geyser.item.Items;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerActionType;
|
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerActionType;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||||
|
@ -58,11 +59,20 @@ public final class ClickPlan {
|
||||||
private final InventoryTranslator translator;
|
private final InventoryTranslator translator;
|
||||||
private final Inventory inventory;
|
private final Inventory inventory;
|
||||||
private final int gridSize;
|
private final int gridSize;
|
||||||
|
/**
|
||||||
|
* The recipe for cloning books requires special handling, this dictates whether that handling should be performed
|
||||||
|
*/
|
||||||
|
private final boolean handleBookCloneRecipe;
|
||||||
|
|
||||||
public ClickPlan(GeyserSession session, InventoryTranslator translator, Inventory inventory) {
|
public ClickPlan(GeyserSession session, InventoryTranslator translator, Inventory inventory) {
|
||||||
|
this(session, translator, inventory, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClickPlan(GeyserSession session, InventoryTranslator translator, Inventory inventory, boolean handleBookCloneRecipe) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.translator = translator;
|
this.translator = translator;
|
||||||
this.inventory = inventory;
|
this.inventory = inventory;
|
||||||
|
this.handleBookCloneRecipe = handleBookCloneRecipe;
|
||||||
|
|
||||||
this.simulatedItems = new Int2ObjectOpenHashMap<>(inventory.getSize());
|
this.simulatedItems = new Int2ObjectOpenHashMap<>(inventory.getSize());
|
||||||
this.changedItems = null;
|
this.changedItems = null;
|
||||||
|
@ -376,7 +386,7 @@ public final class ClickPlan {
|
||||||
for (int i = 0; i < gridSize; i++) {
|
for (int i = 0; i < gridSize; i++) {
|
||||||
final int slot = i + 1;
|
final int slot = i + 1;
|
||||||
GeyserItemStack item = getItem(slot);
|
GeyserItemStack item = getItem(slot);
|
||||||
if (!item.isEmpty()) {
|
if (!item.isEmpty() && (!handleBookCloneRecipe || item.asItem() == Items.WRITTEN_BOOK)) {
|
||||||
// These changes should be broadcasted to the server
|
// These changes should be broadcasted to the server
|
||||||
sub(slot, item, crafted);
|
sub(slot, item, crafted);
|
||||||
}
|
}
|
||||||
|
|
|
@ -448,6 +448,13 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||||
@Setter
|
@Setter
|
||||||
private Int2ObjectMap<GeyserStonecutterData> stonecutterRecipes;
|
private Int2ObjectMap<GeyserStonecutterData> stonecutterRecipes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the ID for cloning books through the crafting table, as these need different handling
|
||||||
|
*/
|
||||||
|
@Setter
|
||||||
|
private int bookCloningID;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to work around 1.13's different behavior in villager trading menus.
|
* Whether to work around 1.13's different behavior in villager trading menus.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -454,35 +454,46 @@ public abstract class InventoryTranslator {
|
||||||
ClickPlan plan = new ClickPlan(session, this, inventory);
|
ClickPlan plan = new ClickPlan(session, this, inventory);
|
||||||
// Track all the crafting table slots to report back the contents of the slots after crafting
|
// Track all the crafting table slots to report back the contents of the slots after crafting
|
||||||
IntSet affectedSlots = new IntOpenHashSet();
|
IntSet affectedSlots = new IntOpenHashSet();
|
||||||
|
boolean reject = false;
|
||||||
for (ItemStackRequestAction action : request.getActions()) {
|
for (ItemStackRequestAction action : request.getActions()) {
|
||||||
switch (action.getType()) {
|
switch (action.getType()) {
|
||||||
case CRAFT_RECIPE: {
|
case CRAFT_RECIPE: {
|
||||||
if (craftState != CraftState.START) {
|
if (craftState != CraftState.START) {
|
||||||
return rejectRequest(request);
|
reject = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
craftState = CraftState.RECIPE_ID;
|
craftState = CraftState.RECIPE_ID;
|
||||||
|
|
||||||
|
if (((RecipeItemStackRequestAction) action).getRecipeNetworkId() == session.getBookCloningID()) {
|
||||||
|
// Book copying needs to be handled differently
|
||||||
|
// The original written book is leftover in the crafting grid
|
||||||
|
return translateBookCopyCraftingRequest(session, inventory, request);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CRAFT_RESULTS_DEPRECATED: {
|
case CRAFT_RESULTS_DEPRECATED: {
|
||||||
CraftResultsDeprecatedAction deprecatedCraftAction = (CraftResultsDeprecatedAction) action;
|
CraftResultsDeprecatedAction deprecatedCraftAction = (CraftResultsDeprecatedAction) action;
|
||||||
if (craftState != CraftState.RECIPE_ID) {
|
if (craftState != CraftState.RECIPE_ID) {
|
||||||
return rejectRequest(request);
|
reject = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
craftState = CraftState.DEPRECATED;
|
craftState = CraftState.DEPRECATED;
|
||||||
|
|
||||||
if (deprecatedCraftAction.getResultItems().length != 1) {
|
if (deprecatedCraftAction.getResultItems().length != 1) {
|
||||||
return rejectRequest(request);
|
reject = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
resultSize = deprecatedCraftAction.getResultItems()[0].getCount();
|
resultSize = deprecatedCraftAction.getResultItems()[0].getCount();
|
||||||
timesCrafted = deprecatedCraftAction.getTimesCrafted();
|
timesCrafted = deprecatedCraftAction.getTimesCrafted();
|
||||||
if (resultSize <= 0 || timesCrafted <= 0) {
|
if (resultSize <= 0 || timesCrafted <= 0) {
|
||||||
return rejectRequest(request);
|
reject = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CONSUME: {
|
case CONSUME: {
|
||||||
if (craftState != CraftState.DEPRECATED && craftState != CraftState.INGREDIENTS) {
|
if (craftState != CraftState.DEPRECATED && craftState != CraftState.INGREDIENTS) {
|
||||||
return rejectRequest(request);
|
reject = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
craftState = CraftState.INGREDIENTS;
|
craftState = CraftState.INGREDIENTS;
|
||||||
affectedSlots.add(bedrockSlotToJava(((ConsumeAction) action).getSource()));
|
affectedSlots.add(bedrockSlotToJava(((ConsumeAction) action).getSource()));
|
||||||
|
@ -492,15 +503,18 @@ public abstract class InventoryTranslator {
|
||||||
case PLACE: {
|
case PLACE: {
|
||||||
TransferItemStackRequestAction transferAction = (TransferItemStackRequestAction) action;
|
TransferItemStackRequestAction transferAction = (TransferItemStackRequestAction) action;
|
||||||
if (craftState != CraftState.INGREDIENTS && craftState != CraftState.TRANSFER) {
|
if (craftState != CraftState.INGREDIENTS && craftState != CraftState.TRANSFER) {
|
||||||
return rejectRequest(request);
|
reject = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
craftState = CraftState.TRANSFER;
|
craftState = CraftState.TRANSFER;
|
||||||
|
|
||||||
if (transferAction.getSource().getContainer() != ContainerSlotType.CREATED_OUTPUT) {
|
if (transferAction.getSource().getContainer() != ContainerSlotType.CREATED_OUTPUT) {
|
||||||
return rejectRequest(request);
|
reject = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (transferAction.getCount() <= 0) {
|
if (transferAction.getCount() <= 0) {
|
||||||
return rejectRequest(request);
|
reject = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sourceSlot = bedrockSlotToJava(transferAction.getSource());
|
int sourceSlot = bedrockSlotToJava(transferAction.getSource());
|
||||||
|
@ -512,7 +526,8 @@ public abstract class InventoryTranslator {
|
||||||
} else {
|
} else {
|
||||||
if (leftover != 0) {
|
if (leftover != 0) {
|
||||||
if (transferAction.getCount() > leftover) {
|
if (transferAction.getCount() > leftover) {
|
||||||
return rejectRequest(request);
|
reject = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (transferAction.getCount() == leftover) {
|
if (transferAction.getCount() == leftover) {
|
||||||
plan.add(Click.LEFT, destSlot);
|
plan.add(Click.LEFT, destSlot);
|
||||||
|
@ -538,7 +553,8 @@ public abstract class InventoryTranslator {
|
||||||
GeyserItemStack cursor = session.getPlayerInventory().getCursor();
|
GeyserItemStack cursor = session.getPlayerInventory().getCursor();
|
||||||
int tempSlot = findTempSlot(inventory, cursor, true, sourceSlot, destSlot);
|
int tempSlot = findTempSlot(inventory, cursor, true, sourceSlot, destSlot);
|
||||||
if (tempSlot == -1) {
|
if (tempSlot == -1) {
|
||||||
return rejectRequest(request);
|
reject = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
plan.add(Click.LEFT, tempSlot); //place cursor into temp slot
|
plan.add(Click.LEFT, tempSlot); //place cursor into temp slot
|
||||||
|
@ -560,14 +576,101 @@ public abstract class InventoryTranslator {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return rejectRequest(request);
|
reject = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (reject) {
|
||||||
|
return rejectRequest(request);
|
||||||
}
|
}
|
||||||
plan.execute(false);
|
plan.execute(false);
|
||||||
affectedSlots.addAll(plan.getAffectedSlots());
|
affectedSlots.addAll(plan.getAffectedSlots());
|
||||||
return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots));
|
return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Book copying is unique in that there is an item remaining in the crafting table when done.
|
||||||
|
*/
|
||||||
|
public ItemStackResponse translateBookCopyCraftingRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) {
|
||||||
|
CraftState craftState = CraftState.START;
|
||||||
|
boolean newBookHandled = false;
|
||||||
|
|
||||||
|
ClickPlan plan = new ClickPlan(session, this, inventory, true);
|
||||||
|
for (ItemStackRequestAction action : request.getActions()) {
|
||||||
|
switch (action.getType()) {
|
||||||
|
case CRAFT_RECIPE -> {
|
||||||
|
if (craftState != CraftState.START) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
craftState = CraftState.RECIPE_ID;
|
||||||
|
}
|
||||||
|
case CRAFT_RESULTS_DEPRECATED -> {
|
||||||
|
CraftResultsDeprecatedAction deprecatedCraftAction = (CraftResultsDeprecatedAction) action;
|
||||||
|
if (craftState != CraftState.RECIPE_ID) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
craftState = CraftState.DEPRECATED;
|
||||||
|
|
||||||
|
if (deprecatedCraftAction.getResultItems().length != 2) {
|
||||||
|
// Crafted item and old book
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
int resultSize = deprecatedCraftAction.getResultItems()[0].getCount();
|
||||||
|
int timesCrafted = deprecatedCraftAction.getTimesCrafted();
|
||||||
|
if (resultSize != 1 || timesCrafted != 1) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case CONSUME -> {
|
||||||
|
// Ignore I guess
|
||||||
|
}
|
||||||
|
case CREATE -> {
|
||||||
|
// After the proper book is created this is called
|
||||||
|
}
|
||||||
|
case TAKE, PLACE -> {
|
||||||
|
TransferItemStackRequestAction transferAction = (TransferItemStackRequestAction) action;
|
||||||
|
if (craftState != CraftState.DEPRECATED) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newBookHandled) {
|
||||||
|
// Don't let this execute for the old book and keep it in its old slot
|
||||||
|
// Bedrock wants to move it to the inventory; don't let it
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transferAction.getSource().getContainer() != ContainerSlotType.CREATED_OUTPUT) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
if (transferAction.getCount() != 1) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sourceSlot = bedrockSlotToJava(transferAction.getSource());
|
||||||
|
int destSlot = bedrockSlotToJava(transferAction.getDestination());
|
||||||
|
|
||||||
|
// Books are pretty simple in this regard - we'll yeet the written book when we execute
|
||||||
|
// the click plan, but otherwise a book isn't stackable so there aren't many options for it
|
||||||
|
if (isCursor(transferAction.getDestination())) {
|
||||||
|
plan.add(Click.LEFT, sourceSlot);
|
||||||
|
} else {
|
||||||
|
plan.add(Click.LEFT, sourceSlot);
|
||||||
|
plan.add(Click.LEFT, destSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
newBookHandled = true;
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plan.execute(false);
|
||||||
|
return acceptRequest(request, makeContainerEntries(session, inventory, plan.getAffectedSlots()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public ItemStackResponse translateAutoCraftingRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) {
|
public ItemStackResponse translateAutoCraftingRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) {
|
||||||
final int gridSize = getGridSize();
|
final int gridSize = getGridSize();
|
||||||
if (gridSize == -1) {
|
if (gridSize == -1) {
|
||||||
|
|
|
@ -200,7 +200,9 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
|
||||||
craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("685a742a-c42e-4a4e-88ea-5eb83fc98e5b"), context.getAndIncrementNetId()));
|
craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("685a742a-c42e-4a4e-88ea-5eb83fc98e5b"), context.getAndIncrementNetId()));
|
||||||
}
|
}
|
||||||
case CRAFTING_SPECIAL_BOOKCLONING -> {
|
case CRAFTING_SPECIAL_BOOKCLONING -> {
|
||||||
craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("d1ca6b84-338e-4f2f-9c6b-76cc8b4bd98d"), context.getAndIncrementNetId()));
|
int bookCloningID = context.getAndIncrementNetId();
|
||||||
|
session.setBookCloningID(bookCloningID);
|
||||||
|
craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("d1ca6b84-338e-4f2f-9c6b-76cc8b4bd98d"), bookCloningID));
|
||||||
}
|
}
|
||||||
case CRAFTING_SPECIAL_REPAIRITEM -> {
|
case CRAFTING_SPECIAL_REPAIRITEM -> {
|
||||||
craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("00000000-0000-0000-0000-000000000001"), context.getAndIncrementNetId()));
|
craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("00000000-0000-0000-0000-000000000001"), context.getAndIncrementNetId()));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue