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

Start adding support for crafting

Expect bugs
Dieser Commit ist enthalten in:
AJ Ferguson 2019-11-27 18:55:58 -09:00
Ursprung af85d6e4d2
Commit 489c39e900
16 geänderte Dateien mit 541 neuen und 182 gelöschten Zeilen

Datei anzeigen

@ -52,7 +52,6 @@ public class Inventory {
@Setter @Setter
protected String title; protected String title;
@Getter
@Setter @Setter
protected ItemStack[] items; protected ItemStack[] items;
@ -82,4 +81,10 @@ public class Inventory {
public ItemStack getItem(int slot) { public ItemStack getItem(int slot) {
return items[slot]; return items[slot];
} }
public void setItem(int slot, ItemStack item) {
if (item != null && (item.getId() == 0 || item.getAmount() < 1))
item = null;
items[slot] = item;
}
} }

Datei anzeigen

@ -44,7 +44,7 @@ public class PlayerInventory extends Inventory {
} }
public void setCursor(ItemStack stack) { public void setCursor(ItemStack stack) {
if (stack != null && stack.getId() == 0) if (stack != null && (stack.getId() == 0 || stack.getAmount() < 1))
stack = null; stack = null;
cursor = stack; cursor = stack;
} }

Datei anzeigen

@ -105,11 +105,9 @@ public class GeyserSession implements Player {
@Setter @Setter
private GameMode gameMode = GameMode.SURVIVAL; private GameMode gameMode = GameMode.SURVIVAL;
@Getter
@Setter @Setter
private int lastClickedSlot; private int craftSlot = 0;
@Getter
@Setter @Setter
private int reopeningWindow = -1; private int reopeningWindow = -1;

Datei anzeigen

@ -99,6 +99,7 @@ public class TranslatorsInit {
Registry.registerJava(ServerRespawnPacket.class, new JavaRespawnTranslator()); Registry.registerJava(ServerRespawnPacket.class, new JavaRespawnTranslator());
Registry.registerJava(ServerSpawnPositionPacket.class, new JavaSpawnPositionTranslator()); Registry.registerJava(ServerSpawnPositionPacket.class, new JavaSpawnPositionTranslator());
Registry.registerJava(ServerDifficultyPacket.class, new JavaDifficultyTranslator()); Registry.registerJava(ServerDifficultyPacket.class, new JavaDifficultyTranslator());
Registry.registerJava(ServerDeclareRecipesPacket.class, new JavaDeclareRecipesTranslator());
Registry.registerJava(ServerEntityAnimationPacket.class, new JavaEntityAnimationTranslator()); Registry.registerJava(ServerEntityAnimationPacket.class, new JavaEntityAnimationTranslator());
Registry.registerJava(ServerEntityPositionPacket.class, new JavaEntityPositionTranslator()); Registry.registerJava(ServerEntityPositionPacket.class, new JavaEntityPositionTranslator());
@ -181,7 +182,7 @@ public class TranslatorsInit {
inventoryTranslators.put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, 23 << 4, ContainerType.DISPENSER)); inventoryTranslators.put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, 23 << 4, ContainerType.DISPENSER));
inventoryTranslators.put(WindowType.HOPPER, new BlockInventoryTranslator(5, 154 << 4, ContainerType.HOPPER)); inventoryTranslators.put(WindowType.HOPPER, new BlockInventoryTranslator(5, 154 << 4, ContainerType.HOPPER));
inventoryTranslators.put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(36, 205 << 4, ContainerType.CONTAINER)); inventoryTranslators.put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(27, 205 << 4, ContainerType.CONTAINER));
//inventoryTranslators.put(WindowType.BEACON, new BlockInventoryTranslator(1, 138 << 4, ContainerType.BEACON)); //TODO //inventoryTranslators.put(WindowType.BEACON, new BlockInventoryTranslator(1, 138 << 4, ContainerType.BEACON)); //TODO
} }
} }

Datei anzeigen

