Merge pull request #67 from AJ-Ferguson/inventory

Inventory Transactions (Work in Progress)
This commit is contained in:
Redned 2020-02-29 13:28:45 -06:00 committed by GitHub
commit 964663f6a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 2835 additions and 590 deletions

View File

@ -79,7 +79,7 @@
<dependency>
<groupId>com.github.steveice10</groupId>
<artifactId>opennbt</artifactId>
<version>1.3-SNAPSHOT</version>
<version>1.4-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>

View File

@ -27,9 +27,12 @@ package org.geysermc.connector.inventory;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
import com.nukkitx.math.vector.Vector3i;
import lombok.Getter;
import lombok.Setter;
import java.util.concurrent.atomic.AtomicInteger;
public class Inventory {
@Getter
@ -43,16 +46,26 @@ public class Inventory {
protected WindowType windowType;
@Getter
protected int size;
protected final int size;
@Getter
@Setter
protected String title;
@Getter
@Setter
protected ItemStack[] items;
@Getter
@Setter
protected Vector3i holderPosition = Vector3i.ZERO;
@Getter
@Setter
protected long holderId = -1;
@Getter
protected AtomicInteger transactionId = new AtomicInteger(1);
public Inventory(int id, WindowType windowType, int size) {
this("Inventory", id, windowType, size);
}
@ -62,11 +75,16 @@ public class Inventory {
this.id = id;
this.windowType = windowType;
this.size = size;
this.items = new ItemStack[size];
}
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;
}
}

View File

