3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-11-19 14:30:17 +01:00

Proof of concept for book cloning fix

Dieser Commit ist enthalten in:
YHDiamond 2024-06-19 20:15:27 -04:00
Ursprung 34158f9463
Commit 8738f991ff
3 geänderte Dateien mit 125 neuen und 12 gelöschten Zeilen

Datei anzeigen

@ -58,11 +58,17 @@ 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;
private final boolean handleBookRecipe;
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 handleBookRecipe) {
this.session = session; this.session = session;
this.translator = translator; this.translator = translator;
this.inventory = inventory; this.inventory = inventory;
this.handleBookRecipe = handleBookRecipe;
this.simulatedItems = new Int2ObjectOpenHashMap<>(inventory.getSize()); this.simulatedItems = new Int2ObjectOpenHashMap<>(inventory.getSize());
this.changedItems = null; this.changedItems = null;
@ -376,7 +382,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() && (!handleBookRecipe || item.getJavaId() != session.getItemMappings().getStoredItems().writtenBook().getJavaItem().javaId())) {
// These changes should be broadcasted to the server // These changes should be broadcasted to the server
sub(slot, item, crafted); sub(slot, item, crafted);
} }

Datei anzeigen

@ -453,35 +453,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() == InventoryUtils.BOOK_CLONING_RECIPE_ID) {
// Book copying needs to be handled differently
// There's a leftover item
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()));
@ -491,15 +502,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());
@ -511,7 +525,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);
@ -537,7 +552,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
@ -559,14 +575,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) {

Datei anzeigen

@ -74,6 +74,10 @@ public class InventoryUtils {
* each recipe needs a unique network ID (or else in .200 the client crashes). * each recipe needs a unique network ID (or else in .200 the client crashes).
*/ */
public static int LAST_RECIPE_NET_ID; public static int LAST_RECIPE_NET_ID;
/**
* Book cloning recipe ID; stored separately as its recipe works differently from others.
*/
public static final int BOOK_CLONING_RECIPE_ID = 278;
public static final ItemStack REFRESH_ITEM = new ItemStack(1, 127, new DataComponents(new HashMap<>())); public static final ItemStack REFRESH_ITEM = new ItemStack(1, 127, new DataComponents(new HashMap<>()));