@ -26,6 +26,7 @@
package org.geysermc.connector.network.translators.bedrock; package org.geysermc.connector.network.translators.bedrock;
import com.github.steveice10.mc.protocol.data.game.window.*; import com.github.steveice10.mc.protocol.data.game.window.*;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientConfirmTransactionPacket;
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.ClientRenameItemPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientRenameItemPacket;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
@ -49,27 +50,36 @@ import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket; import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket;
import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.inventory.PlayerInventory;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.TranslatorsInit; import org.geysermc.connector.network.translators.TranslatorsInit;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.utils.InventoryUtils;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Objects;
public class BedrockInventoryTransactionTranslator extends PacketTranslator<InventoryTransactionPacket> { public class BedrockInventoryTransactionTranslator extends PacketTranslator<InventoryTransactionPacket> {
private final ItemStack refreshItem = new ItemStack(1, 127, new CompoundTag(""));
@Override @Override
public void translate(InventoryTransactionPacket packet, GeyserSession session) { public void translate(InventoryTransactionPacket packet, GeyserSession session) {
switch (packet.getTransactionType()) { switch (packet.getTransactionType()) {
case NORMAL: case NORMAL:
for (InventoryAction action : packet.getActions()) {
if (action.getSource().getContainerId() == ContainerId.CRAFTING_USE_INGREDIENT ||
action.getSource().getContainerId() == ContainerId.CRAFTING_RESULT) {
return;
}
}
Inventory inventory = session.getInventoryCache().getOpenInventory(); Inventory inventory = session.getInventoryCache().getOpenInventory();
if (inventory == null) if (inventory == null)
inventory = session.getInventory(); inventory = session.getInventory();
InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType()); InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
int craftSlot = session.getCraftSlot();
session.setCraftSlot(0);
if (session.getGameMode() == GameMode.CREATIVE && inventory.getId() == 0) { if (session.getGameMode() == GameMode.CREATIVE && inventory.getId() == 0) {
ItemStack javaItem; ItemStack javaItem;
for (InventoryAction action : packet.getActions()) { for (InventoryAction action : packet.getActions()) {
@ -87,17 +97,18 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
break; break;
} }
} }
ClientCreativeInventoryActionPacket creativePacket = new ClientCreativeInventoryActionPacket(javaSlot, InventoryUtils.fixStack(javaItem)); ClientCreativeInventoryActionPacket creativePacket = new ClientCreativeInventoryActionPacket(javaSlot, fixStack(javaItem));
session.getDownstream().getSession().send(creativePacket); session.getDownstream().getSession().send(creativePacket);
inventory.getItems()[javaSlot] = javaItem; inventory.setItem(javaSlot, javaItem);
break; break;
case ContainerId.NONE: case ContainerId.NONE:
if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION && action.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) { if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION &&
action.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
javaItem = TranslatorsInit.getItemTranslator().translateToJava(action.getToItem()); javaItem = TranslatorsInit.getItemTranslator().translateToJava(action.getToItem());
if (javaItem.getId() == 0) { //item missing mapping if (javaItem.getId() == 0) { //item missing mapping
break; break;
} }
ClientCreativeInventoryActionPacket creativeDropPacket = new ClientCreativeInventoryActionPacket(-1, InventoryUtils.fixStack(javaItem)); ClientCreativeInventoryActionPacket creativeDropPacket = new ClientCreativeInventoryActionPacket(-1, fixStack(javaItem));
session.getDownstream().getSession().send(creativeDropPacket); session.getDownstream().getSession().send(creativeDropPacket);
} }
break; break;
@ -111,11 +122,10 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
for (InventoryAction action : packet.getActions()) { for (InventoryAction action : packet.getActions()) {
if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION) { if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION) {
worldAction = action; worldAction = action;
} else if (action.getSource().getContainerId() == ContainerId.CURSOR) { } else if (action.getSource().getContainerId() == ContainerId.CURSOR && action.getSlot() == 0) {
cursorAction = action; cursorAction = action;
} }
} }
List<InventoryAction> actions = packet.getActions(); List<InventoryAction> actions = packet.getActions();
if (inventory.getWindowType() == WindowType.ANVIL) { if (inventory.getWindowType() == WindowType.ANVIL) {
InventoryAction anvilResult = null; InventoryAction anvilResult = null;
@ -132,7 +142,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
itemName = anvilResult.getFromItem(); itemName = anvilResult.getFromItem();
actions = new ArrayList<>(2); actions = new ArrayList<>(2);
for (InventoryAction action : packet.getActions()) { //packet sent by client when grabbing anvil output needs useless actions stripped for (InventoryAction action : packet.getActions()) { //packet sent by client when grabbing anvil output needs useless actions stripped
if (!(action.getSource().getContainerId() == ContainerId.CONTAINER_INPUT || action.getSource().getContainerId() == ContainerId.ANVIL_MATERIAL)) { if (!(action.getSource().getContainerId() == ContainerId.CONTAINER_INPUT ||
action.getSource().getContainerId() == ContainerId.ANVIL_MATERIAL)) {
actions.add(action); actions.add(action);
} }
} }
@ -153,7 +164,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
} }
if (actions.size() == 2) { if (actions.size() == 2) {
if (worldAction != null && worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) { if (worldAction != null) {
//find container action //find container action
InventoryAction containerAction = null; InventoryAction containerAction = null;
for (InventoryAction action : actions) { for (InventoryAction action : actions) {
@ -162,30 +173,54 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
break; break;
} }
} }
if (containerAction != null) { if (containerAction != null && worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
//quick dropping from hotbar? //quick dropping from hotbar?
if (session.getInventoryCache().getOpenInventory() == null && containerAction.getSource().getContainerId() == ContainerId.INVENTORY) { if (session.getInventoryCache().getOpenInventory() == null && containerAction.getSource().getContainerId() == ContainerId.INVENTORY) {
if (containerAction.getSlot() == session.getInventory().getHeldItemSlot()) { int heldSlot = session.getInventory().getHeldItemSlot();
if (containerAction.getSlot() == heldSlot) {
ClientPlayerActionPacket actionPacket = new ClientPlayerActionPacket( ClientPlayerActionPacket actionPacket = new ClientPlayerActionPacket(
containerAction.getToItem().getCount() == 0 ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM, containerAction.getToItem().getCount() == 0 ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM,
new Position(0, 0, 0), BlockFace.DOWN); new Position(0, 0, 0), BlockFace.DOWN);
session.getDownstream().getSession().send(actionPacket); session.getDownstream().getSession().send(actionPacket);
ItemStack item = session.getInventory().getItem(heldSlot);
if (item != null) {
session.getInventory().setItem(heldSlot, new ItemStack(item.getId(), item.getAmount() - 1, item.getNbt()));
}
return; return;
} }
} }
boolean leftClick = containerAction.getToItem().getCount() == 0; int dropAmount = containerAction.getFromItem().getCount() - containerAction.getToItem().getCount();
if (containerAction.getSource().getContainerId() != ContainerId.CURSOR) { //dropping directly from inventory if (containerAction != cursorAction) { //dropping directly from inventory
int javaSlot = translator.bedrockSlotToJava(containerAction); int javaSlot = translator.bedrockSlotToJava(containerAction);
ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(), if (dropAmount == containerAction.getFromItem().getCount()) {
javaSlot, null, WindowAction.DROP_ITEM, ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(),
leftClick ? DropItemParam.DROP_SELECTED_STACK : DropItemParam.DROP_FROM_SELECTED); inventory.getTransactionId().getAndIncrement(),
session.getDownstream().getSession().send(dropPacket); javaSlot, null, WindowAction.DROP_ITEM,
DropItemParam.DROP_SELECTED_STACK);
session.getDownstream().getSession().send(dropPacket);
} else {
for (int i = 0; i < dropAmount; i++) {
ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(),
inventory.getTransactionId().getAndIncrement(),
javaSlot, null, WindowAction.DROP_ITEM,
DropItemParam.DROP_FROM_SELECTED);
session.getDownstream().getSession().send(dropPacket);
}
}
ItemStack item = session.getInventory().getItem(javaSlot);
if (item != null) {
session.getInventory().setItem(javaSlot, new ItemStack(item.getId(), item.getAmount() - dropAmount, item.getNbt()));
}
return; return;
} else { //clicking outside of inventory } else { //clicking outside of inventory
ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(), ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
-999, null, WindowAction.CLICK_ITEM, -999, null, WindowAction.CLICK_ITEM,
leftClick ? ClickItemParam.LEFT_CLICK : ClickItemParam.RIGHT_CLICK); dropAmount > 1 ? ClickItemParam.LEFT_CLICK : ClickItemParam.RIGHT_CLICK);
session.getDownstream().getSession().send(dropPacket); session.getDownstream().getSession().send(dropPacket);
ItemStack cursor = session.getInventory().getCursor();
if (cursor != null) {
session.getInventory().setCursor(new ItemStack(cursor.getId(), dropAmount > 1 ? 0 : cursor.getAmount() - 1, cursor.getNbt()));
}
return; return;
} }
} }
@ -199,43 +234,81 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
} }
} }
if (containerAction != null) { if (containerAction != null) {
if (InventoryUtils.canCombine(cursorAction.getFromItem(), cursorAction.getToItem()) //left/right click
&& cursorAction.getToItem().getCount() > cursorAction.getFromItem().getCount()) { //fill stack List<ClickAction> plan = new ArrayList<>();
int javaSlot = session.getLastClickedSlot(); ItemStack translatedCursor = TranslatorsInit.getItemTranslator().translateToJava(cursorAction.getFromItem());
ClientWindowActionPacket fillStackPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(), boolean refresh = !Objects.equals(session.getInventory().getCursor(), translatedCursor.getId() == 0 ? null : translatedCursor); //refresh slot if there is a cursor mismatch
javaSlot, null, WindowAction.FILL_STACK, FillStackParam.FILL); int javaSlot = translator.bedrockSlotToJava(containerAction);
session.getDownstream().getSession().send(fillStackPacket); if (cursorAction.getFromItem().equals(containerAction.getToItem()) &&
translator.updateInventory(session, inventory); //bedrock fill stack can sometimes differ from java version, refresh and let server change slots containerAction.getFromItem().equals(cursorAction.getToItem()) &&
return; !canStack(cursorAction.getFromItem(), containerAction.getFromItem())) { //simple swap
} else { Click.LEFT.onSlot(javaSlot, plan);
//left/right click } else if (cursorAction.getFromItem().getCount() > cursorAction.getToItem().getCount()) { //release
int javaSlot = translator.bedrockSlotToJava(containerAction); if (cursorAction.getToItem().getCount() == 0) {
boolean rightClick; Click.LEFT.onSlot(javaSlot, plan);
if (cursorAction.getFromItem().getCount() == 0) { //picking up item } else {
rightClick = containerAction.getToItem().getCount() != 0; int difference = cursorAction.getFromItem().getCount() - cursorAction.getToItem().getCount();
} else { //releasing item for (int i = 0; i < difference; i++) {
rightClick = cursorAction.getToItem().getCount() != 0 && cursorAction.getFromItem().getCount() - cursorAction.getToItem().getCount() == 1; Click.RIGHT.onSlot(javaSlot, plan);
}
}
} else { //pickup
if (cursorAction.getFromItem().getCount() == 0) {
if (containerAction.getToItem().getCount() == 0) { //pickup all
Click.LEFT.onSlot(javaSlot, plan);
} else { //pickup some
if (translator.isOutputSlot(javaSlot) ||
containerAction.getToItem().getCount() == containerAction.getFromItem().getCount() / 2) { //right click
Click.RIGHT.onSlot(javaSlot, plan);
} else {
Click.LEFT.onSlot(javaSlot, plan);
int difference = containerAction.getFromItem().getCount() - cursorAction.getToItem().getCount();
for (int i = 0; i < difference; i++) {
Click.RIGHT.onSlot(javaSlot, plan);
}
}
}
} else { //pickup into non-empty cursor
if (translator.isOutputSlot(javaSlot)) {
if (containerAction.getToItem().getCount() == 0) {
Click.LEFT.onSlot(javaSlot, plan);
} else {
ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(),
inventory.getTransactionId().getAndIncrement(),
javaSlot, refreshItem, WindowAction.SHIFT_CLICK_ITEM,
ShiftClickItemParam.LEFT_CLICK);
session.getDownstream().getSession().send(shiftClickPacket);
translator.updateInventory(session, inventory);
return;
}
} else if ((inventory.getId() == 0 || inventory.getWindowType() == WindowType.CRAFTING) && javaSlot == 0) { //crafting output
Click.LEFT.onSlot(javaSlot, plan);
} else {
int cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Collections.singletonList(javaSlot));
if (cursorSlot != -1) {
Click.LEFT.onSlot(cursorSlot, plan);
} else {
translator.updateInventory(session, inventory);
return;
}
Click.LEFT.onSlot(javaSlot, plan);
int difference = cursorAction.getToItem().getCount() - cursorAction.getFromItem().getCount();
for (int i = 0; i < difference; i++) {
Click.RIGHT.onSlot(cursorSlot, plan);
}
Click.LEFT.onSlot(javaSlot, plan);
Click.LEFT.onSlot(cursorSlot, plan);
}
} }
ItemStack translatedCursor = TranslatorsInit.getItemTranslator().translateToJava(cursorAction.getFromItem());
boolean refresh = !Objects.equals(session.getInventory().getCursor(), translatedCursor.getId() == 0 ? null : translatedCursor); //refresh slot if there is a cursor mismatch
ClientWindowActionPacket clickPacket = new ClientWindowActionPacket(inventory.getId(),
inventory.getTransactionId().getAndIncrement(), javaSlot,
refresh ? new ItemStack(1, 127, new CompoundTag("")) : InventoryUtils.fixStack(TranslatorsInit.getItemTranslator().translateToJava(containerAction.getFromItem())), //send invalid item stack to refresh slot
WindowAction.CLICK_ITEM, rightClick ? ClickItemParam.RIGHT_CLICK : ClickItemParam.LEFT_CLICK);
session.getDownstream().getSession().send(clickPacket);
inventory.getItems()[javaSlot] = TranslatorsInit.getItemTranslator().translateToJava(containerAction.getToItem());
translator.updateSlot(session, inventory, javaSlot);
session.getInventory().setCursor(TranslatorsInit.getItemTranslator().translateToJava(cursorAction.getToItem()));
session.setLastClickedSlot(javaSlot);
return;
} }
executePlan(session, inventory, translator, plan, refresh);
return;
} }
} else { } else {
//either moving 1 item or swapping 2 slots (touchscreen or one slot shift click) List<ClickAction> plan = new ArrayList<>();
InventoryAction fromAction; InventoryAction fromAction;
InventoryAction toAction; InventoryAction toAction;
//find source slot if (actions.get(0).getFromItem().getCount() >= actions.get(0).getToItem().getCount()) {
if (actions.get(0).getFromItem().getCount() > actions.get(0).getToItem().getCount()) {
fromAction = actions.get(0); fromAction = actions.get(0);
toAction = actions.get(1); toAction = actions.get(1);
} else { } else {
@ -245,120 +318,93 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
int fromSlot = translator.bedrockSlotToJava(fromAction); int fromSlot = translator.bedrockSlotToJava(fromAction);
int toSlot = translator.bedrockSlotToJava(toAction); int toSlot = translator.bedrockSlotToJava(toAction);
//check if dealing with output only slot like furnace. this is to handle a situation where the output slot was partially emptied without right clicking (touchscreen or full inventory) if ((inventory.getId() == 0 || inventory.getWindowType() == WindowType.CRAFTING) && fromSlot == 0) {
//this is only possible by shift clicking if ((craftSlot != 0 && craftSlot != -2) && (inventory.getItem(toSlot) == null ||
if (translator.isOutputSlot(fromAction) && fromAction.getToItem().getCount() != 0) { canStack(session.getInventory().getCursor(), inventory.getItem(toSlot)))) {
ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(), boolean refresh = false;
fromSlot, InventoryUtils.fixStack(inventory.getItem(fromSlot)), WindowAction.SHIFT_CLICK_ITEM, ShiftClickItemParam.LEFT_CLICK); if (fromAction.getToItem().getCount() == 0) {
session.getDownstream().getSession().send(shiftClickPacket); refresh = true;
inventory.getItems()[toSlot] = TranslatorsInit.getItemTranslator().translateToJava(toAction.getToItem()); Click.LEFT.onSlot(toSlot, plan);
inventory.getItems()[fromSlot] = TranslatorsInit.getItemTranslator().translateToJava(fromAction.getToItem()); if (craftSlot != -1) {
return; Click.LEFT.onSlot(craftSlot, plan);
} else {
//pickup fromAction item
ClientWindowActionPacket leftClick1Packet = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
fromSlot, InventoryUtils.fixStack(TranslatorsInit.getItemTranslator().translateToJava(fromAction.getFromItem())), WindowAction.CLICK_ITEM,
ClickItemParam.LEFT_CLICK);
session.getDownstream().getSession().send(leftClick1Packet);
//release fromAction item into toAction slot
ClientWindowActionPacket leftClick2Packet = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
toSlot, InventoryUtils.fixStack(TranslatorsInit.getItemTranslator().translateToJava(toAction.getFromItem())), WindowAction.CLICK_ITEM,
ClickItemParam.LEFT_CLICK);
session.getDownstream().getSession().send(leftClick2Packet);
//test if swapping two items or moving one item
//if swapping then complete it
if (fromAction.getToItem().getId() != 0) {
ClientWindowActionPacket leftClick3Packet = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
fromSlot, null, WindowAction.CLICK_ITEM,
ClickItemParam.LEFT_CLICK);
session.getDownstream().getSession().send(leftClick3Packet);
}
inventory.getItems()[toSlot] = TranslatorsInit.getItemTranslator().translateToJava(toAction.getToItem());
inventory.getItems()[fromSlot] = TranslatorsInit.getItemTranslator().translateToJava(fromAction.getToItem());
return;
}
}
} else if (actions.size() > 2) {
//shift click or fill stack?
ItemData firstItem;
if (actions.get(0).getFromItem().getId() != 0) {
firstItem = actions.get(0).getFromItem();
} else {
firstItem = actions.get(0).getToItem();
}
List<InventoryAction> sourceActions = new ArrayList<>(actions.size());
List<InventoryAction> destActions = new ArrayList<>(actions.size());
boolean sameItems = true;
for (InventoryAction action : actions) {
if (action.getFromItem().getCount() > action.getToItem().getCount()) {
if (!InventoryUtils.canCombine(action.getFromItem(), firstItem))
sameItems = false;
sourceActions.add(action);
} else {
if (!InventoryUtils.canCombine(action.getToItem(), firstItem))
sameItems = false;
destActions.add(action);
}
}
if (sameItems) {
if (sourceActions.size() == 1) { //shift click
InventoryAction sourceAction = sourceActions.get(0);
//in java edition, shift clicked item must move across hotbar and main inventory
if (sourceAction.getSource().getContainerId() == ContainerId.INVENTORY) {
for (InventoryAction action : actions) {
if (action != sourceAction && action.getSource().getContainerId() == ContainerId.INVENTORY) {
if ((sourceAction.getSlot() < 9 && action.getSlot() < 9) || (sourceAction.getSlot() >= 9 && action.getSlot() >= 9)) {
//shift click not compatible with java edition. refresh inventory and abort
translator.updateInventory(session, inventory);
return;
}
} }
} else {
int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount();
for (int i = 0; i < difference; i++) {
Click.RIGHT.onSlot(toSlot, plan);
}
session.setCraftSlot(craftSlot);
} }
} executePlan(session, inventory, translator, plan, refresh);
int javaSlot = translator.bedrockSlotToJava(sourceAction); return;
ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
javaSlot, InventoryUtils.fixStack(inventory.getItem(javaSlot)), WindowAction.SHIFT_CLICK_ITEM, ShiftClickItemParam.LEFT_CLICK);
session.getDownstream().getSession().send(shiftClickPacket);
return;
} else if (destActions.size() == 1) { //fill stack
InventoryAction destAction = destActions.get(0);
int javaSlot;
if (destAction != cursorAction) { //if touchscreen
javaSlot = translator.bedrockSlotToJava(destAction);
ClientWindowActionPacket leftClickPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
javaSlot, InventoryUtils.fixStack(inventory.getItem(javaSlot)), WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK);
session.getDownstream().getSession().send(leftClickPacket);
} else { } else {
javaSlot = session.getLastClickedSlot(); session.setCraftSlot(-2);
} }
ClientWindowActionPacket fillStackPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
javaSlot, null, WindowAction.FILL_STACK, FillStackParam.FILL);
session.getDownstream().getSession().send(fillStackPacket);
if (destAction != cursorAction) { //if touchscreen
ClientWindowActionPacket leftClickPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
javaSlot, null, WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK);
session.getDownstream().getSession().send(leftClickPacket);
inventory.getItems()[javaSlot] = TranslatorsInit.getItemTranslator().translateToJava(destAction.getToItem());
}
translator.updateInventory(session, inventory);
return;
} }
int cursorSlot = -1;
if (session.getInventory().getCursor() != null) { //move cursor contents to a temporary slot
cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Arrays.asList(fromSlot, toSlot));
if (cursorSlot != -1) {
Click.LEFT.onSlot(cursorSlot, plan);
} else {
translator.updateInventory(session, inventory);
return;
}
}
if ((fromAction.getFromItem().equals(toAction.getToItem()) && !canStack(fromAction.getFromItem(), toAction.getFromItem())) || fromAction.getToItem().getId() == 0) { //slot swap
Click.LEFT.onSlot(fromSlot, plan);
Click.LEFT.onSlot(toSlot, plan);
if (fromAction.getToItem().getId() != 0) {
Click.LEFT.onSlot(fromSlot, plan);
}
} else if (canStack(fromAction.getFromItem(), toAction.getToItem())) { //partial item move
if (translator.isOutputSlot(fromSlot)) {
ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(),
inventory.getTransactionId().getAndIncrement(),
fromSlot, refreshItem, WindowAction.SHIFT_CLICK_ITEM,
ShiftClickItemParam.LEFT_CLICK);
session.getDownstream().getSession().send(shiftClickPacket);
translator.updateInventory(session, inventory);
return;
} else if ((inventory.getId() == 0 || inventory.getWindowType() == WindowType.CRAFTING) && fromSlot == 0) {
session.setCraftSlot(cursorSlot);
Click.LEFT.onSlot(fromSlot, plan);
int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount();
for (int i = 0; i < difference; i++) {
Click.RIGHT.onSlot(toSlot, plan);
}
//client will send additional packets later to finish transferring crafting output
//translator will know how to handle this using the craftSlot variable
} else {
Click.LEFT.onSlot(fromSlot, plan);
int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount();
for (int i = 0; i < difference; i++) {
Click.RIGHT.onSlot(toSlot, plan);
}
Click.LEFT.onSlot(fromSlot, plan);
}
}
if (cursorSlot != -1) {
Click.LEFT.onSlot(cursorSlot, plan);
}
executePlan(session, inventory, translator, plan, false);
return;
} }
} }
//refresh inventory, transaction was not translated
translator.updateInventory(session, inventory); translator.updateInventory(session, inventory);
break; break;
case INVENTORY_MISMATCH: case INVENTORY_MISMATCH:
InventorySlotPacket cursorPacket = new InventorySlotPacket(); InventorySlotPacket cursorPacket = new InventorySlotPacket();
cursorPacket.setContainerId(ContainerId.CURSOR); cursorPacket.setContainerId(ContainerId.CURSOR);
cursorPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(session.getInventory().getCursor())); cursorPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(session.getInventory().getCursor()));
session.getUpstream().sendPacket(cursorPacket); //session.getUpstream().sendPacket(cursorPacket);
Inventory inv = session.getInventoryCache().getOpenInventory(); Inventory inv = session.getInventoryCache().getOpenInventory();
if (inv == null) if (inv == null)
inv = session.getInventory(); inv = session.getInventory();
TranslatorsInit.getInventoryTranslators().get(inv.getWindowType()).updateInventory(session, inv); TranslatorsInit.getInventoryTranslators().get(inv.getWindowType()).updateInventory(session, inv);
break;
case ITEM_USE: case ITEM_USE:
if (packet.getActionType() == 1) { if (packet.getActionType() == 1) {
ClientPlayerUseItemPacket useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND); ClientPlayerUseItemPacket useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
@ -389,4 +435,134 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
break; break;
} }
} }
private int findTempSlot(Inventory inventory, ItemStack item, List<Integer> slotBlacklist) {
/*try and find a slot that can temporarily store the given item
only look in the main inventory and hotbar
only slots that are empty or contain a different type of item are valid*/
int offset = inventory.getId() == 0 ? 1 : 0; //offhand is not a viable slot (some servers disable it)
List<ItemStack> itemBlacklist = new ArrayList<>(slotBlacklist.size() + 1);
itemBlacklist.add(item);
for (int slot : slotBlacklist) {
ItemStack blacklistItem = inventory.getItem(slot);
if (blacklistItem != null)
itemBlacklist.add(blacklistItem);
}
for (int i = inventory.getSize() - (36 + offset); i < inventory.getSize() - offset; i++) {
ItemStack testItem = inventory.getItem(i);
boolean acceptable = true;
if (testItem != null) {
for (ItemStack blacklistItem : itemBlacklist) {
if (canStack(testItem, blacklistItem)) {
acceptable = false;
break;
}
}
}
if (acceptable && !slotBlacklist.contains(i))
return i;
}
//could not find a viable temp slot
return -1;
}
//NPE if compound tag is null
private ItemStack fixStack(ItemStack stack) {
if (stack == null || stack.getId() == 0)
return null;
return new ItemStack(stack.getId(), stack.getAmount(), stack.getNbt() == null ? new CompoundTag("") : stack.getNbt());
}
private boolean canStack(ItemStack item1, ItemStack item2) {
if (item1 == null || item2 == null)
return false;
return item1.getId() == item2.getId() && item1.getNbt() == item2.getNbt();
}
private boolean canStack(ItemData item1, ItemData item2) {
if (item1 == null || item2 == null)
return false;
return item1.equals(item2, false, true, true);
}
private void executePlan(GeyserSession session, Inventory inventory, InventoryTranslator translator, List<ClickAction> plan, boolean refresh) {
PlayerInventory playerInventory = session.getInventory();
ListIterator<ClickAction> planIter = plan.listIterator();
while (planIter.hasNext()) {
ClickAction action = planIter.next();
ItemStack cursorItem = playerInventory.getCursor();
ItemStack clickedItem = inventory.getItem(action.slot);
short actionId = (short) inventory.getTransactionId().getAndIncrement();
boolean craftingOutput = (inventory.getId() == 0 || inventory.getWindowType() == WindowType.CRAFTING) && action.slot == 0;
if (craftingOutput)
refresh = true;
ClientWindowActionPacket clickPacket = new ClientWindowActionPacket(inventory.getId(),
actionId, action.slot, !planIter.hasNext() && refresh ? refreshItem : fixStack(clickedItem),
WindowAction.CLICK_ITEM, action.click.actionParam);
if (craftingOutput) { //crafting output
if (cursorItem == null && clickedItem != null) {
playerInventory.setCursor(clickedItem);
} else if (canStack(cursorItem, clickedItem)) {
playerInventory.setCursor(new ItemStack(cursorItem.getId(),
cursorItem.getAmount() + clickedItem.getAmount(), cursorItem.getNbt()));
}
} else {
switch (action.click) {
case LEFT:
if (!canStack(cursorItem, clickedItem)) {
playerInventory.setCursor(clickedItem);
inventory.setItem(action.slot, cursorItem);
} else {
playerInventory.setCursor(null);
inventory.setItem(action.slot, new ItemStack(clickedItem.getId(),
clickedItem.getAmount() + cursorItem.getAmount(), clickedItem.getNbt()));
}
break;
case RIGHT:
if (cursorItem == null && clickedItem != null) {
ItemStack halfItem = new ItemStack(clickedItem.getId(),
clickedItem.getAmount() / 2, clickedItem.getNbt());
inventory.setItem(action.slot, halfItem);
playerInventory.setCursor(new ItemStack(clickedItem.getId(),
clickedItem.getAmount() - halfItem.getAmount(), clickedItem.getNbt()));
} else if (cursorItem != null && clickedItem == null) {
playerInventory.setCursor(new ItemStack(cursorItem.getId(),
cursorItem.getAmount() - 1, cursorItem.getNbt()));
inventory.setItem(action.slot, new ItemStack(cursorItem.getId(),
1, cursorItem.getNbt()));
} else if (canStack(cursorItem, clickedItem)) {
playerInventory.setCursor(new ItemStack(cursorItem.getId(),
cursorItem.getAmount() - 1, cursorItem.getNbt()));
inventory.setItem(action.slot, new ItemStack(clickedItem.getId(),
clickedItem.getAmount() + 1, clickedItem.getNbt()));
}
break;
}
}
session.getDownstream().getSession().send(clickPacket);
session.getDownstream().getSession().send(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true));
}
}
private enum Click {
LEFT(ClickItemParam.LEFT_CLICK),
RIGHT(ClickItemParam.RIGHT_CLICK);
final WindowActionParam actionParam;
Click(WindowActionParam actionParam) {
this.actionParam = actionParam;
}
void onSlot(int slot, List<ClickAction> plan) {
plan.add(new ClickAction(slot, this));
}
}
private static class ClickAction {
final int slot;
final Click click;
ClickAction(int slot, Click click) {
this.slot = slot;
this.click = click;
}
}
} }