@ -35,12 +35,20 @@ public class PlayerInventory extends Inventory {
@Setter
private int heldItemSlot;
public PlayerInventory() {
super(0, null, 45);
@Getter
private ItemStack cursor;
public PlayerInventory() {
super(0, null, 46);
heldItemSlot = 0;
}
public void setCursor(ItemStack stack) {
if (stack != null && (stack.getId() == 0 || stack.getAmount() < 1))
stack = null;
cursor = stack;
}
public ItemStack getItemInHand() {
return items[heldItemSlot];
}

View File

@ -44,6 +44,7 @@ import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.protocol.bedrock.BedrockServerSession;
import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.GamePublishSetting;
import com.nukkitx.protocol.bedrock.data.GameRuleData;
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
@ -111,6 +112,9 @@ public class GeyserSession implements Player {
private boolean manyDimPackets = false;
private ServerRespawnPacket lastDimPacket = null;
@Setter
private int craftSlot = 0;
public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) {
this.connector = connector;
this.upstream = new UpstreamSession(bedrockServerSession);
@ -150,6 +154,11 @@ public class GeyserSession implements Player {
entityPacket.setTag(CompoundTag.EMPTY);
upstream.sendPacket(entityPacket);
InventoryContentPacket creativePacket = new InventoryContentPacket();
creativePacket.setContainerId(ContainerId.CREATIVE);
creativePacket.setContents(Toolbox.CREATIVE_ITEMS);
upstream.sendPacket(creativePacket);
PlayStatusPacket playStatusPacket = new PlayStatusPacket();
playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
upstream.sendPacket(playStatusPacket);

View File

@ -25,15 +25,12 @@
package org.geysermc.connector.network.session.cache;
import com.github.steveice10.packetlib.packet.Packet;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class InventoryCache {
@ -47,9 +44,6 @@ public class InventoryCache {
@Getter
private Map<Integer, Inventory> inventories = new HashMap<Integer, Inventory>();
@Getter
private Map<Integer, List<Packet>> cachedPackets = new HashMap<Integer, List<Packet>>();
public InventoryCache(GeyserSession session) {
this.session = session;
}
@ -65,10 +59,4 @@ public class InventoryCache {
public void uncacheInventory(int id) {
inventories.remove(id);
}
public void cachePacket(int id, Packet packet) {
List<Packet> packets = cachedPackets.getOrDefault(id, new ArrayList<Packet>());
packets.add(packet);
cachedPackets.put(id, packets);
}
}

View File

@ -25,6 +25,7 @@
package org.geysermc.connector.network.translators;
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
import com.github.steveice10.mc.protocol.packet.ingame.server.*;
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.*;
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.*;
@ -33,37 +34,36 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerD
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerScoreboardObjectivePacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerTeamPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerUpdateScorePacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.*;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.*;
import com.nukkitx.nbt.CompoundTagBuilder;
import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.nbt.stream.NBTOutputStream;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import com.nukkitx.protocol.bedrock.packet.*;
import lombok.Getter;
import org.geysermc.connector.network.translators.bedrock.*;
import org.geysermc.connector.network.translators.block.BlockTranslator;
import org.geysermc.connector.network.translators.inventory.GenericInventoryTranslator;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.network.translators.inventory.*;
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.network.translators.java.*;
import org.geysermc.connector.network.translators.java.entity.*;
import org.geysermc.connector.network.translators.java.entity.player.*;
import org.geysermc.connector.network.translators.java.entity.spawn.*;
import org.geysermc.connector.network.translators.java.inventory.OpenWindowPacketTranslator;
import org.geysermc.connector.network.translators.java.scoreboard.JavaDisplayScoreboardTranslator;
import org.geysermc.connector.network.translators.java.scoreboard.JavaScoreboardObjectiveTranslator;
import org.geysermc.connector.network.translators.java.scoreboard.JavaTeamTranslator;
import org.geysermc.connector.network.translators.java.scoreboard.JavaUpdateScoreTranslator;
import org.geysermc.connector.network.translators.java.window.JavaOpenWindowTranslator;
import org.geysermc.connector.network.translators.java.window.JavaSetSlotTranslator;
import org.geysermc.connector.network.translators.java.window.JavaWindowItemsTranslator;
import org.geysermc.connector.network.translators.java.window.*;
import org.geysermc.connector.network.translators.java.world.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class TranslatorsInit {
@ -71,7 +71,7 @@ public class TranslatorsInit {
private static ItemTranslator itemTranslator;
@Getter
private static InventoryTranslator inventoryTranslator = new GenericInventoryTranslator();
private static Map<WindowType, InventoryTranslator> inventoryTranslators = new HashMap<>();
private static final CompoundTag EMPTY_TAG = CompoundTagBuilder.builder().buildRootTag();
public static final byte[] EMPTY_LEVEL_CHUNK_DATA;
@ -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());
@ -125,15 +126,13 @@ public class TranslatorsInit {
Registry.registerJava(ServerPlayerSetExperiencePacket.class, new JavaPlayerSetExperienceTranslator());
Registry.registerJava(ServerPlayerHealthPacket.class, new JavaPlayerHealthTranslator());
Registry.registerJava(ServerPlayerActionAckPacket.class, new JavaPlayerActionAckTranslator());
Registry.registerJava(ServerPlayerChangeHeldItemPacket.class, new JavaPlayerChangeHeldItemTranslator());
Registry.registerJava(ServerPlayerAbilitiesPacket.class, new JavaPlayerAbilitiesTranslator());
Registry.registerJava(ServerNotifyClientPacket.class, new JavaNotifyClientTranslator());
Registry.registerJava(ServerChunkDataPacket.class, new JavaChunkDataTranslator());
Registry.registerJava(ServerEntityDestroyPacket.class, new JavaEntityDestroyTranslator());
Registry.registerJava(ServerWindowItemsPacket.class, new JavaWindowItemsTranslator());
Registry.registerJava(ServerOpenWindowPacket.class, new JavaOpenWindowTranslator());
Registry.registerJava(ServerSetSlotPacket.class, new JavaSetSlotTranslator());
Registry.registerJava(ServerScoreboardObjectivePacket.class, new JavaScoreboardObjectiveTranslator());
Registry.registerJava(ServerDisplayScoreboardPacket.class, new JavaDisplayScoreboardTranslator());
Registry.registerJava(ServerUpdateScorePacket.class, new JavaUpdateScoreTranslator());
@ -142,11 +141,16 @@ public class TranslatorsInit {
Registry.registerJava(ServerMultiBlockChangePacket.class, new JavaMultiBlockChangeTranslator());
Registry.registerJava(ServerUnloadChunkPacket.class, new JavaUnloadChunkTranslator());
Registry.registerJava(ServerWindowItemsPacket.class, new JavaWindowItemsTranslator());
Registry.registerJava(ServerOpenWindowPacket.class, new JavaOpenWindowTranslator());
Registry.registerJava(ServerSetSlotPacket.class, new JavaSetSlotTranslator());
Registry.registerJava(ServerCloseWindowPacket.class, new JavaCloseWindowTranslator());
Registry.registerJava(ServerConfirmTransactionPacket.class, new JavaConfirmTransactionTranslator());
Registry.registerJava(ServerWindowPropertyPacket.class, new JavaWindowPropertyTranslator());
Registry.registerJava(ServerUpdateViewPositionPacket.class, new JavaUpdateViewPositionTranslator());
Registry.registerJava(ServerUpdateViewDistancePacket.class, new JavaUpdateViewDistanceTranslator());
Registry.registerJava(ServerOpenWindowPacket.class, new OpenWindowPacketTranslator());
Registry.registerBedrock(AnimatePacket.class, new BedrockAnimateTranslator());
Registry.registerBedrock(CommandRequestPacket.class, new BedrockCommandRequestTranslator());
Registry.registerBedrock(InventoryTransactionPacket.class, new BedrockInventoryTransactionTranslator());
@ -156,6 +160,7 @@ public class TranslatorsInit {
Registry.registerBedrock(SetLocalPlayerAsInitializedPacket.class, new BedrockPlayerInitializedTranslator());
Registry.registerBedrock(InteractPacket.class, new BedrockInteractTranslator());
Registry.registerBedrock(TextPacket.class, new BedrockTextTranslator());
Registry.registerBedrock(ContainerClosePacket.class, new BedrockContainerCloseTranslator());
Registry.registerBedrock(RespawnPacket.class, new BedrockRespawnTranslator());
Registry.registerBedrock(ShowCreditsPacket.class, new BedrockShowCreditsTranslator());
@ -166,11 +171,27 @@ public class TranslatorsInit {
}
private static void registerInventoryTranslators() {
/*inventoryTranslators.put(WindowType.GENERIC_9X1, new GenericInventoryTranslator());
inventoryTranslators.put(WindowType.GENERIC_9X2, new GenericInventoryTranslator());
inventoryTranslators.put(WindowType.GENERIC_9X3, new GenericInventoryTranslator());
inventoryTranslators.put(WindowType.GENERIC_9X4, new GenericInventoryTranslator());
inventoryTranslators.put(WindowType.GENERIC_9X5, new GenericInventoryTranslator());
inventoryTranslators.put(WindowType.GENERIC_9X6, new GenericInventoryTranslator());*/
inventoryTranslators.put(null, new PlayerInventoryTranslator()); //player inventory
inventoryTranslators.put(WindowType.GENERIC_9X1, new SingleChestInventoryTranslator(9));
inventoryTranslators.put(WindowType.GENERIC_9X2, new SingleChestInventoryTranslator(18));
inventoryTranslators.put(WindowType.GENERIC_9X3, new SingleChestInventoryTranslator(27));
inventoryTranslators.put(WindowType.GENERIC_9X4, new DoubleChestInventoryTranslator(36));
inventoryTranslators.put(WindowType.GENERIC_9X5, new DoubleChestInventoryTranslator(45));
inventoryTranslators.put(WindowType.GENERIC_9X6, new DoubleChestInventoryTranslator(54));
inventoryTranslators.put(WindowType.BREWING_STAND, new BrewingInventoryTranslator());
inventoryTranslators.put(WindowType.ANVIL, new AnvilInventoryTranslator());
inventoryTranslators.put(WindowType.CRAFTING, new CraftingInventoryTranslator());
//inventoryTranslators.put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO
InventoryTranslator furnace = new FurnaceInventoryTranslator();
inventoryTranslators.put(WindowType.FURNACE, furnace);
inventoryTranslators.put(WindowType.BLAST_FURNACE, furnace);
inventoryTranslators.put(WindowType.SMOKER, furnace);
InventoryUpdater containerUpdater = new ContainerInventoryUpdater();
inventoryTranslators.put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER, containerUpdater));
inventoryTranslators.put(WindowType.HOPPER, new BlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, containerUpdater));
inventoryTranslators.put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER, containerUpdater));
//inventoryTranslators.put(WindowType.BEACON, new BlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.bedrock;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
import org.geysermc.connector.inventory.Inventory;
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;
public class BedrockContainerCloseTranslator extends PacketTranslator<ContainerClosePacket> {
@Override
public void translate(ContainerClosePacket packet, GeyserSession session) {
byte windowId = packet.getWindowId();
if (windowId == -1) { //player inventory or crafting table
Inventory openInventory = session.getInventoryCache().getOpenInventory();
if (openInventory != null) {
windowId = (byte) openInventory.getId();
} else {
windowId = 0;
}
}
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
session.getDownstream().getSession().send(closeWindowPacket);
InventoryUtils.closeInventory(session, windowId);
}
}

View File

@ -25,6 +25,7 @@
package org.geysermc.connector.network.translators.bedrock;
import com.nukkitx.math.vector.Vector3f;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
@ -34,17 +35,32 @@ import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.inventory.Inventory;
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.utils.InventoryUtils;
import java.util.*;
public class BedrockInventoryTransactionTranslator extends PacketTranslator<InventoryTransactionPacket> {
@Override
public void translate(InventoryTransactionPacket packet, GeyserSession session) {
switch (packet.getTransactionType()) {
case NORMAL:
Inventory inventory = session.getInventoryCache().getOpenInventory();
if (inventory == null) inventory = session.getInventory();
TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType()).translateActions(session, inventory, packet.getActions());
break;
case INVENTORY_MISMATCH:
Inventory inv = session.getInventoryCache().getOpenInventory();
if (inv == null) inv = session.getInventory();
TranslatorsInit.getInventoryTranslators().get(inv.getWindowType()).updateInventory(session, inv);
InventoryUtils.updateCursor(session);
break;
case ITEM_USE:
if (packet.getActionType() == 1) {
ClientPlayerUseItemPacket useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);

View File

@ -48,6 +48,7 @@ public class BlockTranslator {
private static final Int2IntMap JAVA_TO_BEDROCK_BLOCK_MAP = new Int2IntOpenHashMap();
private static final Int2ObjectMap<BlockState> BEDROCK_TO_JAVA_BLOCK_MAP = new Int2ObjectOpenHashMap<>();
private static final Map<String, BlockState> JAVA_ID_BLOCK_MAP = new HashMap<>();
private static final IntSet WATERLOGGED = new IntOpenHashSet();
private static final int BLOCK_STATE_VERSION = 17760256;
@ -89,18 +90,21 @@ public class BlockTranslator {
javaRuntimeId++;
Map.Entry<String, JsonNode> entry = blocksIterator.next();
String javaId = entry.getKey();
BlockState javaBlockState = new BlockState(javaRuntimeId);
CompoundTag blockTag = buildBedrockState(entry.getValue());
JAVA_ID_BLOCK_MAP.put(javaId, javaBlockState);
if ("minecraft:water[level=0]".equals(javaId)) {
waterRuntimeId = bedrockRuntimeId;
}
boolean waterlogged = entry.getValue().has("waterlogged") && entry.getValue().get("waterlogged").booleanValue();
if (waterlogged) {
BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId | 1 << 31, new BlockState(javaRuntimeId));
BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId | 1 << 31, javaBlockState);
WATERLOGGED.add(javaRuntimeId);
} else {
BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId, new BlockState(javaRuntimeId));
BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId, javaBlockState);
}
CompoundTag runtimeTag = blockStateMap.remove(blockTag);
@ -175,6 +179,10 @@ public class BlockTranslator {
return BEDROCK_TO_JAVA_BLOCK_MAP.get(bedrockId);
}
public static BlockState getJavaBlockState(String javaId) {
return JAVA_ID_BLOCK_MAP.get(javaId);
}
public static boolean isWaterlogged(BlockState state) {
return WATERLOGGED.contains(state.getId());
}

View File

@ -0,0 +1,117 @@
/*
* Copyright (c) 2019-2020 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.inventory;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientRenameItemPacket;
import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import com.nukkitx.protocol.bedrock.data.InventoryActionData;
import com.nukkitx.protocol.bedrock.data.ItemData;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
import java.util.List;
public class AnvilInventoryTranslator extends BlockInventoryTranslator {
public AnvilInventoryTranslator() {
super(3, "minecraft:anvil[facing=north]", ContainerType.ANVIL, new CursorInventoryUpdater());
}
@Override
public int bedrockSlotToJava(InventoryActionData action) {
if (action.getSource().getContainerId() == ContainerId.CURSOR) {
switch (action.getSlot()) {
case 1:
return 0;
case 2:
return 1;
case 50:
return 2;
}
}
return super.bedrockSlotToJava(action);
}
@Override
public int javaSlotToBedrock(int slot) {
switch (slot) {
case 0:
return 1;
case 1:
return 2;
case 2:
return 50;
}
return super.javaSlotToBedrock(slot);
}
@Override
public SlotType getSlotType(int javaSlot) {
if (javaSlot == 2)
return SlotType.OUTPUT;
return SlotType.NORMAL;
}
@Override
public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
InventoryActionData anvilResult = null;
InventoryActionData anvilInput = null;
for (InventoryActionData action : actions) {
if (action.getSource().getContainerId() == ContainerId.ANVIL_MATERIAL) {
//useless packet
return;
} else if (action.getSource().getContainerId() == ContainerId.ANVIL_RESULT) {
anvilResult = action;
} else if (bedrockSlotToJava(action) == 0) {
anvilInput = action;
}
}
ItemData itemName = null;
if (anvilResult != null) {
itemName = anvilResult.getFromItem();
} else if (anvilInput != null) {
itemName = anvilInput.getToItem();
}
if (itemName != null) {
String rename;
com.nukkitx.nbt.tag.CompoundTag tag = itemName.getTag();
if (tag != null) {
rename = tag.getCompound("display").getString("Name");
} else {
rename = "";
}
ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename);
session.getDownstream().getSession().send(renameItemPacket);
}
if (anvilResult != null) {
//client will send another packet to grab anvil output
return;
}
super.translateActions(session, inventory, actions);
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2019-2020 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.inventory;
import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.InventoryActionData;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.action.InventoryActionDataTranslator;
import java.util.List;
public abstract class BaseInventoryTranslator extends InventoryTranslator{
BaseInventoryTranslator(int size) {
super(size);
}
@Override
public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
//
}
@Override
public int bedrockSlotToJava(InventoryActionData 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;
}
}
return slotnum;
}
@Override
public int javaSlotToBedrock(int slot) {
if (slot >= this.size) {
final int tmp = slot - this.size;
if (tmp < 27) {
return tmp + 9;
} else {
return tmp - 27;
}
}
return slot;
}
@Override
public SlotType getSlotType(int javaSlot) {
return SlotType.NORMAL;
}
@Override
public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
InventoryActionDataTranslator.translate(this, session, inventory, actions);
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2019-2020 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.inventory;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.block.BlockTranslator;
import org.geysermc.connector.network.translators.inventory.holder.BlockInventoryHolder;
import org.geysermc.connector.network.translators.inventory.holder.InventoryHolder;
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
public class BlockInventoryTranslator extends BaseInventoryTranslator {
private final InventoryHolder holder;
private final InventoryUpdater updater;
public BlockInventoryTranslator(int size, String javaBlockIdentifier, ContainerType containerType, InventoryUpdater updater) {
super(size);
BlockState javaBlockState = BlockTranslator.getJavaBlockState(javaBlockIdentifier);
int blockId = BlockTranslator.getBedrockBlockId(javaBlockState);
this.holder = new BlockInventoryHolder(blockId, containerType);
this.updater = updater;
}
@Override
public void prepareInventory(GeyserSession session, Inventory inventory) {
holder.prepareInventory(this, session, inventory);
}
@Override
public void openInventory(GeyserSession session, Inventory inventory) {
holder.openInventory(this, session, inventory);
}
@Override
public void closeInventory(GeyserSession session, Inventory inventory) {
holder.closeInventory(this, session, inventory);
}
@Override
public void updateInventory(GeyserSession session, Inventory inventory) {
updater.updateInventory(this, session, inventory);
}
@Override
public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
updater.updateSlot(this, session, inventory, slot);
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2019-2020 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.inventory;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import com.nukkitx.protocol.bedrock.data.InventoryActionData;
import com.nukkitx.protocol.bedrock.packet.ContainerSetDataPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
public class BrewingInventoryTranslator extends BlockInventoryTranslator {
public BrewingInventoryTranslator() {
super(5, "minecraft:brewing_stand[has_bottle_0=false,has_bottle_1=false,has_bottle_2=false]", ContainerType.BREWING_STAND, new ContainerInventoryUpdater());
}
@Override
public void openInventory(GeyserSession session, Inventory inventory) {
super.openInventory(session, inventory);
ContainerSetDataPacket dataPacket = new ContainerSetDataPacket();
dataPacket.setWindowId((byte) inventory.getId());
dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_FUEL_TOTAL);
dataPacket.setValue(20);
session.getUpstream().sendPacket(dataPacket);
}
@Override
public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
ContainerSetDataPacket dataPacket = new ContainerSetDataPacket();
dataPacket.setWindowId((byte) inventory.getId());
switch (key) {
case 0:
dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_BREW_TIME);
break;
case 1:
dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_FUEL_AMOUNT);
break;
default:
return;
}
dataPacket.setValue(value);
session.getUpstream().sendPacket(dataPacket);
}
@Override
public int bedrockSlotToJava(InventoryActionData action) {
final int slot = super.bedrockSlotToJava(action);
switch (slot) {
case 0:
return 3;
case 1:
return 0;
case 2:
return 1;
case 3:
return 2;
default:
return slot;
}
}
@Override
public int javaSlotToBedrock(int slot) {
switch (slot) {
case 0:
return 1;
case 1:
return 2;
case 2:
return 3;
case 3:
return 0;
}
return super.javaSlotToBedrock(slot);
}
}

View File

@ -0,0 +1,118 @@
/*
* Copyright (c) 2019-2020 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.inventory;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import com.nukkitx.protocol.bedrock.data.InventoryActionData;
import com.nukkitx.protocol.bedrock.data.InventorySource;
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
import org.geysermc.connector.utils.InventoryUtils;
import java.util.List;
public class CraftingInventoryTranslator extends BaseInventoryTranslator {
private final InventoryUpdater updater;
public CraftingInventoryTranslator() {
super(10);
this.updater = new CursorInventoryUpdater();
}
@Override
public void prepareInventory(GeyserSession session, Inventory inventory) {
//
}
@Override
public void openInventory(GeyserSession session, Inventory inventory) {
ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
containerOpenPacket.setWindowId((byte) inventory.getId());
containerOpenPacket.setType((byte) ContainerType.WORKBENCH.id());
containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
session.getUpstream().sendPacket(containerOpenPacket);
}
@Override
public void closeInventory(GeyserSession session, Inventory inventory) {
//
}
@Override
public void updateInventory(GeyserSession session, Inventory inventory) {
updater.updateInventory(this, session, inventory);
}
@Override
public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
updater.updateSlot(this, session, inventory, slot);
}
@Override
public int bedrockSlotToJava(InventoryActionData action) {
if (action.getSource().getContainerId() == ContainerId.CURSOR) {
int slotnum = action.getSlot();
if (slotnum >= 32 && 42 >= slotnum) {
return slotnum - 31;
} else if (slotnum == 50) {
return 0;
}
}
return super.bedrockSlotToJava(action);
}
@Override
public int javaSlotToBedrock(int slot) {
return slot == 0 ? 50 : slot + 31;
}
@Override
public SlotType getSlotType(int javaSlot) {
if (javaSlot == 0)
return SlotType.OUTPUT;
return SlotType.NORMAL;
}
@Override
public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
if (session.getGameMode() == GameMode.CREATIVE) {
for (InventoryActionData action : actions) {
if (action.getSource().getType() == InventorySource.Type.CREATIVE) {
updateInventory(session, inventory);
InventoryUtils.updateCursor(session);
return;
}
}
}
super.translateActions(session, inventory, actions);
}
}

View File

@ -0,0 +1,141 @@
/*
* Copyright (c) 2019-2020 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.inventory;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.block.BlockTranslator;
import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater;
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
private final int blockId;
private final InventoryUpdater updater;
public DoubleChestInventoryTranslator(int size) {
super(size);
BlockState javaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]");
this.blockId = BlockTranslator.getBedrockBlockId(javaBlockState);
this.updater = new ChestInventoryUpdater(54);
}
@Override
public void prepareInventory(GeyserSession session, Inventory inventory) {
Vector3i position = session.getPlayerEntity().getPosition().toInt().add(Vector3i.UP);
Vector3i pairPosition = position.add(Vector3i.UNIT_X);
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(position);
blockPacket.setRuntimeId(blockId);
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.getUpstream().sendPacket(blockPacket);
CompoundTag tag = CompoundTag.builder()
.stringTag("id", "Chest")
.intTag("x", position.getX())
.intTag("y", position.getY())
.intTag("z", position.getZ())
.intTag("pairx", pairPosition.getX())
.intTag("pairz", pairPosition.getZ())
.stringTag("CustomName", inventory.getTitle()).buildRootTag();
BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
dataPacket.setData(tag);
dataPacket.setBlockPosition(position);
session.getUpstream().sendPacket(dataPacket);
blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(pairPosition);
blockPacket.setRuntimeId(blockId);
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.getUpstream().sendPacket(blockPacket);
tag = CompoundTag.builder()
.stringTag("id", "Chest")
.intTag("x", pairPosition.getX())
.intTag("y", pairPosition.getY())
.intTag("z", pairPosition.getZ())
.intTag("pairx", position.getX())
.intTag("pairz", position.getZ())
.stringTag("CustomName", inventory.getTitle()).buildRootTag();
dataPacket = new BlockEntityDataPacket();
dataPacket.setData(tag);
dataPacket.setBlockPosition(pairPosition);
session.getUpstream().sendPacket(dataPacket);
inventory.setHolderPosition(position);
}
@Override
public void openInventory(GeyserSession session, Inventory inventory) {
ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
containerOpenPacket.setWindowId((byte) inventory.getId());
containerOpenPacket.setType((byte) ContainerType.CONTAINER.id());
containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
session.getUpstream().sendPacket(containerOpenPacket);
}
@Override
public void closeInventory(GeyserSession session, Inventory inventory) {
Vector3i holderPos = inventory.getHolderPosition();
Position pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
BlockState realBlock = session.getChunkCache().getBlockAt(pos);
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
session.getUpstream().sendPacket(blockPacket);
holderPos = holderPos.add(Vector3i.UNIT_X);
pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
realBlock = session.getChunkCache().getBlockAt(pos);
blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
session.getUpstream().sendPacket(blockPacket);
}
@Override
public void updateInventory(GeyserSession session, Inventory inventory) {
updater.updateInventory(this, session, inventory);
}
@Override
public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
updater.updateSlot(this, session, inventory, slot);
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2019-2020 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.inventory;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
public class EnchantmentInventoryTranslator extends BlockInventoryTranslator {
public EnchantmentInventoryTranslator() {
super(2, "minecraft:enchanting_table", ContainerType.ENCHANTMENT, new ContainerInventoryUpdater());
}
@Override
public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 2019-2020 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.inventory;
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import com.nukkitx.protocol.bedrock.packet.ContainerSetDataPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
public class FurnaceInventoryTranslator extends BlockInventoryTranslator {
public FurnaceInventoryTranslator() {
super(3, "minecraft:furnace[facing=north,lit=false]", ContainerType.FURNACE, new ContainerInventoryUpdater());
}
@Override
public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
ContainerSetDataPacket dataPacket = new ContainerSetDataPacket();
dataPacket.setWindowId((byte) inventory.getId());
switch (key) {
case 0:
dataPacket.setProperty(ContainerSetDataPacket.FURNACE_LIT_TIME);
break;
case 1:
dataPacket.setProperty(ContainerSetDataPacket.FURNACE_LIT_DURATION);
break;
case 2:
dataPacket.setProperty(ContainerSetDataPacket.FURNACE_TICK_COUNT);
if (inventory.getWindowType() == WindowType.BLAST_FURNACE || inventory.getWindowType() == WindowType.SMOKER) {
value *= 2;
}
break;
default:
return;
}
dataPacket.setValue(value);
session.getUpstream().sendPacket(dataPacket);
}
@Override
public SlotType getSlotType(int javaSlot) {
if (javaSlot == 2)
return SlotType.FURNACE_OUTPUT;
return SlotType.NORMAL;
}
}

View File

@ -25,14 +25,25 @@
package org.geysermc.connector.network.translators.inventory;
import com.nukkitx.protocol.bedrock.data.InventoryActionData;
import lombok.AllArgsConstructor;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.List;
@AllArgsConstructor
public abstract class InventoryTranslator {
public final int size;
public abstract void prepareInventory(GeyserSession session, Inventory inventory);
public abstract void openInventory(GeyserSession session, Inventory inventory);
public abstract void closeInventory(GeyserSession session, Inventory inventory);
public abstract void updateProperty(GeyserSession session, Inventory inventory, int key, int value);
public abstract void updateInventory(GeyserSession session, Inventory inventory);
public abstract void updateSlot(GeyserSession session, Inventory inventory, int slot);
public abstract int bedrockSlotToJava(InventoryActionData action);
public abstract int javaSlotToBedrock(int slot);
public abstract SlotType getSlotType(int javaSlot);
public abstract void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions);
}

View File

@ -0,0 +1,225 @@
/*
* Copyright (c) 2019-2020 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.inventory;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket;
import com.nukkitx.protocol.bedrock.data.*;
import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.TranslatorsInit;
import org.geysermc.connector.network.translators.inventory.action.InventoryActionDataTranslator;
import org.geysermc.connector.utils.InventoryUtils;
import java.util.List;
public class PlayerInventoryTranslator extends InventoryTranslator {
public PlayerInventoryTranslator() {
super(46);
}
@Override
public void updateInventory(GeyserSession session, Inventory inventory) {
// Crafting grid
for (int i = 1; i < 5; i++) {
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.CURSOR);
slotPacket.setSlot(i + 27);
slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i)));
session.getUpstream().sendPacket(slotPacket);
}
InventoryContentPacket inventoryContentPacket = new InventoryContentPacket();
inventoryContentPacket.setContainerId(ContainerId.INVENTORY);
ItemData[] contents = new ItemData[36];
// Inventory
for (int i = 9; i < 36; i++) {
contents[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
}
// Hotbar
for (int i = 36; i < 45; i++) {
contents[i - 36] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
}
inventoryContentPacket.setContents(contents);
session.getUpstream().sendPacket(inventoryContentPacket);
// Armor
InventoryContentPacket armorContentPacket = new InventoryContentPacket();
armorContentPacket.setContainerId(ContainerId.ARMOR);
contents = new ItemData[4];
for (int i = 5; i < 9; i++) {
contents[i - 5] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
}
armorContentPacket.setContents(contents);
session.getUpstream().sendPacket(armorContentPacket);
// Offhand
InventoryContentPacket offhandPacket = new InventoryContentPacket();
offhandPacket.setContainerId(ContainerId.OFFHAND);
offhandPacket.setContents(new ItemData[]{TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(45))});
session.getUpstream().sendPacket(offhandPacket);
}
@Override
public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
if (slot >= 1 && slot <= 44) {
InventorySlotPacket slotPacket = new InventorySlotPacket();
if (slot >= 9) {
slotPacket.setContainerId(ContainerId.INVENTORY);
if (slot >= 36) {
slotPacket.setSlot(slot - 36);
} else {
slotPacket.setSlot(slot);
}
} else if (slot >= 5) {
slotPacket.setContainerId(ContainerId.ARMOR);
slotPacket.setSlot(slot - 5);
} else {
slotPacket.setContainerId(ContainerId.CURSOR);
slotPacket.setSlot(slot + 27);
}
slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(slot)));
session.getUpstream().sendPacket(slotPacket);
} else if (slot == 45) {
InventoryContentPacket offhandPacket = new InventoryContentPacket();
offhandPacket.setContainerId(ContainerId.OFFHAND);
offhandPacket.setContents(new ItemData[]{TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(slot))});
session.getUpstream().sendPacket(offhandPacket);
}
}
@Override
public int bedrockSlotToJava(InventoryActionData action) {
int slotnum = action.getSlot();
switch (action.getSource().getContainerId()) {
case ContainerId.INVENTORY:
// Inventory
if (slotnum >= 9 && slotnum <= 35) {
return slotnum;
}
// Hotbar
if (slotnum >= 0 && slotnum <= 8) {
return slotnum + 36;
}
break;
case ContainerId.ARMOR:
if (slotnum >= 0 && slotnum <= 3) {
return slotnum + 5;
}
break;
case ContainerId.OFFHAND:
return 45;
case ContainerId.CURSOR:
if (slotnum >= 28 && 31 >= slotnum) {
return slotnum - 27;
} else if (slotnum == 50) {
return 0;
}
break;
}
return slotnum;
}
@Override
public int javaSlotToBedrock(int slot) {
return slot;
}
@Override
public SlotType getSlotType(int javaSlot) {
if (javaSlot == 0)
return SlotType.OUTPUT;
return SlotType.NORMAL;
}
@Override
public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
if (session.getGameMode() == GameMode.CREATIVE) {
//crafting grid is not visible in creative mode in java edition
for (InventoryActionData action : actions) {
if (action.getSource().getContainerId() == ContainerId.CURSOR && (action.getSlot() >= 28 && 31 >= action.getSlot())) {
updateInventory(session, inventory);
InventoryUtils.updateCursor(session);
return;
}
}
ItemStack javaItem;
for (InventoryActionData action : actions) {
switch (action.getSource().getContainerId()) {
case ContainerId.INVENTORY:
case ContainerId.ARMOR:
case ContainerId.OFFHAND:
int javaSlot = bedrockSlotToJava(action);
if (action.getToItem().getId() == 0) {
javaItem = new ItemStack(-1, 0, null);
} else {
javaItem = TranslatorsInit.getItemTranslator().translateToJava(action.getToItem());
}
ClientCreativeInventoryActionPacket creativePacket = new ClientCreativeInventoryActionPacket(javaSlot, javaItem);
session.getDownstream().getSession().send(creativePacket);
inventory.setItem(javaSlot, javaItem);
break;
case ContainerId.CURSOR:
if (action.getSlot() == 0) {
session.getInventory().setCursor(TranslatorsInit.getItemTranslator().translateToJava(action.getToItem()));
}
break;
case ContainerId.NONE:
if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION
&& action.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
javaItem = TranslatorsInit.getItemTranslator().translateToJava(action.getToItem());
ClientCreativeInventoryActionPacket creativeDropPacket = new ClientCreativeInventoryActionPacket(-1, javaItem);
session.getDownstream().getSession().send(creativeDropPacket);
}
break;
}
}
return;
}
InventoryActionDataTranslator.translate(this, session, inventory, actions);
}
@Override
public void prepareInventory(GeyserSession session, Inventory inventory) {
}
@Override
public void openInventory(GeyserSession session, Inventory inventory) {
}
@Override
public void closeInventory(GeyserSession session, Inventory inventory) {
}
@Override
public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2019-2020 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.inventory;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater;
public class SingleChestInventoryTranslator extends BlockInventoryTranslator {
public SingleChestInventoryTranslator(int size) {
super(size, "minecraft:chest[facing=north,type=single,waterlogged=false]", ContainerType.CONTAINER, new ChestInventoryUpdater(27));
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2019-2020 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.inventory;
public enum SlotType {
NORMAL,
OUTPUT,
FURNACE_OUTPUT
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2019-2020 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.inventory.action;
import com.github.steveice10.mc.protocol.data.game.window.ClickItemParam;
import com.github.steveice10.mc.protocol.data.game.window.WindowActionParam;
import lombok.AllArgsConstructor;
@AllArgsConstructor
enum Click {
LEFT(ClickItemParam.LEFT_CLICK),
RIGHT(ClickItemParam.RIGHT_CLICK);
public final WindowActionParam actionParam;
}

View File

@ -0,0 +1,125 @@
/*
* Copyright (c) 2019-2020 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.inventory.action;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.window.WindowAction;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientConfirmTransactionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientWindowActionPacket;
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.inventory.InventoryTranslator;
import org.geysermc.connector.network.translators.inventory.SlotType;
import org.geysermc.connector.utils.InventoryUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
class ClickPlan {
private final List<ClickAction> plan = new ArrayList<>();
public void add(Click click, int slot) {
plan.add(new ClickAction(click, slot));
}
public void execute(GeyserSession session, InventoryTranslator translator, Inventory inventory, boolean refresh) {
PlayerInventory playerInventory = session.getInventory();
ListIterator<ClickAction> planIter = plan.listIterator();
while (planIter.hasNext()) {
final ClickAction action = planIter.next();
final ItemStack cursorItem = playerInventory.getCursor();
final ItemStack clickedItem = inventory.getItem(action.slot);
final short actionId = (short) inventory.getTransactionId().getAndIncrement();
//TODO: stop relying on refreshing the inventory for crafting to work properly
if (translator.getSlotType(action.slot) != SlotType.NORMAL)
refresh = true;
ClientWindowActionPacket clickPacket = new ClientWindowActionPacket(inventory.getId(),
actionId, action.slot, !planIter.hasNext() && refresh ? InventoryUtils.REFRESH_ITEM : clickedItem,
WindowAction.CLICK_ITEM, action.click.actionParam);
if (translator.getSlotType(action.slot) == SlotType.OUTPUT) {
if (cursorItem == null && clickedItem != null) {
playerInventory.setCursor(clickedItem);
} else if (InventoryUtils.canStack(cursorItem, clickedItem)) {
playerInventory.setCursor(new ItemStack(cursorItem.getId(),
cursorItem.getAmount() + clickedItem.getAmount(), cursorItem.getNbt()));
}
} else {
switch (action.click) {
case LEFT:
if (!InventoryUtils.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 (InventoryUtils.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));
}
/*if (refresh) {
translator.updateInventory(session, inventory);
InventoryUtils.updateCursor(session);
}*/
}
private static class ClickAction {
final Click click;
final int slot;
ClickAction(Click click, int slot) {
this.click = click;
this.slot = slot;
}
}
}

View File

@ -0,0 +1,330 @@
/*
* Copyright (c) 2019-2020 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.inventory.action;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
import com.github.steveice10.mc.protocol.data.game.window.*;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientWindowActionPacket;
import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.InventoryActionData;
import com.nukkitx.protocol.bedrock.data.InventorySource;
import com.nukkitx.protocol.bedrock.data.ItemData;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.TranslatorsInit;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.network.translators.inventory.SlotType;
import org.geysermc.connector.utils.InventoryUtils;
import java.util.*;
public class InventoryActionDataTranslator {
public static void translate(InventoryTranslator translator, GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
if (actions.size() != 2)
return;
InventoryActionData worldAction = null;
InventoryActionData cursorAction = null;
InventoryActionData containerAction = null;
boolean refresh = false;
for (InventoryActionData action : actions) {
if (action.getSource().getContainerId() == ContainerId.CRAFTING_USE_INGREDIENT || action.getSource().getContainerId() == ContainerId.CRAFTING_RESULT) {
return;
} else if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION) {
worldAction = action;
} else if (action.getSource().getContainerId() == ContainerId.CURSOR && action.getSlot() == 0) {
cursorAction = action;
ItemData translatedCursor = TranslatorsInit.getItemTranslator().translateToBedrock(session.getInventory().getCursor());
if (!translatedCursor.equals(action.getFromItem())) {
refresh = true;
}
} else {
containerAction = action;
ItemData translatedItem = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(translator.bedrockSlotToJava(action)));
if (!translatedItem.equals(action.getFromItem())) {
refresh = true;
}
}
}
final int craftSlot = session.getCraftSlot();
session.setCraftSlot(0);
if (worldAction != null) {
InventoryActionData sourceAction;
if (cursorAction != null) {
sourceAction = cursorAction;
} else {
sourceAction = containerAction;
}
if (sourceAction != null) {
if (worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
//quick dropping from hotbar?
if (session.getInventoryCache().getOpenInventory() == null && sourceAction.getSource().getContainerId() == ContainerId.INVENTORY) {
int heldSlot = session.getInventory().getHeldItemSlot();
if (sourceAction.getSlot() == heldSlot) {
ClientPlayerActionPacket actionPacket = new ClientPlayerActionPacket(
sourceAction.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;
}
}
int dropAmount = sourceAction.getFromItem().getCount() - sourceAction.getToItem().getCount();
if (sourceAction != cursorAction) { //dropping directly from inventory
int javaSlot = translator.bedrockSlotToJava(sourceAction);
if (dropAmount == sourceAction.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,
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;
}
}
}
} else if (cursorAction != null && containerAction != null) {
//left/right click
ClickPlan plan = new ClickPlan();
int javaSlot = translator.bedrockSlotToJava(containerAction);
if (cursorAction.getFromItem().equals(containerAction.getToItem())
&& containerAction.getFromItem().equals(cursorAction.getToItem())
&& !InventoryUtils.canStack(cursorAction.getFromItem(), containerAction.getFromItem())) { //simple swap
plan.add(Click.LEFT, javaSlot);
} else if (cursorAction.getFromItem().getCount() > cursorAction.getToItem().getCount()) { //release
if (cursorAction.getToItem().getCount() == 0) {
plan.add(Click.LEFT, javaSlot);
} else {
int difference = cursorAction.getFromItem().getCount() - cursorAction.getToItem().getCount();
for (int i = 0; i < difference; i++) {
plan.add(Click.RIGHT, javaSlot);
}
}
} else { //pickup
if (cursorAction.getFromItem().getCount() == 0) {
if (containerAction.getToItem().getCount() == 0) { //pickup all
plan.add(Click.LEFT, javaSlot);
} else { //pickup some
if (translator.getSlotType(javaSlot) == SlotType.FURNACE_OUTPUT
|| containerAction.getToItem().getCount() == containerAction.getFromItem().getCount() / 2) { //right click
plan.add(Click.RIGHT, javaSlot);
} else {
plan.add(Click.LEFT, javaSlot);
int difference = containerAction.getFromItem().getCount() - cursorAction.getToItem().getCount();
for (int i = 0; i < difference; i++) {
plan.add(Click.RIGHT, javaSlot);
}
}
}
} else { //pickup into non-empty cursor
if (translator.getSlotType(javaSlot) == SlotType.FURNACE_OUTPUT) {
if (containerAction.getToItem().getCount() == 0) {
plan.add(Click.LEFT, javaSlot);
} else {
ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(),
inventory.getTransactionId().getAndIncrement(),
javaSlot, InventoryUtils.REFRESH_ITEM, WindowAction.SHIFT_CLICK_ITEM,
ShiftClickItemParam.LEFT_CLICK);
session.getDownstream().getSession().send(shiftClickPacket);
translator.updateInventory(session, inventory);
return;
}
} else if (translator.getSlotType(javaSlot) == SlotType.OUTPUT) {
plan.add(Click.LEFT, javaSlot);
} else {
int cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Collections.singletonList(javaSlot));
if (cursorSlot != -1) {
plan.add(Click.LEFT, cursorSlot);
} else {
translator.updateInventory(session, inventory);
return;
}
plan.add(Click.LEFT, javaSlot);
int difference = cursorAction.getToItem().getCount() - cursorAction.getFromItem().getCount();
for (int i = 0; i < difference; i++) {
plan.add(Click.RIGHT, cursorSlot);
}
plan.add(Click.LEFT, javaSlot);
plan.add(Click.LEFT, cursorSlot);
}
}
}
plan.execute(session, translator, inventory, refresh);
return;
} else {
ClickPlan plan = new ClickPlan();
InventoryActionData fromAction;
InventoryActionData toAction;
if (actions.get(0).getFromItem().getCount() >= actions.get(0).getToItem().getCount()) {
fromAction = actions.get(0);
toAction = actions.get(1);
} else {
fromAction = actions.get(1);
toAction = actions.get(0);
}
int fromSlot = translator.bedrockSlotToJava(fromAction);
int toSlot = translator.bedrockSlotToJava(toAction);
if (translator.getSlotType(fromSlot) == SlotType.OUTPUT) {
if ((craftSlot != 0 && craftSlot != -2) && (inventory.getItem(toSlot) == null
|| InventoryUtils.canStack(session.getInventory().getCursor(), inventory.getItem(toSlot)))) {
if (fromAction.getToItem().getCount() == 0) {
refresh = true;
plan.add(Click.LEFT, toSlot);
if (craftSlot != -1) {
plan.add(Click.LEFT, craftSlot);
}
} else {
int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount();
for (int i = 0; i < difference; i++) {
plan.add(Click.RIGHT, toSlot);
}
session.setCraftSlot(craftSlot);
}
plan.execute(session, translator, inventory, refresh);
return;
} else {
session.setCraftSlot(-2);
}
}
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) {
plan.add(Click.LEFT, cursorSlot);
} else {
translator.updateInventory(session, inventory);
return;
}
}
if ((fromAction.getFromItem().equals(toAction.getToItem()) && !InventoryUtils.canStack(fromAction.getFromItem(), toAction.getFromItem()))
|| fromAction.getToItem().getId() == 0) { //slot swap
plan.add(Click.LEFT, fromSlot);
plan.add(Click.LEFT, toSlot);
if (fromAction.getToItem().getId() != 0) {
plan.add(Click.LEFT, fromSlot);
}
} else if (InventoryUtils.canStack(fromAction.getFromItem(), toAction.getToItem())) { //partial item move
if (translator.getSlotType(fromSlot) == SlotType.FURNACE_OUTPUT) {
ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(),
inventory.getTransactionId().getAndIncrement(),
fromSlot, InventoryUtils.REFRESH_ITEM, WindowAction.SHIFT_CLICK_ITEM,
ShiftClickItemParam.LEFT_CLICK);
session.getDownstream().getSession().send(shiftClickPacket);
translator.updateInventory(session, inventory);
return;
} else if (translator.getSlotType(fromSlot) == SlotType.OUTPUT) {
session.setCraftSlot(cursorSlot);
plan.add(Click.LEFT, fromSlot);
int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount();
for (int i = 0; i < difference; i++) {
plan.add(Click.RIGHT, toSlot);
}
//client will send additional packets later to finish transferring crafting output
//translator will know how to handle this using the craftSlot variable
} else {
plan.add(Click.LEFT, fromSlot);
int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount();
for (int i = 0; i < difference; i++) {
plan.add(Click.RIGHT, toSlot);
}
plan.add(Click.LEFT, fromSlot);
}
}
if (cursorSlot != -1) {
plan.add(Click.LEFT, cursorSlot);
}
plan.execute(session, translator, inventory, refresh);
return;
}
translator.updateInventory(session, inventory);
InventoryUtils.updateCursor(session);
}
private static 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 (InventoryUtils.canStack(testItem, blacklistItem)) {
acceptable = false;
break;
}
}
}
if (acceptable && !slotBlacklist.contains(i))
return i;
}
//could not find a viable temp slot
return -1;
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2019-2020 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.inventory.holder;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
import lombok.AllArgsConstructor;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.block.BlockTranslator;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
@AllArgsConstructor
public class BlockInventoryHolder extends InventoryHolder {
private final int blockId;
private final ContainerType containerType;
@Override
public void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
Vector3i position = session.getPlayerEntity().getPosition().toInt();
position = position.add(Vector3i.UP);
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(position);
blockPacket.setRuntimeId(blockId);
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.getUpstream().sendPacket(blockPacket);
inventory.setHolderPosition(position);
CompoundTag tag = CompoundTag.builder()
.intTag("x", position.getX())
.intTag("y", position.getY())
.intTag("z", position.getZ())
.stringTag("CustomName", inventory.getTitle()).buildRootTag();
BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
dataPacket.setData(tag);
dataPacket.setBlockPosition(position);
session.getUpstream().sendPacket(dataPacket);
}
@Override
public void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
containerOpenPacket.setWindowId((byte) inventory.getId());
containerOpenPacket.setType((byte) containerType.id());
containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
session.getUpstream().sendPacket(containerOpenPacket);
}
@Override
public void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
Vector3i holderPos = inventory.getHolderPosition();
Position pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
BlockState realBlock = session.getChunkCache().getBlockAt(pos);
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
session.getUpstream().sendPacket(blockPacket);
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2019-2020 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.inventory.holder;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
public abstract class InventoryHolder {
public abstract void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory);
public abstract void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory);
public abstract void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory);
}

View File

@ -23,38 +23,32 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.network.translators.inventory;
package org.geysermc.connector.network.translators.inventory.updater;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import lombok.AllArgsConstructor;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.TranslatorsInit;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
public class GenericInventoryTranslator extends InventoryTranslator {
@AllArgsConstructor
public class ChestInventoryUpdater extends InventoryUpdater {
private final int paddedSize;
@Override
public void prepareInventory(GeyserSession session, Inventory inventory) {
// TODO: Add code here
}
public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
super.updateInventory(translator, session, inventory);
@Override
public void openInventory(GeyserSession session, Inventory inventory) {
ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
containerOpenPacket.setWindowId((byte) inventory.getId());
containerOpenPacket.setType((byte) 0);
containerOpenPacket.setBlockPosition(Vector3i.ZERO);
session.getUpstream().sendPacket(containerOpenPacket);
}
@Override
public void updateInventory(GeyserSession session, Inventory inventory) {
ItemData[] bedrockItems = new ItemData[inventory.getItems().length];
ItemData[] bedrockItems = new ItemData[paddedSize];
for (int i = 0; i < bedrockItems.length; i++) {
bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
if (i <= translator.size) {
bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
} else {
bedrockItems[i] = ItemData.AIR;
}
}
InventoryContentPacket contentPacket = new InventoryContentPacket();
@ -64,11 +58,15 @@ public class GenericInventoryTranslator extends InventoryTranslator {
}
@Override
public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) {
if (super.updateSlot(translator, session, inventory, javaSlot))
return true;
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(inventory.getId());
slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[slot]));
slotPacket.setSlot(slot);
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(javaSlot)));
session.getUpstream().sendPacket(slotPacket);
return true;
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2019-2020 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.inventory.updater;
import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.TranslatorsInit;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
public class ContainerInventoryUpdater extends InventoryUpdater {
@Override
public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
super.updateInventory(translator, session, inventory);
ItemData[] bedrockItems = new ItemData[translator.size];
for (int i = 0; i < bedrockItems.length; i++) {
bedrockItems[translator.javaSlotToBedrock(i)] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
}
InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(inventory.getId());
contentPacket.setContents(bedrockItems);
session.getUpstream().sendPacket(contentPacket);
}
@Override
public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) {
if (super.updateSlot(translator, session, inventory, javaSlot))
return true;
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(inventory.getId());
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(javaSlot)));
session.getUpstream().sendPacket(slotPacket);
return true;
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2019-2020 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.inventory.updater;
import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.TranslatorsInit;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
public class CursorInventoryUpdater extends InventoryUpdater {
@Override
public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
super.updateInventory(translator, session, inventory);
for (int i = 0; i < translator.size; i++) {
final int bedrockSlot = translator.javaSlotToBedrock(i);
if (bedrockSlot == 50)
continue;
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.CURSOR);
slotPacket.setSlot(bedrockSlot);
slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i)));
session.getUpstream().sendPacket(slotPacket);
}
}
@Override
public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) {
if (super.updateSlot(translator, session, inventory, javaSlot))
return true;
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.CURSOR);
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(javaSlot)));
session.getUpstream().sendPacket(slotPacket);
return true;
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2019-2020 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.inventory.updater;
import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.TranslatorsInit;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
public abstract class InventoryUpdater {
public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
ItemData[] bedrockItems = new ItemData[36];
for (int i = 0; i < 36; i++) {
final int offset = i < 9 ? 27 : -9;
bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(translator.size + i + offset));
}
InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(ContainerId.INVENTORY);
contentPacket.setContents(bedrockItems);
session.getUpstream().sendPacket(contentPacket);
}
public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) {
if (javaSlot >= translator.size) {
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.INVENTORY);
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(javaSlot)));
session.getUpstream().sendPacket(slotPacket);
return true;
}
return false;
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (c) 2019-2020 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.item;
import lombok.Getter;
import java.util.Locale;
@Getter
enum Enchantment {
PROTECTION,
FIRE_PROTECTION,
FEATHER_FALLING,
BLAST_PROTECTION,
PROJECTILE_PROTECTION,
THORNS,
RESPIRATION,
DEPTH_STRIDER,
AQUA_AFFINITY,
SHARPNESS,
SMITE,
BANE_OF_ARTHROPODS,
KNOCKBACK,
FIRE_ASPECT,
LOOTING,
EFFICIENCY,
SILK_TOUCH,
UNBREAKING,
FORTUNE,
POWER,
PUNCH,
FLAME,
INFINITY,
LUCK_OF_THE_SEA,
LURE,
FROST_WALKER,
MENDING,
BINDING_CURSE,
VANISHING_CURSE,
IMPALING,
RIPTIDE,
LOYALTY,
CHANNELING,
MULTISHOT,
PIERCING,
QUICK_CHARGE;
private final String javaIdentifier;
Enchantment() {
this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ENGLISH);
}
public static Enchantment getByJavaIdentifier(String javaIdentifier) {
for (Enchantment enchantment : Enchantment.values()) {
if (enchantment.javaIdentifier.equals(javaIdentifier)) {
return enchantment;
}
}
return null;
}
public static Enchantment getByBedrockId(int bedrockId) {
if (bedrockId >= 0 && bedrockId < Enchantment.values().length) {
return Enchantment.values()[bedrockId];
}
return null;
}
}

View File

@ -41,6 +41,7 @@ import com.github.steveice10.opennbt.tag.builtin.ShortTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.protocol.bedrock.data.ItemData;
import org.geysermc.api.Geyser;
import org.geysermc.connector.console.GeyserLogger;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.utils.Toolbox;
@ -57,19 +58,38 @@ public class ItemTranslator {
if (data.getTag() == null) {
return new ItemStack(javaItem.getJavaId(), data.getCount());
} else if (javaItem.getJavaIdentifier().equals("minecraft:enchanted_book")) {
CompoundTag javaTag = translateToJavaNBT(data.getTag());
Map<String, Tag> javaValue = javaTag.getValue();
Tag enchTag = javaValue.get("Enchantments");
if (enchTag instanceof ListTag) {
enchTag = new ListTag("StoredEnchantments", ((ListTag) enchTag).getValue());
javaValue.remove("Enchantments");
javaValue.put("StoredEnchantments", enchTag);
javaTag.setValue(javaValue);
}
return new ItemStack(javaItem.getJavaId(), data.getCount(), javaTag);
}
return new ItemStack(javaItem.getJavaId(), data.getCount(), translateToJavaNBT(data.getTag()));
}
public ItemData translateToBedrock(ItemStack stack) {
// Most likely dirt if null
if (stack == null) {
return ItemData.of(3, (short)0, 0);
return ItemData.AIR;
}
ItemEntry bedrockItem = getItem(stack);
if (stack.getNbt() == null) {
return ItemData.of(bedrockItem.getBedrockId(), (short) bedrockItem.getBedrockData(), stack.getAmount());
} else if (bedrockItem.getJavaIdentifier().endsWith("potion")) {
Tag potionTag = stack.getNbt().get("Potion");
if (potionTag instanceof StringTag) {
Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue());
if (potion != null) {
return ItemData.of(bedrockItem.getBedrockId(), potion.getBedrockId(), stack.getAmount(), translateToBedrockNBT(stack.getNbt()));
}
Geyser.getLogger().debug("Unknown java potion: " + potionTag.getValue());
}
}
return ItemData.of(bedrockItem.getBedrockId(), (short) bedrockItem.getBedrockData(), stack.getAmount(), translateToBedrockNBT(stack.getNbt()));
}
@ -80,7 +100,7 @@ public class ItemTranslator {
public ItemEntry getItem(ItemData data) {
for (ItemEntry itemEntry : Toolbox.ITEM_ENTRIES.values()) {
if (itemEntry.getBedrockId() == data.getId() && itemEntry.getBedrockData() == data.getDamage()) {
if (itemEntry.getBedrockId() == data.getId() && (itemEntry.getBedrockData() == data.getDamage() || itemEntry.getJavaIdentifier().endsWith("potion"))) {
return itemEntry;
}
}
@ -99,10 +119,11 @@ public class ItemTranslator {
if (translatedTag == null)
continue;
javaValue.put(str, translatedTag);
javaValue.put(translatedTag.getName(), translatedTag);
}
}
javaTag.setValue(javaValue);
return javaTag;
}
@ -161,6 +182,29 @@ public class ItemTranslator {
com.nukkitx.nbt.tag.ListTag listTag = (com.nukkitx.nbt.tag.ListTag) tag;
List<Tag> tags = new ArrayList<>();
if (tag.getName().equals("ench")) {
for (Object value : listTag.getValue()) {
if (!(value instanceof com.nukkitx.nbt.tag.CompoundTag))
continue;
com.nukkitx.nbt.tag.CompoundTag tagValue = (com.nukkitx.nbt.tag.CompoundTag) value;
int bedrockId = tagValue.getShort("id", (short) -1);
Enchantment enchantment = Enchantment.getByBedrockId(bedrockId);
if (enchantment != null) {
CompoundTag javaTag = new CompoundTag("");
Map<String, Tag> javaValue = javaTag.getValue();
javaValue.put("id", new StringTag("id", enchantment.getJavaIdentifier()));
javaValue.put("lvl", new IntTag("lvl", tagValue.getShort("lvl", (short) 1)));
javaTag.setValue(javaValue);
tags.add(javaTag);
} else {
Geyser.getLogger().debug("Unknown bedrock enchantment: " + bedrockId);
}
}
return new ListTag("Enchantments", tags);
}
for (Object value : listTag.getValue()) {
if (!(value instanceof com.nukkitx.nbt.tag.Tag))
continue;
@ -189,7 +233,7 @@ public class ItemTranslator {
if (translatedTag == null)
continue;
javaValue.put(str, translatedTag);
javaValue.put(translatedTag.getName(), translatedTag);
}
}
@ -250,7 +294,33 @@ public class ItemTranslator {
if (tag instanceof ListTag) {
ListTag listTag = (ListTag) tag;
if (listTag.getName().equalsIgnoreCase("Lore")) {
if (listTag.getName().equalsIgnoreCase("Enchantments") || listTag.getName().equalsIgnoreCase("StoredEnchantments")) {
List<com.nukkitx.nbt.tag.CompoundTag> tags = new ArrayList<>();
for (Object value : listTag.getValue()) {
if (!(value instanceof CompoundTag))
continue;
Tag javaEnchLvl = ((CompoundTag) value).get("lvl");
if (!(javaEnchLvl instanceof ShortTag))
continue;
Tag javaEnchId = ((CompoundTag) value).get("id");
if (!(javaEnchId instanceof StringTag))
continue;
Enchantment enchantment = Enchantment.getByJavaIdentifier(((StringTag) javaEnchId).getValue());
if (enchantment == null) {
Geyser.getLogger().debug("Unknown java enchantment: " + javaEnchId.getValue());
continue;
}
com.nukkitx.nbt.CompoundTagBuilder builder = com.nukkitx.nbt.tag.CompoundTag.EMPTY.toBuilder();
builder.shortTag("lvl", ((ShortTag) javaEnchLvl).getValue());
builder.shortTag("id", (short) enchantment.ordinal());
tags.add(builder.buildRootTag());
}
return new com.nukkitx.nbt.tag.ListTag<>("ench", com.nukkitx.nbt.tag.CompoundTag.class, tags);
} else if (listTag.getName().equalsIgnoreCase("Lore")) {
List<com.nukkitx.nbt.tag.StringTag> tags = new ArrayList<>();
for (Object value : listTag.getValue()) {
if (!(value instanceof Tag))

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2019-2020 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.item;
import lombok.Getter;
import java.util.Locale;
@Getter
enum Potion {
WATER(0),
MUNDANE(1),
THICK(3),
AWKWARD(4),
NIGHT_VISION(5),
LONG_NIGHT_VISION(6),
INVISIBILITY(7),
LONG_INVISIBILITY(8),
LEAPING(9),
STRONG_LEAPING(11),
LONG_LEAPING(10),
FIRE_RESISTANCE(12),
LONG_FIRE_RESISTANCE(13),
SWIFTNESS(14),
STRONG_SWIFTNESS(16),
LONG_SWIFTNESS(15),
SLOWNESS(17),
STRONG_SLOWNESS(18), //does not exist
LONG_SLOWNESS(18),
WATER_BREATHING(19),
LONG_WATER_BREATHING(20),
HEALING(21),
STRONG_HEALING(22),
HARMING(23),
STRONG_HARMING(24),
POISON(25),
STRONG_POISON(27),
LONG_POISON(26),
REGENERATION(28),
STRONG_REGENERATION(30),
LONG_REGENERATION(29),
STRENGTH(31),
STRONG_STRENGTH(33),
LONG_STRENGTH(32),
WEAKNESS(34),
LONG_WEAKNESS(35),
LUCK(2), //does not exist
TURTLE_MASTER(37),
STRONG_TURTLE_MASTER(39),
LONG_TURTLE_MASTER(38),
SLOW_FALLING(40),
LONG_SLOW_FALLING(41);
private final String javaIdentifier;
private final short bedrockId;
Potion(int bedrockId) {
this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ENGLISH);
this.bedrockId = (short) bedrockId;
}
public static Potion getByJavaIdentifier(String javaIdentifier) {
for (Potion potion : Potion.values()) {
if (potion.javaIdentifier.equals(javaIdentifier)) {
return potion;
}
}
return null;
}
public static Potion getByBedrockId(short bedrockId) {
for (Potion potion : Potion.values()) {
if (potion.bedrockId == bedrockId) {
return potion;
}
}
return null;
}
}

View File

@ -0,0 +1,172 @@
/*
* Copyright (c) 2019-2020 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.data.PotionMixData;
import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
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 org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.utils.Toolbox;
import java.util.*;
import java.util.stream.Collectors;
public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclareRecipesPacket> {
private static final Collection<PotionMixData> POTION_MIXES =
Arrays.stream(new int[]{372, 331, 348, 376, 289, 437, 353, 414, 382, 375, 462, 378, 396, 377, 370, 469, 470})
.mapToObj(ingredient -> new PotionMixData(0, ingredient, 0))
.collect(Collectors.toList());
@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());
output = ItemData.of(output.getId(), output.getDamage(), output.getCount()); //strip NBT
ItemData[][] inputCombinations = combinations(shapelessRecipeData.getIngredients());
for (ItemData[] inputs : inputCombinations) {
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());
output = ItemData.of(output.getId(), output.getDamage(), output.getCount()); //strip NBT
ItemData[][] inputCombinations = combinations(shapedRecipeData.getIngredients());
for (ItemData[] inputs : inputCombinations) {
UUID uuid = UUID.randomUUID();
craftingDataPacket.getCraftingData().add(CraftingData.fromShaped(uuid.toString(),
shapedRecipeData.getWidth(), shapedRecipeData.getHeight(), inputs,
new ItemData[]{output}, uuid, "crafting_table", 0));
}
break;
}
}
}
craftingDataPacket.getPotionMixData().addAll(POTION_MIXES);
session.getUpstream().sendPacket(craftingDataPacket);
}
//TODO: rewrite
private ItemData[][] combinations(Ingredient[] ingredients) {
Map<Set<ItemData>, IntSet> squashedOptions = new HashMap<>();
for (int i = 0; i < ingredients.length; i++) {
if (ingredients[i].getOptions().length == 0) {
squashedOptions.computeIfAbsent(Collections.singleton(ItemData.AIR), k -> new IntOpenHashSet()).add(i);
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())));
Set<ItemData> optionSet = new HashSet<>(groupedByIds.size());
for (Map.Entry<GroupedItem, List<ItemData>> entry : groupedByIds.entrySet()) {
if (entry.getValue().size() > 1) {
GroupedItem groupedItem = entry.getKey();
int idCount = 0;
//not optimal
for (ItemEntry itemEntry : Toolbox.ITEM_ENTRIES.values()) {
if (itemEntry.getBedrockId() == groupedItem.id) {
idCount++;
}
}
if (entry.getValue().size() < idCount) {
optionSet.addAll(entry.getValue());
} else {
optionSet.add(ItemData.of(groupedItem.id, (short) -1, groupedItem.count, groupedItem.tag));
}
} else {
ItemData item = entry.getValue().get(0);
optionSet.add(item);
}
}
squashedOptions.computeIfAbsent(optionSet, k -> new IntOpenHashSet()).add(i);
}
int totalCombinations = 1;
for (Set optionSet : squashedOptions.keySet()) {
totalCombinations *= optionSet.size();
}
if (totalCombinations > 500) {
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 new ItemData[][]{translatedItems};
}
List<Set<ItemData>> sortedSets = new ArrayList<>(squashedOptions.keySet());
sortedSets.sort(Comparator.comparing(Set::size, Comparator.reverseOrder()));
ItemData[][] combinations = new ItemData[totalCombinations][ingredients.length];
int x = 1;
for (Set<ItemData> set : sortedSets) {
IntSet slotSet = squashedOptions.get(set);
int i = 0;
for (ItemData item : set) {
for (int j = 0; j < totalCombinations / set.size(); j++) {
final int comboIndex = (i * x) + (j % x) + ((j / x) * set.size() * x);
for (int slot : slotSet) {
combinations[comboIndex][slot] = item;
}
}
i++;
}
x *= set.size();
}
return combinations;
}
@EqualsAndHashCode
@AllArgsConstructor
private static class GroupedItem {
int id;
int count;
CompoundTag tag;
}
}

View File

@ -45,6 +45,8 @@ public class JavaRespawnTranslator extends PacketTranslator<ServerRespawnPacket>
// Max health must be divisible by two in bedrock
entity.getAttributes().put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(maxHealth, (maxHealth % 2 == 1 ? maxHealth + 1 : maxHealth)));
session.getInventoryCache().setOpenInventory(null);
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
playerGameTypePacket.setGamemode(packet.getGamemode().ordinal());
session.getUpstream().sendPacket(playerGameTypePacket);

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019-2020 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.entity.player;
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerChangeHeldItemPacket;
import com.nukkitx.protocol.bedrock.packet.PlayerHotbarPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
public class JavaPlayerChangeHeldItemTranslator extends PacketTranslator<ServerPlayerChangeHeldItemPacket> {
@Override
public void translate(ServerPlayerChangeHeldItemPacket packet, GeyserSession session) {
PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket();
hotbarPacket.setContainerId(0);
hotbarPacket.setSelectedHotbarSlot(packet.getSlot());
hotbarPacket.setSelectHotbarSlot(true);
session.getUpstream().sendPacket(hotbarPacket);
session.getInventory().setHeldItemSlot(packet.getSlot());
}
}

View File

@ -1,22 +0,0 @@
package org.geysermc.connector.network.translators.java.inventory;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
import org.geysermc.connector.inventory.Inventory;
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;
public class OpenWindowPacketTranslator extends PacketTranslator<ServerOpenWindowPacket> {
@Override
public void translate(ServerOpenWindowPacket packet, GeyserSession session) {
System.out.println("debug: " + packet.getType());
InventoryTranslator translator = TranslatorsInit.getInventoryTranslator();
translator.openInventory(session, new Inventory(packet.getName(), packet.getWindowId(), packet.getType(), 54));
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2019-2020 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.window;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerCloseWindowPacket;
import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.utils.InventoryUtils;
public class JavaCloseWindowTranslator extends PacketTranslator<ServerCloseWindowPacket> {
@Override
public void translate(ServerCloseWindowPacket packet, GeyserSession session) {
ContainerClosePacket closePacket = new ContainerClosePacket();
closePacket.setWindowId((byte)packet.getWindowId());
session.getUpstream().sendPacket(closePacket);
InventoryUtils.closeInventory(session, packet.getWindowId());
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019-2020 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.window;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientConfirmTransactionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerConfirmTransactionPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.utils.InventoryUtils;
public class JavaConfirmTransactionTranslator extends PacketTranslator<ServerConfirmTransactionPacket> {
@Override
public void translate(ServerConfirmTransactionPacket packet, GeyserSession session) {
if (!packet.isAccepted()) {
ClientConfirmTransactionPacket confirmPacket = new ClientConfirmTransactionPacket(packet.getWindowId(), packet.getActionId(), true);
session.getDownstream().getSession().send(confirmPacket);
}
}
}

View File

@ -25,15 +25,66 @@
package org.geysermc.connector.network.translators.java.window;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
import org.geysermc.api.Geyser;
import org.geysermc.connector.inventory.Inventory;
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.concurrent.TimeUnit;
public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowPacket> {
@Override
public void translate(ServerOpenWindowPacket packet, GeyserSession session) {
InventoryUtils.openInventory(session, packet);
if (packet.getWindowId() == 0) {
return;
}
InventoryTranslator newTranslator = TranslatorsInit.getInventoryTranslators().get(packet.getType());
Inventory openInventory = session.getInventoryCache().getOpenInventory();
if (newTranslator == null) {
if (openInventory != null) {
ContainerClosePacket closePacket = new ContainerClosePacket();
closePacket.setWindowId((byte)openInventory.getId());
session.getUpstream().sendPacket(closePacket);
TranslatorsInit.getInventoryTranslators().get(openInventory.getWindowType()).closeInventory(session, openInventory);
}
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId());
session.getDownstream().getSession().send(closeWindowPacket);
return;
}
String name = packet.getName();
try {
JsonParser parser = new JsonParser();
JsonObject jsonObject = parser.parse(packet.getName()).getAsJsonObject();
if (jsonObject.has("text")) {
name = jsonObject.get("text").getAsString();
} else if (jsonObject.has("translate")) {
name = jsonObject.get("translate").getAsString();
}
} catch (Exception e) {
Geyser.getLogger().debug("JavaOpenWindowTranslator: " + e.toString());
}
Inventory newInventory = new Inventory(name, packet.getWindowId(), packet.getType(), newTranslator.size + 36);
session.getInventoryCache().cacheInventory(newInventory);
if (openInventory != null) {
InventoryTranslator openTranslator = TranslatorsInit.getInventoryTranslators().get(openInventory.getWindowType());
if (!openTranslator.getClass().equals(newTranslator.getClass())) {
InventoryUtils.closeInventory(session, openInventory.getId());
Geyser.getGeneralThreadPool().schedule(() -> InventoryUtils.openInventory(session, newInventory), 500, TimeUnit.MILLISECONDS);
return;
}
}
InventoryUtils.openInventory(session, newInventory);
}
}

View File

@ -25,44 +25,39 @@
package org.geysermc.connector.network.translators.java.window;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.session.cache.InventoryCache;
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.Objects;
public class JavaSetSlotTranslator extends PacketTranslator<ServerSetSlotPacket> {
@Override
public void translate(ServerSetSlotPacket packet, GeyserSession session) {
InventoryCache inventoryCache = session.getInventoryCache();
if (!inventoryCache.getInventories().containsKey(packet.getWindowId())) {
inventoryCache.cachePacket(packet.getWindowId(), packet);
if (packet.getWindowId() == 255 && packet.getSlot() == -1) { //cursor
if (Objects.equals(session.getInventory().getCursor(), packet.getItem()))
return;
if (session.getCraftSlot() != 0)
return;
session.getInventory().setCursor(packet.getItem());
InventoryUtils.updateCursor(session);
return;
}
Inventory inventory = inventoryCache.getInventories().get(packet.getWindowId());
if (packet.getWindowId() != 0 && inventory.getWindowType() == null)
Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId());
if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null))
return;
// Player inventory
if (packet.getWindowId() == 0) {
if (packet.getSlot() >= inventory.getItems().length)
return; // Most likely not a player inventory
ItemStack[] items = inventory.getItems();
items[packet.getSlot()] = packet.getItem();
inventory.setItems(items);
InventoryUtils.refreshPlayerInventory(session, inventory);
if (inventory.isOpen()) {
InventoryUtils.updateSlot(session, packet);
} else {
inventoryCache.cachePacket(packet.getWindowId(), packet);
}
InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
if (translator != null) {
inventory.setItem(packet.getSlot(), packet.getItem());
translator.updateSlot(session, inventory, packet.getSlot());
}
}
}

View File

@ -28,28 +28,29 @@ package org.geysermc.connector.network.translators.java.window;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.session.cache.InventoryCache;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.utils.InventoryUtils;
import org.geysermc.connector.network.translators.TranslatorsInit;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import java.util.Arrays;
public class JavaWindowItemsTranslator extends PacketTranslator<ServerWindowItemsPacket> {
@Override
public void translate(ServerWindowItemsPacket packet, GeyserSession session) {
InventoryCache inventoryCache = session.getInventoryCache();
if (!inventoryCache.getInventories().containsKey(packet.getWindowId())) {
inventoryCache.cachePacket(packet.getWindowId(), packet);
Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId());
if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null))
return;
}
Inventory inventory = inventoryCache.getInventories().get(packet.getWindowId());
// Player inventory
if (packet.getWindowId() == 0) {
if (packet.getItems().length < inventory.getSize()) {
inventory.setItems(Arrays.copyOf(packet.getItems(), inventory.getSize()));
} else {
inventory.setItems(packet.getItems());
InventoryUtils.refreshPlayerInventory(session, inventory);
return;
}
InventoryUtils.updateInventory(session, packet);
InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
if (translator != null) {
translator.updateInventory(session, inventory);
}
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019-2020 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.window;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowPropertyPacket;
import org.geysermc.connector.inventory.Inventory;
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;
public class JavaWindowPropertyTranslator extends PacketTranslator<ServerWindowPropertyPacket> {
@Override
public void translate(ServerWindowPropertyPacket packet, GeyserSession session) {
Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId());
if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null))
return;
InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
if (translator != null) {
translator.updateProperty(session, inventory, packet.getRawProperty(), packet.getValue());
}
}
}

View File

@ -26,113 +26,76 @@
package org.geysermc.connector.utils;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket;
import com.github.steveice10.packetlib.packet.Packet;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.api.Geyser;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.TranslatorsInit;
import org.geysermc.connector.network.translators.inventory.DoubleChestInventoryTranslator;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
public class InventoryUtils {
public static final ItemStack REFRESH_ITEM = new ItemStack(1, 127, new CompoundTag("")); //TODO: stop using this
public static void refreshPlayerInventory(GeyserSession session, Inventory inventory) {
InventoryContentPacket inventoryContentPacket = new InventoryContentPacket();
inventoryContentPacket.setContainerId(ContainerId.INVENTORY);
ItemData[] contents = new ItemData[40];
// Inventory
for (int i = 9; i < 36; i++) {
contents[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
public static void openInventory(GeyserSession session, Inventory inventory) {
InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
if (translator != null) {
session.getInventoryCache().setOpenInventory(inventory);
translator.prepareInventory(session, inventory);
//TODO: find better way to handle double chest delay
if (translator instanceof DoubleChestInventoryTranslator) {
Geyser.getGeneralThreadPool().schedule(() -> {
translator.openInventory(session, inventory);
translator.updateInventory(session, inventory);
}, 200, TimeUnit.MILLISECONDS);
} else {
translator.openInventory(session, inventory);
translator.updateInventory(session, inventory);
}
}
// Hotbar
for (int i = 36; i < 45; i++) {
contents[i - 36] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
}
// Armor
for (int i = 5; i < 9; i++) {
contents[i + 31] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
}
inventoryContentPacket.setContents(contents);
session.getUpstream().sendPacket(inventoryContentPacket);
}
public static void openInventory(GeyserSession session, ServerOpenWindowPacket packet) {
Inventory inventory = new Inventory(packet.getWindowId(), packet.getType(), 45); // TODO: Find a way to set this value
session.getInventoryCache().getInventories().put(packet.getWindowId(), inventory);
session.getInventoryCache().setOpenInventory(inventory);
InventoryTranslator translator = TranslatorsInit.getInventoryTranslator();
translator.prepareInventory(session, inventory);
Geyser.getGeneralThreadPool().schedule(() -> {
List<Packet> packets = session.getInventoryCache().getCachedPackets().get(inventory.getId());
packets.forEach(itemPacket -> {
if (itemPacket != null) {
if (ServerWindowItemsPacket.class.isAssignableFrom(itemPacket.getClass())) {
updateInventory(session, (ServerWindowItemsPacket) itemPacket);
}
}
});
}, 200, TimeUnit.MILLISECONDS);
public static void closeInventory(GeyserSession session, int windowId) {
if (windowId != 0) {
Inventory inventory = session.getInventoryCache().getInventories().get(windowId);
if (inventory != null) {
InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
translator.closeInventory(session, inventory);
session.getInventoryCache().uncacheInventory(windowId);
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);
}
public static void updateInventory(GeyserSession session, ServerWindowItemsPacket packet) {
if (packet.getWindowId() == 0)
return;
if (session.getInventoryCache().getOpenInventory() == null || !session.getInventoryCache().getInventories().containsKey(packet.getWindowId()))
return;
Inventory openInventory = session.getInventoryCache().getOpenInventory();
if (packet.getWindowId() != openInventory.getId())
return;
InventoryTranslator translator = TranslatorsInit.getInventoryTranslator();
if (translator == null) {
session.getDownstream().getSession().send(new ClientCloseWindowPacket(packet.getWindowId()));
return;
}
openInventory.setItems(packet.getItems());
translator.updateInventory(session, openInventory);
public static void updateCursor(GeyserSession session) {
InventorySlotPacket cursorPacket = new InventorySlotPacket();
cursorPacket.setContainerId(ContainerId.CURSOR);
cursorPacket.setSlot(0);
cursorPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(session.getInventory().getCursor()));
session.getUpstream().sendPacket(cursorPacket);
}
public static void updateSlot(GeyserSession session, ServerSetSlotPacket packet) {
if (packet.getWindowId() == 0)
return;
public static boolean canStack(ItemStack item1, ItemStack item2) {
if (item1 == null || item2 == null)
return false;
return item1.getId() == item2.getId() && Objects.equals(item1.getNbt(), item2.getNbt());
}
if (session.getInventoryCache().getOpenInventory() == null || !session.getInventoryCache().getInventories().containsKey(packet.getWindowId()))
return;
Inventory openInventory = session.getInventoryCache().getOpenInventory();
if (packet.getWindowId() != openInventory.getId())
return;
InventoryTranslator translator = TranslatorsInit.getInventoryTranslator();
if (translator == null) {
session.getDownstream().getSession().send(new ClientCloseWindowPacket(packet.getWindowId()));
return;
}
if (packet.getSlot() >= openInventory.getSize()) {
session.getDownstream().getSession().send(new ClientCloseWindowPacket(packet.getWindowId()));
return;
}
ItemStack[] items = openInventory.getItems();
items[packet.getSlot()] = packet.getItem();
translator.updateSlot(session, openInventory, packet.getSlot());
public static boolean canStack(ItemData item1, ItemData item2) {
if (item1 == null || item2 == null)
return false;
return item1.equals(item2, false, true, true);
}
}

View File

@ -32,6 +32,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.nbt.stream.NBTInputStream;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@ -39,6 +40,8 @@ import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.console.GeyserLogger;
import org.geysermc.connector.network.translators.item.ItemEntry;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
@ -46,6 +49,7 @@ public class Toolbox {
public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
public static final CompoundTag BIOMES;
public static final ItemData[] CREATIVE_ITEMS;
public static final Collection<StartGamePacket.ItemEntry> ITEMS = new ArrayList<>();
@ -102,6 +106,36 @@ public class Toolbox {
entry.getValue().get("bedrock_id").intValue(), entry.getValue().get("bedrock_data").intValue()));
itemIndex++;
}
stream = getResource("bedrock/creative_items.json");
JsonNode creativeItemEntries;
try {
creativeItemEntries = JSON_MAPPER.readTree(stream).get("items");
} catch (Exception e) {
throw new AssertionError("Unable to load creative items", e);
}
List<ItemData> creativeItems = new ArrayList<>();
for (JsonNode itemNode : creativeItemEntries) {
short damage = 0;
if (itemNode.has("damage")) {
damage = itemNode.get("damage").numberValue().shortValue();
}
if (itemNode.has("nbt_b64")) {
byte[] bytes = Base64.getDecoder().decode(itemNode.get("nbt_b64").asText());
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
try {
com.nukkitx.nbt.tag.CompoundTag tag = (com.nukkitx.nbt.tag.CompoundTag) NbtUtils.createReaderLE(bais).readTag();
creativeItems.add(ItemData.of(itemNode.get("id").asInt(), damage, 1, tag));
} catch (IOException e) {
e.printStackTrace();
}
} else {
creativeItems.add(ItemData.of(itemNode.get("id").asInt(), damage, 1));
}
}
CREATIVE_ITEMS = creativeItems.toArray(new ItemData[0]);
}
public static InputStream getResource(String resource) {

View File

@ -31,6 +31,8 @@ import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import java.util.Objects;
@Getter
@Setter
@AllArgsConstructor

View File

@ -3008,21 +3008,6 @@
{
"id" : -198
},
{
"id" : 238,
"damage" : 8
},
{
"id" : 238
},
{
"id" : 238,
"damage" : 12
},
{
"id" : 238,
"damage" : 4
},
{
"id" : 379
},
@ -3419,363 +3404,6 @@
{
"id" : 386
},
{
"id" : 36
},
{
"id" : -12
},
{
"id" : -13
},
{
"id" : -14
},
{
"id" : -15
},
{
"id" : -16
},
{
"id" : -17
},
{
"id" : -18
},
{
"id" : -19
},
{
"id" : -20
},
{
"id" : -21
},
{
"id" : -22
},
{
"id" : -23
},
{
"id" : -24
},
{
"id" : -25
},
{
"id" : -26
},
{
"id" : -27
},
{
"id" : -28
},
{
"id" : -29
},
{
"id" : -30
},
{
"id" : -31
},
{
"id" : -32
},
{
"id" : -33
},
{
"id" : -34
},
{
"id" : -35
},
{
"id" : -36
},
{
"id" : -37
},
{
"id" : -38
},
{
"id" : -39
},
{
"id" : -40
},
{
"id" : -41
},
{
"id" : -42
},
{
"id" : -43
},
{
"id" : -44
},
{
"id" : -45
},
{
"id" : -46
},
{
"id" : -47
},
{
"id" : -48
},
{
"id" : -49
},
{
"id" : -50
},
{
"id" : -51
},
{
"id" : -52
},
{
"id" : -53
},
{
"id" : -54
},
{
"id" : -55
},
{
"id" : -56
},
{
"id" : -57
},
{
"id" : -58
},
{
"id" : -59
},
{
"id" : -60
},
{
"id" : -61
},
{
"id" : -62
},
{
"id" : -63
},
{
"id" : -64
},
{
"id" : -65
},
{
"id" : -66
},
{
"id" : -67
},
{
"id" : -68
},
{
"id" : -69
},
{
"id" : -70
},
{
"id" : -71
},
{
"id" : -72
},
{
"id" : -73
},
{
"id" : -74
},
{
"id" : -75
},
{
"id" : -76
},
{
"id" : -77
},
{
"id" : -78
},
{
"id" : -79
},
{
"id" : -80
},
{
"id" : -81
},
{
"id" : -82
},
{
"id" : -83
},
{
"id" : -84
},
{
"id" : -85
},
{
"id" : -86
},
{
"id" : -87
},
{
"id" : -88
},
{
"id" : -89
},
{
"id" : -90
},
{
"id" : -91
},
{
"id" : -92
},
{
"id" : -93
},
{
"id" : -94
},
{
"id" : -95
},
{
"id" : -96
},
{
"id" : -97
},
{
"id" : -98
},
{
"id" : -99
},
{
"id" : -100
},
{
"id" : -101
},
{
"id" : -102
},
{
"id" : -103
},
{
"id" : -104
},
{
"id" : -105
},
{
"id" : -106
},
{
"id" : -107
},
{
"id" : -108
},
{
"id" : -109
},
{
"id" : -110
},
{
"id" : -111
},
{
"id" : -112
},
{
"id" : -113
},
{
"id" : -114
},
{
"id" : -115
},
{
"id" : -116
},
{
"id" : -117
},
{
"id" : -118
},
{
"id" : -119
},
{
"id" : -120
},
{
"id" : -121
},
{
"id" : -122
},
{
"id" : -123
},
{
"id" : -124
},
{
"id" : -125
},
{
"id" : -126
},
{
"id" : -127
},
{
"id" : -128
},
{
"id" : -129
},
{
"id" : 403,
"nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAAAAAA="