forked from GeyserMC/Geyser
Start adding support for crafting
Expect bugs
This commit is contained in:
parent
af85d6e4d2
commit
489c39e900
16 changed files with 541 additions and 182 deletions
|
@ -52,7 +52,6 @@ public class Inventory {
|
|||
@Setter
|
||||
protected String title;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
protected ItemStack[] items;
|
||||
|
||||
|
@ -82,4 +81,10 @@ public class Inventory {
|
|||
public ItemStack getItem(int 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public class PlayerInventory extends Inventory {
|
|||
}
|
||||
|
||||
public void setCursor(ItemStack stack) {
|
||||
if (stack != null && stack.getId() == 0)
|
||||
if (stack != null && (stack.getId() == 0 || stack.getAmount() < 1))
|
||||
stack = null;
|
||||
cursor = stack;
|
||||
}
|
||||
|
|
|
@ -105,11 +105,9 @@ public class GeyserSession implements Player {
|
|||
@Setter
|
||||
private GameMode gameMode = GameMode.SURVIVAL;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private int lastClickedSlot;
|
||||
private int craftSlot = 0;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private int reopeningWindow = -1;
|
||||
|
||||
|
|
|
@ -99,6 +99,7 @@ public class TranslatorsInit {
|
|||
Registry.registerJava(ServerRespawnPacket.class, new JavaRespawnTranslator());
|
||||
Registry.registerJava(ServerSpawnPositionPacket.class, new JavaSpawnPositionTranslator());
|
||||
Registry.registerJava(ServerDifficultyPacket.class, new JavaDifficultyTranslator());
|
||||
Registry.registerJava(ServerDeclareRecipesPacket.class, new JavaDeclareRecipesTranslator());
|
||||
|
||||
Registry.registerJava(ServerEntityAnimationPacket.class, new JavaEntityAnimationTranslator());
|
||||
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.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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.connector.network.translators.bedrock;
|
||||
|
||||
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.ClientRenameItemPacket;
|
||||
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 org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.inventory.Inventory;
|
||||
import org.geysermc.connector.inventory.PlayerInventory;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.TranslatorsInit;
|
||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||
import org.geysermc.connector.utils.InventoryUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
|
||||
public class BedrockInventoryTransactionTranslator extends PacketTranslator<InventoryTransactionPacket> {
|
||||
private final ItemStack refreshItem = new ItemStack(1, 127, new CompoundTag(""));
|
||||
|
||||
@Override
|
||||
public void translate(InventoryTransactionPacket packet, GeyserSession session) {
|
||||
switch (packet.getTransactionType()) {
|
||||
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();
|
||||
if (inventory == null)
|
||||
inventory = session.getInventory();
|
||||
InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
|
||||
|
||||
int craftSlot = session.getCraftSlot();
|
||||
session.setCraftSlot(0);
|
||||
|
||||
if (session.getGameMode() == GameMode.CREATIVE && inventory.getId() == 0) {
|
||||
ItemStack javaItem;
|
||||
for (InventoryAction action : packet.getActions()) {
|
||||
|
@ -87,17 +97,18 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
break;
|
||||
}
|
||||
}
|
||||
ClientCreativeInventoryActionPacket creativePacket = new ClientCreativeInventoryActionPacket(javaSlot, InventoryUtils.fixStack(javaItem));
|
||||
ClientCreativeInventoryActionPacket creativePacket = new ClientCreativeInventoryActionPacket(javaSlot, fixStack(javaItem));
|
||||
session.getDownstream().getSession().send(creativePacket);
|
||||
inventory.getItems()[javaSlot] = javaItem;
|
||||
inventory.setItem(javaSlot, javaItem);
|
||||
break;
|
||||
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());
|
||||
if (javaItem.getId() == 0) { //item missing mapping
|
||||
break;
|
||||
}
|
||||
ClientCreativeInventoryActionPacket creativeDropPacket = new ClientCreativeInventoryActionPacket(-1, InventoryUtils.fixStack(javaItem));
|
||||
ClientCreativeInventoryActionPacket creativeDropPacket = new ClientCreativeInventoryActionPacket(-1, fixStack(javaItem));
|
||||
session.getDownstream().getSession().send(creativeDropPacket);
|
||||
}
|
||||
break;
|
||||
|
@ -111,11 +122,10 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
for (InventoryAction action : packet.getActions()) {
|
||||
if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION) {
|
||||
worldAction = action;
|
||||
} else if (action.getSource().getContainerId() == ContainerId.CURSOR) {
|
||||
} else if (action.getSource().getContainerId() == ContainerId.CURSOR && action.getSlot() == 0) {
|
||||
cursorAction = action;
|
||||
}
|
||||
}
|
||||
|
||||
List<InventoryAction> actions = packet.getActions();
|
||||
if (inventory.getWindowType() == WindowType.ANVIL) {
|
||||
InventoryAction anvilResult = null;
|
||||
|
@ -132,7 +142,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
itemName = anvilResult.getFromItem();
|
||||
actions = new ArrayList<>(2);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +164,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
}
|
||||
|
||||
if (actions.size() == 2) {
|
||||
if (worldAction != null && worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
|
||||
if (worldAction != null) {
|
||||
//find container action
|
||||
InventoryAction containerAction = null;
|
||||
for (InventoryAction action : actions) {
|
||||
|
@ -162,30 +173,54 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (containerAction != null) {
|
||||
if (containerAction != null && worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
|
||||
//quick dropping from hotbar?
|
||||
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(
|
||||
containerAction.getToItem().getCount() == 0 ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM,
|
||||
new Position(0, 0, 0), BlockFace.DOWN);
|
||||
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;
|
||||
}
|
||||
}
|
||||
boolean leftClick = containerAction.getToItem().getCount() == 0;
|
||||
if (containerAction.getSource().getContainerId() != ContainerId.CURSOR) { //dropping directly from inventory
|
||||
int dropAmount = containerAction.getFromItem().getCount() - containerAction.getToItem().getCount();
|
||||
if (containerAction != cursorAction) { //dropping directly from inventory
|
||||
int javaSlot = translator.bedrockSlotToJava(containerAction);
|
||||
ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
|
||||
javaSlot, null, WindowAction.DROP_ITEM,
|
||||
leftClick ? DropItemParam.DROP_SELECTED_STACK : DropItemParam.DROP_FROM_SELECTED);
|
||||
session.getDownstream().getSession().send(dropPacket);
|
||||
if (dropAmount == containerAction.getFromItem().getCount()) {
|
||||
ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(),
|
||||
inventory.getTransactionId().getAndIncrement(),
|
||||
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;
|
||||
} else { //clicking outside of inventory
|
||||
ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
|
||||
-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);
|
||||
ItemStack cursor = session.getInventory().getCursor();
|
||||
if (cursor != null) {
|
||||
session.getInventory().setCursor(new ItemStack(cursor.getId(), dropAmount > 1 ? 0 : cursor.getAmount() - 1, cursor.getNbt()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -199,43 +234,81 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
}
|
||||
}
|
||||
if (containerAction != null) {
|
||||
if (InventoryUtils.canCombine(cursorAction.getFromItem(), cursorAction.getToItem())
|
||||
&& cursorAction.getToItem().getCount() > cursorAction.getFromItem().getCount()) { //fill stack
|
||||
int javaSlot = session.getLastClickedSlot();
|
||||
ClientWindowActionPacket fillStackPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
|
||||
javaSlot, null, WindowAction.FILL_STACK, FillStackParam.FILL);
|
||||
session.getDownstream().getSession().send(fillStackPacket);
|
||||
translator.updateInventory(session, inventory); //bedrock fill stack can sometimes differ from java version, refresh and let server change slots
|
||||
return;
|
||||
} else {
|
||||
//left/right click
|
||||
int javaSlot = translator.bedrockSlotToJava(containerAction);
|
||||
boolean rightClick;
|
||||
if (cursorAction.getFromItem().getCount() == 0) { //picking up item
|
||||
rightClick = containerAction.getToItem().getCount() != 0;
|
||||
} else { //releasing item
|
||||
rightClick = cursorAction.getToItem().getCount() != 0 && cursorAction.getFromItem().getCount() - cursorAction.getToItem().getCount() == 1;
|
||||
//left/right click
|
||||
List<ClickAction> plan = new ArrayList<>();
|
||||
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
|
||||
int javaSlot = translator.bedrockSlotToJava(containerAction);
|
||||
if (cursorAction.getFromItem().equals(containerAction.getToItem()) &&
|
||||
containerAction.getFromItem().equals(cursorAction.getToItem()) &&
|
||||
!canStack(cursorAction.getFromItem(), containerAction.getFromItem())) { //simple swap
|
||||
Click.LEFT.onSlot(javaSlot, plan);
|
||||
} else if (cursorAction.getFromItem().getCount() > cursorAction.getToItem().getCount()) { //release
|
||||
if (cursorAction.getToItem().getCount() == 0) {
|
||||
Click.LEFT.onSlot(javaSlot, plan);
|
||||
} else {
|
||||
int difference = cursorAction.getFromItem().getCount() - cursorAction.getToItem().getCount();
|
||||
for (int i = 0; i < difference; i++) {
|
||||
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 {
|
||||
//either moving 1 item or swapping 2 slots (touchscreen or one slot shift click)
|
||||
List<ClickAction> plan = new ArrayList<>();
|
||||
InventoryAction fromAction;
|
||||
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);
|
||||
toAction = actions.get(1);
|
||||
} else {
|
||||
|
@ -245,120 +318,93 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
int fromSlot = translator.bedrockSlotToJava(fromAction);
|
||||
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)
|
||||
//this is only possible by shift clicking
|
||||
if (translator.isOutputSlot(fromAction) && fromAction.getToItem().getCount() != 0) {
|
||||
ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
|
||||
fromSlot, InventoryUtils.fixStack(inventory.getItem(fromSlot)), WindowAction.SHIFT_CLICK_ITEM, ShiftClickItemParam.LEFT_CLICK);
|
||||
session.getDownstream().getSession().send(shiftClickPacket);
|
||||
inventory.getItems()[toSlot] = TranslatorsInit.getItemTranslator().translateToJava(toAction.getToItem());
|
||||
inventory.getItems()[fromSlot] = TranslatorsInit.getItemTranslator().translateToJava(fromAction.getToItem());
|
||||
return;
|
||||
} 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;
|
||||
}
|
||||
if ((inventory.getId() == 0 || inventory.getWindowType() == WindowType.CRAFTING) && fromSlot == 0) {
|
||||
if ((craftSlot != 0 && craftSlot != -2) && (inventory.getItem(toSlot) == null ||
|
||||
canStack(session.getInventory().getCursor(), inventory.getItem(toSlot)))) {
|
||||
boolean refresh = false;
|
||||
if (fromAction.getToItem().getCount() == 0) {
|
||||
refresh = true;
|
||||
Click.LEFT.onSlot(toSlot, plan);
|
||||
if (craftSlot != -1) {
|
||||
Click.LEFT.onSlot(craftSlot, plan);
|
||||
}
|
||||
} else {
|
||||
int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount();
|
||||
for (int i = 0; i < difference; i++) {
|
||||
Click.RIGHT.onSlot(toSlot, plan);
|
||||
}
|
||||
session.setCraftSlot(craftSlot);
|
||||
}
|
||||
}
|
||||
int javaSlot = translator.bedrockSlotToJava(sourceAction);
|
||||
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);
|
||||
executePlan(session, inventory, translator, plan, refresh);
|
||||
return;
|
||||
} 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);
|
||||
break;
|
||||
case INVENTORY_MISMATCH:
|
||||
InventorySlotPacket cursorPacket = new InventorySlotPacket();
|
||||
cursorPacket.setContainerId(ContainerId.CURSOR);
|
||||
cursorPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(session.getInventory().getCursor()));
|
||||
session.getUpstream().sendPacket(cursorPacket);
|
||||
//session.getUpstream().sendPacket(cursorPacket);
|
||||
|
||||
Inventory inv = session.getInventoryCache().getOpenInventory();
|
||||
if (inv == null)
|
||||
inv = session.getInventory();
|
||||
TranslatorsInit.getInventoryTranslators().get(inv.getWindowType()).updateInventory(session, inv);
|
||||
break;
|
||||
case ITEM_USE:
|
||||
if (packet.getActionType() == 1) {
|
||||
ClientPlayerUseItemPacket useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
||||
|
@ -389,4 +435,134 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ public class AnvilInventoryTranslator extends BlockInventoryTranslator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutputSlot(InventoryAction action) {
|
||||
return action.getSource().getContainerId() == ContainerId.ANVIL_RESULT;
|
||||
public boolean isOutputSlot(int slot) {
|
||||
return slot == 2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ public abstract class ContainerInventoryTranslator extends InventoryTranslator {
|
|||
public void updateInventory(GeyserSession session, Inventory inventory) {
|
||||
ItemData[] bedrockItems = new ItemData[this.size];
|
||||
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();
|
||||
contentPacket.setContainerId(inventory.getId());
|
||||
|
@ -52,7 +52,7 @@ public abstract class ContainerInventoryTranslator extends InventoryTranslator {
|
|||
|
||||
Inventory playerInventory = session.getInventory();
|
||||
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);
|
||||
}
|
||||
|
@ -61,13 +61,13 @@ public abstract class ContainerInventoryTranslator extends InventoryTranslator {
|
|||
public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
|
||||
if (slot >= this.size) {
|
||||
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);
|
||||
} else {
|
||||
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||
slotPacket.setContainerId(inventory.getId());
|
||||
slotPacket.setInventorySlot(javaSlotToBedrock(slot));
|
||||
slotPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[slot]));
|
||||
slotPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(slot)));
|
||||
session.getUpstream().sendPacket(slotPacket);
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ public abstract class ContainerInventoryTranslator extends InventoryTranslator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutputSlot(InventoryAction action) {
|
||||
public boolean isOutputSlot(int slot) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package org.geysermc.connector.network.translators.inventory;
|
||||
|
||||
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.InventoryAction;
|
||||
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
|
||||
import org.geysermc.connector.inventory.Inventory;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
@ -30,4 +32,24 @@ public class CraftingTableInventoryTranslator extends ContainerInventoryTranslat
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ public class DoubleChestInventoryTranslator extends BlockInventoryTranslator {
|
|||
ItemData[] bedrockItems = new ItemData[54];
|
||||
for (int i = 0; i < bedrockItems.length; i++) {
|
||||
if (i <= this.size) {
|
||||
bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
|
||||
bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
|
||||
} else {
|
||||
bedrockItems[i] = ItemData.AIR;
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ public class DoubleChestInventoryTranslator extends BlockInventoryTranslator {
|
|||
|
||||
Inventory playerInventory = session.getInventory();
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ public class FurnaceInventoryTranslator extends BlockInventoryTranslator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutputSlot(InventoryAction action) {
|
||||
return action.getSlot() == 2;
|
||||
public boolean isOutputSlot(int slot) {
|
||||
return slot == 2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,5 +44,5 @@ public abstract class InventoryTranslator {
|
|||
public abstract void updateSlot(GeyserSession session, Inventory inventory, int slot);
|
||||
public abstract int bedrockSlotToJava(InventoryAction action);
|
||||
public abstract int javaSlotToBedrock(int slot);
|
||||
public abstract boolean isOutputSlot(InventoryAction action);
|
||||
public abstract boolean isOutputSlot(int slot);
|
||||
}
|
||||
|
|
|
@ -45,12 +45,12 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
|
|||
ItemData[] contents = new ItemData[36];
|
||||
// Inventory
|
||||
for (int i = 9; i < 36; i++) {
|
||||
contents[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
|
||||
contents[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
|
||||
}
|
||||
|
||||
// Hotbar
|
||||
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);
|
||||
|
@ -61,7 +61,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
|
|||
armorContentPacket.setContainerId(ContainerId.ARMOR);
|
||||
contents = new ItemData[4];
|
||||
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);
|
||||
session.getUpstream().sendPacket(armorContentPacket);
|
||||
|
@ -124,6 +124,12 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
|
|||
case ContainerId.CRAFTING_ADD_INGREDIENT:
|
||||
case ContainerId.CRAFTING_REMOVE_INGREDIENT:
|
||||
return slotnum + 1;
|
||||
case ContainerId.CURSOR:
|
||||
if (slotnum >= 28 && 31 >= slotnum) {
|
||||
return slotnum - 27;
|
||||
} else if (slotnum == 50) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return slotnum;
|
||||
}
|
||||
|
@ -134,7 +140,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutputSlot(InventoryAction action) {
|
||||
public boolean isOutputSlot(int slot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ public class SingleChestInventoryTranslator extends BlockInventoryTranslator {
|
|||
ItemData[] bedrockItems = new ItemData[27];
|
||||
for (int i = 0; i < bedrockItems.length; i++) {
|
||||
if (i <= this.size) {
|
||||
bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
|
||||
bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
|
||||
} else {
|
||||
bedrockItems[i] = ItemData.AIR;
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ public class SingleChestInventoryTranslator extends BlockInventoryTranslator {
|
|||
|
||||
Inventory playerInventory = session.getInventory();
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -47,9 +47,12 @@ public class JavaSetSlotTranslator extends PacketTranslator<ServerSetSlotPacket>
|
|||
if (packet.getWindowId() == 255 && packet.getSlot() == -1) { //cursor
|
||||
if (Objects.equals(session.getInventory().getCursor(), packet.getItem()))
|
||||
return;
|
||||
if (session.getCraftSlot() != 0)
|
||||
return;
|
||||
|
||||
//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();
|
||||
cursorPacket.setContainerId(ContainerId.CURSOR);
|
||||
cursorPacket.setSlot(ItemData.AIR);
|
||||
|
@ -64,7 +67,7 @@ public class JavaSetSlotTranslator extends PacketTranslator<ServerSetSlotPacket>
|
|||
ContainerClosePacket closePacket = new ContainerClosePacket();
|
||||
closePacket.setWindowId((byte) inventory.getId());
|
||||
Geyser.getGeneralThreadPool().schedule(() -> session.getUpstream().sendPacket(closePacket), 150, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}*/
|
||||
|
||||
session.getInventory().setCursor(packet.getItem());
|
||||
return;
|
||||
|
@ -76,7 +79,7 @@ public class JavaSetSlotTranslator extends PacketTranslator<ServerSetSlotPacket>
|
|||
|
||||
InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
|
||||
if (translator != null) {
|
||||
inventory.getItems()[packet.getSlot()] = packet.getItem();
|
||||
inventory.setItem(packet.getSlot(), packet.getItem());
|
||||
translator.updateSlot(session, inventory, packet.getSlot());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,14 +44,13 @@ public class InventoryUtils {
|
|||
session.getInventoryCache().uncacheInventory(windowId);
|
||||
session.getInventoryCache().setOpenInventory(null);
|
||||
}
|
||||
} else {
|
||||
Inventory inventory = session.getInventory();
|
||||
InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
|
||||
translator.updateInventory(session, inventory);
|
||||
}
|
||||
}
|
||||
|
||||
//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);
|
||||
session.setCraftSlot(0);
|
||||
session.getInventory().setCursor(null);
|
||||
}
|
||||
|
||||
//NPE if nbt tag is null
|
||||
|
|
Loading…
Reference in a new issue