Datei anzeigen

@ -54,7 +54,7 @@ public class AnvilInventoryTranslator extends BlockInventoryTranslator {
} }
@Override @Override
public boolean isOutputSlot(InventoryAction action) { public boolean isOutputSlot(int slot) {
return action.getSource().getContainerId() == ContainerId.ANVIL_RESULT; return slot == 2;
} }
} }

Datei anzeigen

@ -43,7 +43,7 @@ public abstract class ContainerInventoryTranslator extends InventoryTranslator {
public void updateInventory(GeyserSession session, Inventory inventory) { public void updateInventory(GeyserSession session, Inventory inventory) {
ItemData[] bedrockItems = new ItemData[this.size]; ItemData[] bedrockItems = new ItemData[this.size];
for (int i = 0; i < bedrockItems.length; i++) { for (int i = 0; i < bedrockItems.length; i++) {
bedrockItems[javaSlotToBedrock(i)] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]); bedrockItems[javaSlotToBedrock(i)] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
} }
InventoryContentPacket contentPacket = new InventoryContentPacket(); InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(inventory.getId()); contentPacket.setContainerId(inventory.getId());
@ -52,7 +52,7 @@ public abstract class ContainerInventoryTranslator extends InventoryTranslator {
Inventory playerInventory = session.getInventory(); Inventory playerInventory = session.getInventory();
for (int i = 0; i < 36; i++) { for (int i = 0; i < 36; i++) {
playerInventory.getItems()[i + 9] = inventory.getItems()[i + this.size]; playerInventory.setItem(i + 9, inventory.getItem(i + this.size));
} }
TranslatorsInit.getInventoryTranslators().get(playerInventory.getWindowType()).updateInventory(session, playerInventory); TranslatorsInit.getInventoryTranslators().get(playerInventory.getWindowType()).updateInventory(session, playerInventory);
} }
@ -61,13 +61,13 @@ public abstract class ContainerInventoryTranslator extends InventoryTranslator {
public void updateSlot(GeyserSession session, Inventory inventory, int slot) { public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
if (slot >= this.size) { if (slot >= this.size) {
Inventory playerInventory = session.getInventory(); Inventory playerInventory = session.getInventory();
playerInventory.getItems()[(slot + 9) - this.size] = inventory.getItem(slot); playerInventory.setItem((slot + 9) - this.size, inventory.getItem(slot));
TranslatorsInit.getInventoryTranslators().get(playerInventory.getWindowType()).updateSlot(session, playerInventory, (slot + 9) - this.size); TranslatorsInit.getInventoryTranslators().get(playerInventory.getWindowType()).updateSlot(session, playerInventory, (slot + 9) - this.size);
} else { } else {
InventorySlotPacket slotPacket = new InventorySlotPacket(); InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(inventory.getId()); slotPacket.setContainerId(inventory.getId());
slotPacket.setInventorySlot(javaSlotToBedrock(slot)); slotPacket.setInventorySlot(javaSlotToBedrock(slot));
slotPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[slot])); slotPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(slot)));
session.getUpstream().sendPacket(slotPacket); session.getUpstream().sendPacket(slotPacket);
} }
} }
@ -97,7 +97,7 @@ public abstract class ContainerInventoryTranslator extends InventoryTranslator {
} }
@Override @Override
public boolean isOutputSlot(InventoryAction action) { public boolean isOutputSlot(int slot) {
return false; return false;
} }
} }

Datei anzeigen

@ -1,7 +1,9 @@
package org.geysermc.connector.network.translators.inventory; package org.geysermc.connector.network.translators.inventory;
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.ContainerType; import com.nukkitx.protocol.bedrock.data.ContainerType;
import com.nukkitx.protocol.bedrock.data.InventoryAction;
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
@ -30,4 +32,24 @@ public class CraftingTableInventoryTranslator extends ContainerInventoryTranslat
public void closeInventory(GeyserSession session, Inventory inventory) { public void closeInventory(GeyserSession session, Inventory inventory) {
} }
@Override
public int bedrockSlotToJava(InventoryAction action) {
int slotnum = action.getSlot();
if (action.getSource().getContainerId() == ContainerId.INVENTORY) {
//hotbar
if (slotnum >= 9) {
return slotnum + this.size - 9;
} else {
return slotnum + this.size + 27;
}
} else {
if (slotnum >= 32 && 42 >= slotnum) {
return slotnum - 31;
} else if (slotnum == 50) {
return 0;
}
return slotnum;
}
}
} }

Datei anzeigen

@ -118,7 +118,7 @@ public class DoubleChestInventoryTranslator extends BlockInventoryTranslator {
ItemData[] bedrockItems = new ItemData[54]; ItemData[] bedrockItems = new ItemData[54];
for (int i = 0; i < bedrockItems.length; i++) { for (int i = 0; i < bedrockItems.length; i++) {
if (i <= this.size) { if (i <= this.size) {
bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]); bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
} else { } else {
bedrockItems[i] = ItemData.AIR; bedrockItems[i] = ItemData.AIR;
} }
@ -130,7 +130,7 @@ public class DoubleChestInventoryTranslator extends BlockInventoryTranslator {
Inventory playerInventory = session.getInventory(); Inventory playerInventory = session.getInventory();
for (int i = 0; i < 36; i++) { for (int i = 0; i < 36; i++) {
playerInventory.getItems()[i + 9] = inventory.getItems()[i + this.size]; playerInventory.setItem(i + 9, inventory.getItem(i + this.size));
} }
TranslatorsInit.getInventoryTranslators().get(playerInventory.getWindowType()).updateInventory(session, playerInventory); TranslatorsInit.getInventoryTranslators().get(playerInventory.getWindowType()).updateInventory(session, playerInventory);
} }

Datei anzeigen

@ -59,7 +59,7 @@ public class FurnaceInventoryTranslator extends BlockInventoryTranslator {
} }
@Override @Override
public boolean isOutputSlot(InventoryAction action) { public boolean isOutputSlot(int slot) {
return action.getSlot() == 2; return slot == 2;
} }
} }

Datei anzeigen

@ -44,5 +44,5 @@ public abstract class InventoryTranslator {
public abstract void updateSlot(GeyserSession session, Inventory inventory, int slot); public abstract void updateSlot(GeyserSession session, Inventory inventory, int slot);
public abstract int bedrockSlotToJava(InventoryAction action); public abstract int bedrockSlotToJava(InventoryAction action);
public abstract int javaSlotToBedrock(int slot); public abstract int javaSlotToBedrock(int slot);
public abstract boolean isOutputSlot(InventoryAction action); public abstract boolean isOutputSlot(int slot);
} }

Datei anzeigen

@ -45,12 +45,12 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
ItemData[] contents = new ItemData[36]; ItemData[] contents = new ItemData[36];
// Inventory // Inventory
for (int i = 9; i < 36; i++) { for (int i = 9; i < 36; i++) {
contents[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]); contents[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
} }
// Hotbar // Hotbar
for (int i = 36; i < 45; i++) { for (int i = 36; i < 45; i++) {
contents[i - 36] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]); contents[i - 36] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
} }
inventoryContentPacket.setContents(contents); inventoryContentPacket.setContents(contents);
@ -61,7 +61,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
armorContentPacket.setContainerId(ContainerId.ARMOR); armorContentPacket.setContainerId(ContainerId.ARMOR);
contents = new ItemData[4]; contents = new ItemData[4];
for (int i = 5; i < 9; i++) { for (int i = 5; i < 9; i++) {
contents[i - 5] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]); contents[i - 5] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
} }
armorContentPacket.setContents(contents); armorContentPacket.setContents(contents);
session.getUpstream().sendPacket(armorContentPacket); session.getUpstream().sendPacket(armorContentPacket);
@ -124,6 +124,12 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
case ContainerId.CRAFTING_ADD_INGREDIENT: case ContainerId.CRAFTING_ADD_INGREDIENT:
case ContainerId.CRAFTING_REMOVE_INGREDIENT: case ContainerId.CRAFTING_REMOVE_INGREDIENT:
return slotnum + 1; return slotnum + 1;
case ContainerId.CURSOR:
if (slotnum >= 28 && 31 >= slotnum) {
return slotnum - 27;
} else if (slotnum == 50) {
return 0;
}
} }
return slotnum; return slotnum;
} }
@ -134,7 +140,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
} }
@Override @Override
public boolean isOutputSlot(InventoryAction action) { public boolean isOutputSlot(int slot) {
return false; return false;
} }

Datei anzeigen

@ -43,7 +43,7 @@ public class SingleChestInventoryTranslator extends BlockInventoryTranslator {
ItemData[] bedrockItems = new ItemData[27]; ItemData[] bedrockItems = new ItemData[27];
for (int i = 0; i < bedrockItems.length; i++) { for (int i = 0; i < bedrockItems.length; i++) {
if (i <= this.size) { if (i <= this.size) {
bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]); bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
} else { } else {
bedrockItems[i] = ItemData.AIR; bedrockItems[i] = ItemData.AIR;
} }
@ -55,7 +55,7 @@ public class SingleChestInventoryTranslator extends BlockInventoryTranslator {
Inventory playerInventory = session.getInventory(); Inventory playerInventory = session.getInventory();
for (int i = 0; i < 36; i++) { for (int i = 0; i < 36; i++) {
playerInventory.getItems()[i + 9] = inventory.getItems()[i + this.size]; playerInventory.setItem(i + 9, inventory.getItem(i + this.size));
} }
TranslatorsInit.getInventoryTranslators().get(playerInventory.getWindowType()).updateInventory(session, playerInventory); TranslatorsInit.getInventoryTranslators().get(playerInventory.getWindowType()).updateInventory(session, playerInventory);
} }

Datei anzeigen

@ -0,0 +1,149 @@
/*
* Copyright (c) 2019 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.recipe.Ingredient;
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.packet.ingame.server.ServerDeclareRecipesPacket;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.protocol.bedrock.data.CraftingData;
import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.TranslatorsInit;
import java.util.*;
import java.util.stream.Collectors;
public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclareRecipesPacket> {
@Override
public void translate(ServerDeclareRecipesPacket packet, GeyserSession session) {
CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
craftingDataPacket.setCleanRecipes(true);
for (Recipe recipe : packet.getRecipes()) {
switch (recipe.getType()) {
case CRAFTING_SHAPELESS: {
ShapelessRecipeData shapelessRecipeData = (ShapelessRecipeData) recipe.getData();
ItemData output = TranslatorsInit.getItemTranslator().translateToBedrock(shapelessRecipeData.getResult());
List<ItemData[]> inputList = combinations(shapelessRecipeData.getIngredients());
for (ItemData[] inputs : inputList) {
UUID uuid = UUID.randomUUID();
craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
inputs, new ItemData[]{output}, uuid, "crafting_table", 0));
}
break;
}
case CRAFTING_SHAPED: {
ShapedRecipeData shapedRecipeData = (ShapedRecipeData) recipe.getData();
ItemData output = TranslatorsInit.getItemTranslator().translateToBedrock(shapedRecipeData.getResult());
List<ItemData[]> inputList = combinations(shapedRecipeData.getIngredients());
for (ItemData[] inputs : inputList) {
UUID uuid = UUID.randomUUID();
craftingDataPacket.getCraftingData().add(CraftingData.fromShaped(uuid.toString(),
shapedRecipeData.getWidth(), shapedRecipeData.getHeight(), inputs,
new ItemData[]{output}, uuid, "crafting_table", 0));
}
break;
}
}
}
session.getUpstream().sendPacket(craftingDataPacket);
}
private List<ItemData[]> combinations(Ingredient[] ingredients) {
ItemData[][] squashed = new ItemData[ingredients.length][];
for (int i = 0; i < ingredients.length; i++) {
if (ingredients[i].getOptions().length == 0) {
squashed[i] = new ItemData[]{ItemData.AIR};
continue;
}
Ingredient ingredient = ingredients[i];
Map<GroupedItem, List<ItemData>> groupedByIds = Arrays.stream(ingredient.getOptions())
.map(item -> TranslatorsInit.getItemTranslator().translateToBedrock(item))
.collect(Collectors.groupingBy(item -> new GroupedItem(item.getId(), item.getCount(), item.getTag())));
squashed[i] = new ItemData[groupedByIds.size()];
int index = 0;
for (Map.Entry<GroupedItem, List<ItemData>> entry : groupedByIds.entrySet()) {
if (entry.getValue().size() > 1) {
GroupedItem groupedItem = entry.getKey();
squashed[i][index++] = ItemData.of(groupedItem.id, (short) -1, groupedItem.count, groupedItem.tag);
} else {
ItemData item = entry.getValue().get(0);
squashed[i][index++] = item;
}
}
}
int[] sizeArray = new int[squashed.length];
int[] counterArray = new int[squashed.length];
int totalCombinationCount = 1;
for(int i = 0; i < squashed.length; i++) {
sizeArray[i] = squashed[i].length;
totalCombinationCount *= squashed[i].length;
}
if (totalCombinationCount > 10000) {
ItemData[] translatedItems = new ItemData[ingredients.length];
for (int i = 0; i < ingredients.length; i++) {
if (ingredients[i].getOptions().length > 0) {
translatedItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(ingredients[i].getOptions()[0]);
} else {
translatedItems[i] = ItemData.AIR;
}
}
return Collections.singletonList(translatedItems);
}
List<ItemData[]> combinationList = new ArrayList<>(totalCombinationCount);
for (int countdown = totalCombinationCount; countdown > 0; --countdown) {
ItemData[] translatedItems = new ItemData[squashed.length];
for(int i = 0; i < squashed.length; ++i) {
if (squashed[i].length > 0)
translatedItems[i] = squashed[i][counterArray[i]];
}
combinationList.add(translatedItems);
for(int incIndex = squashed.length - 1; incIndex >= 0; --incIndex) {
if(counterArray[incIndex] + 1 < sizeArray[incIndex]) {
++counterArray[incIndex];
break;
}
counterArray[incIndex] = 0;
}
}
return combinationList;
}
@EqualsAndHashCode
@AllArgsConstructor
private static class GroupedItem {
int id;
int count;
CompoundTag tag;
}
}

Datei anzeigen

@ -47,9 +47,12 @@ public class JavaSetSlotTranslator extends PacketTranslator<ServerSetSlotPacket>
if (packet.getWindowId() == 255 && packet.getSlot() == -1) { //cursor if (packet.getWindowId() == 255 && packet.getSlot() == -1) { //cursor
if (Objects.equals(session.getInventory().getCursor(), packet.getItem())) if (Objects.equals(session.getInventory().getCursor(), packet.getItem()))
return; return;
if (session.getCraftSlot() != 0)
return;
//bedrock client is bugged when changing the cursor. reopen inventory after changing it //bedrock client is bugged when changing the cursor. reopen inventory after changing it
if (packet.getItem() == null && session.getInventory().getCursor() != null) { //TODO: fix this. too buggy rn
/*if (packet.getItem() == null && session.getInventory().getCursor() != null) {
InventorySlotPacket cursorPacket = new InventorySlotPacket(); InventorySlotPacket cursorPacket = new InventorySlotPacket();
cursorPacket.setContainerId(ContainerId.CURSOR); cursorPacket.setContainerId(ContainerId.CURSOR);
cursorPacket.setSlot(ItemData.AIR); cursorPacket.setSlot(ItemData.AIR);
@ -64,7 +67,7 @@ public class JavaSetSlotTranslator extends PacketTranslator<ServerSetSlotPacket>
ContainerClosePacket closePacket = new ContainerClosePacket(); ContainerClosePacket closePacket = new ContainerClosePacket();
closePacket.setWindowId((byte) inventory.getId()); closePacket.setWindowId((byte) inventory.getId());
Geyser.getGeneralThreadPool().schedule(() -> session.getUpstream().sendPacket(closePacket), 150, TimeUnit.MILLISECONDS); Geyser.getGeneralThreadPool().schedule(() -> session.getUpstream().sendPacket(closePacket), 150, TimeUnit.MILLISECONDS);
} }*/
session.getInventory().setCursor(packet.getItem()); session.getInventory().setCursor(packet.getItem());
return; return;
@ -76,7 +79,7 @@ public class JavaSetSlotTranslator extends PacketTranslator<ServerSetSlotPacket>
InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType()); InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
if (translator != null) { if (translator != null) {
inventory.getItems()[packet.getSlot()] = packet.getItem(); inventory.setItem(packet.getSlot(), packet.getItem());
translator.updateSlot(session, inventory, packet.getSlot()); translator.updateSlot(session, inventory, packet.getSlot());
} }
} }

Datei anzeigen

@ -44,14 +44,13 @@ public class InventoryUtils {
session.getInventoryCache().uncacheInventory(windowId); session.getInventoryCache().uncacheInventory(windowId);
session.getInventoryCache().setOpenInventory(null); session.getInventoryCache().setOpenInventory(null);
} }
} else {
Inventory inventory = session.getInventory();
InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
translator.updateInventory(session, inventory);
} }
} session.setCraftSlot(0);
session.getInventory().setCursor(null);
//currently, ItemStack.equals() does not check the item id
public static boolean canCombine(ItemData stack1, ItemData stack2) {
if (stack1 == null || stack2 == null)
return false;
return stack1.getId() == stack2.getId() && stack1.equals(stack2, false, true, true);
} }
//NPE if nbt tag is null //NPE if nbt tag is null