forked from GeyserMC/Geyser
		
	Compare commits
	
		
			51 commits
		
	
	
		
			master
			...
			server-inv
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							 | 
						c8016647f2 | ||
| 
							 | 
						a88678a5c1 | ||
| 
							 | 
						1d47dc3f18 | ||
| 
							 | 
						ff69752d2c | ||
| 
							 | 
						57e176efd6 | ||
| 
							 | 
						a160e3694b | ||
| 
							 | 
						50f295b4cd | ||
| 
							 | 
						8928d554a1 | ||
| 
							 | 
						528a9a4431 | ||
| 
							 | 
						7a82852134 | ||
| 
							 | 
						2a5c134ea7 | ||
| 
							 | 
						8317961340 | ||
| 
							 | 
						b7b3278d8b | ||
| 
							 | 
						c4fc604e0c | ||
| 
							 | 
						3c1a40c56a | ||
| 
							 | 
						f4b1d470c3 | ||
| 
							 | 
						ac4f6eceac | ||
| 
							 | 
						a5c020e7ee | ||
| 
							 | 
						3ba396e625 | ||
| 
							 | 
						2265de3ae9 | ||
| 
							 | 
						790c695b27 | ||
| 
							 | 
						956d264c3e | ||
| 
							 | 
						60da3b9432 | ||
| 
							 | 
						4b461e5e0a | ||
| 
							 | 
						7c4e95625a | ||
| 
							 | 
						6df89ed679 | ||
| 
							 | 
						078af59249 | ||
| 
							 | 
						06f346b30b | ||
| 
							 | 
						c1f5380ed1 | ||
| 
							 | 
						c7fade295e | ||
| 
							 | 
						3d0b0a1076 | ||
| 
							 | 
						617a1216d5 | ||
| 
							 | 
						6ae81cce52 | ||
| 
							 | 
						94febd6a2f | ||
| 
							 | 
						4d80edf6d9 | ||
| 
							 | 
						0f735a8330 | ||
| 
							 | 
						e4ecd1a092 | ||
| 
							 | 
						ff4f712eda | ||
| 
							 | 
						f4f804e1ca | ||
| 
							 | 
						009905184e | ||
| 
							 | 
						c6b4d163a1 | ||
| 
							 | 
						f47cf32d90 | ||
| 
							 | 
						1705f1034c | ||
| 
							 | 
						aa4a1058e3 | ||
| 
							 | 
						33a86485dc | ||
| 
							 | 
						929b0ba80c | ||
| 
							 | 
						988e697a70 | ||
| 
							 | 
						3b3e72d5c3 | ||
| 
							 | 
						f167ed2583 | ||
| 
							 | 
						049242db4b | ||
| 
							 | 
						7f4b588cdf | 
					 123 changed files with 4900 additions and 1607 deletions
				
			
		| 
						 | 
				
			
			@ -157,14 +157,14 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
 | 
			
		|||
                if (isViaVersion && isViaVersionNeeded()) {
 | 
			
		||||
                    if (isLegacy) {
 | 
			
		||||
                        // Pre-1.13
 | 
			
		||||
                        this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager();
 | 
			
		||||
                        this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager(this);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // Post-1.13
 | 
			
		||||
                        this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this, use3dBiomes);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    // No ViaVersion
 | 
			
		||||
                    this.geyserWorldManager = new GeyserSpigotNativeWorldManager(use3dBiomes);
 | 
			
		||||
                    this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this, use3dBiomes);
 | 
			
		||||
                }
 | 
			
		||||
                geyserLogger.debug("Using NMS adapter: " + this.geyserWorldManager.getClass() + ", " + nmsVersion);
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
| 
						 | 
				
			
			@ -180,13 +180,13 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
 | 
			
		|||
            // No NMS adapter
 | 
			
		||||
            if (isLegacy && isViaVersion) {
 | 
			
		||||
                // Use ViaVersion for converting pre-1.13 block states
 | 
			
		||||
                this.geyserWorldManager = new GeyserSpigot1_12WorldManager();
 | 
			
		||||
                this.geyserWorldManager = new GeyserSpigot1_12WorldManager(this);
 | 
			
		||||
            } else if (isLegacy) {
 | 
			
		||||
                // Not sure how this happens - without ViaVersion, we don't know any block states, so just assume everything is air
 | 
			
		||||
                this.geyserWorldManager = new GeyserSpigotFallbackWorldManager();
 | 
			
		||||
                this.geyserWorldManager = new GeyserSpigotFallbackWorldManager(this);
 | 
			
		||||
            } else {
 | 
			
		||||
                // Post-1.13
 | 
			
		||||
                this.geyserWorldManager = new GeyserSpigotWorldManager(use3dBiomes);
 | 
			
		||||
                this.geyserWorldManager = new GeyserSpigotWorldManager(this, use3dBiomes);
 | 
			
		||||
            }
 | 
			
		||||
            geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass());
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ package org.geysermc.platform.spigot.world.manager;
 | 
			
		|||
 | 
			
		||||
import org.bukkit.Bukkit;
 | 
			
		||||
import org.bukkit.entity.Player;
 | 
			
		||||
import org.bukkit.plugin.Plugin;
 | 
			
		||||
import org.geysermc.adapters.spigot.SpigotAdapters;
 | 
			
		||||
import org.geysermc.adapters.spigot.SpigotWorldAdapter;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +41,8 @@ import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
 | 
			
		|||
public class GeyserSpigot1_12NativeWorldManager extends GeyserSpigot1_12WorldManager {
 | 
			
		||||
    private final SpigotWorldAdapter adapter;
 | 
			
		||||
 | 
			
		||||
    public GeyserSpigot1_12NativeWorldManager() {
 | 
			
		||||
    public GeyserSpigot1_12NativeWorldManager(Plugin plugin) {
 | 
			
		||||
        super(plugin);
 | 
			
		||||
        this.adapter = SpigotAdapters.getWorldAdapter();
 | 
			
		||||
        // Unlike post-1.13, we can't build up a cache of block states, because block entities need some special conversion
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,6 +30,7 @@ import org.bukkit.Bukkit;
 | 
			
		|||
import org.bukkit.World;
 | 
			
		||||
import org.bukkit.block.Block;
 | 
			
		||||
import org.bukkit.entity.Player;
 | 
			
		||||
import org.bukkit.plugin.Plugin;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
 | 
			
		||||
import us.myles.ViaVersion.api.Pair;
 | 
			
		||||
| 
						 | 
				
			
			@ -61,8 +62,8 @@ public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager {
 | 
			
		|||
     */
 | 
			
		||||
    private final List<Pair<Integer, Protocol>> protocolList;
 | 
			
		||||
 | 
			
		||||
    public GeyserSpigot1_12WorldManager() {
 | 
			
		||||
        super(false);
 | 
			
		||||
    public GeyserSpigot1_12WorldManager(Plugin plugin) {
 | 
			
		||||
        super(plugin, false);
 | 
			
		||||
        this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData();
 | 
			
		||||
        this.protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION,
 | 
			
		||||
                ProtocolVersion.v1_13.getVersion());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,7 @@
 | 
			
		|||
package org.geysermc.platform.spigot.world.manager;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
 | 
			
		||||
import org.bukkit.plugin.Plugin;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -35,9 +36,9 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator;
 | 
			
		|||
 * If this occurs to you somehow, please let us know!!
 | 
			
		||||
 */
 | 
			
		||||
public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager {
 | 
			
		||||
    public GeyserSpigotFallbackWorldManager() {
 | 
			
		||||
    public GeyserSpigotFallbackWorldManager(Plugin plugin) {
 | 
			
		||||
        // Since this is pre-1.13 (and thus pre-1.15), there will never be 3D biomes.
 | 
			
		||||
        super(false);
 | 
			
		||||
        super(plugin, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,7 +47,7 @@ public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorl
 | 
			
		|||
    private final Int2IntMap oldToNewBlockId;
 | 
			
		||||
 | 
			
		||||
    public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin, boolean use3dBiomes) {
 | 
			
		||||
        super(use3dBiomes);
 | 
			
		||||
        super(plugin, use3dBiomes);
 | 
			
		||||
        IntList allBlockStates = adapter.getAllBlockStates();
 | 
			
		||||
        oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size());
 | 
			
		||||
        ProtocolVersion serverVersion = plugin.getServerProtocolVersion();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ package org.geysermc.platform.spigot.world.manager;
 | 
			
		|||
 | 
			
		||||
import org.bukkit.Bukkit;
 | 
			
		||||
import org.bukkit.entity.Player;
 | 
			
		||||
import org.bukkit.plugin.Plugin;
 | 
			
		||||
import org.geysermc.adapters.spigot.SpigotAdapters;
 | 
			
		||||
import org.geysermc.adapters.spigot.SpigotWorldAdapter;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
| 
						 | 
				
			
			@ -35,8 +36,8 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator;
 | 
			
		|||
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
 | 
			
		||||
    protected final SpigotWorldAdapter adapter;
 | 
			
		||||
 | 
			
		||||
    public GeyserSpigotNativeWorldManager(boolean use3dBiomes) {
 | 
			
		||||
        super(use3dBiomes);
 | 
			
		||||
    public GeyserSpigotNativeWorldManager(Plugin plugin, boolean use3dBiomes) {
 | 
			
		||||
        super(plugin, use3dBiomes);
 | 
			
		||||
        adapter = SpigotAdapters.getWorldAdapter();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,23 +28,35 @@ package org.geysermc.platform.spigot.world.manager;
 | 
			
		|||
import com.fasterxml.jackson.databind.JsonNode;
 | 
			
		||||
import com.github.steveice10.mc.protocol.MinecraftConstants;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
 | 
			
		||||
import com.nukkitx.math.vector.Vector3i;
 | 
			
		||||
import com.nukkitx.nbt.NbtMap;
 | 
			
		||||
import com.nukkitx.nbt.NbtMapBuilder;
 | 
			
		||||
import com.nukkitx.nbt.NbtType;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
 | 
			
		||||
import org.bukkit.Bukkit;
 | 
			
		||||
import org.bukkit.World;
 | 
			
		||||
import org.bukkit.block.Biome;
 | 
			
		||||
import org.bukkit.block.Block;
 | 
			
		||||
import org.bukkit.block.Lectern;
 | 
			
		||||
import org.bukkit.block.data.BlockData;
 | 
			
		||||
import org.bukkit.entity.Player;
 | 
			
		||||
import org.bukkit.inventory.ItemStack;
 | 
			
		||||
import org.bukkit.inventory.meta.BookMeta;
 | 
			
		||||
import org.bukkit.plugin.Plugin;
 | 
			
		||||
import org.geysermc.connector.GeyserConnector;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.translators.LecternInventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.world.GeyserWorldManager;
 | 
			
		||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
 | 
			
		||||
import org.geysermc.connector.utils.BlockEntityUtils;
 | 
			
		||||
import org.geysermc.connector.utils.FileUtils;
 | 
			
		||||
import org.geysermc.connector.utils.GameRule;
 | 
			
		||||
import org.geysermc.connector.utils.LanguageUtils;
 | 
			
		||||
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The base world manager to use when there is no supported NMS revision
 | 
			
		||||
| 
						 | 
				
			
			@ -72,8 +84,11 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
 | 
			
		|||
     */
 | 
			
		||||
    private final Int2IntMap biomeToIdMap = new Int2IntOpenHashMap(Biome.values().length);
 | 
			
		||||
 | 
			
		||||
    public GeyserSpigotWorldManager(boolean use3dBiomes) {
 | 
			
		||||
    private final Plugin plugin;
 | 
			
		||||
 | 
			
		||||
    public GeyserSpigotWorldManager(Plugin plugin, boolean use3dBiomes) {
 | 
			
		||||
        this.use3dBiomes = use3dBiomes;
 | 
			
		||||
        this.plugin = plugin;
 | 
			
		||||
 | 
			
		||||
        // Load the values into the biome-to-ID map
 | 
			
		||||
        InputStream biomeStream = FileUtils.getResource("biomes.json");
 | 
			
		||||
| 
						 | 
				
			
			@ -132,9 +147,6 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
 | 
			
		|||
    @Override
 | 
			
		||||
    @SuppressWarnings("deprecation")
 | 
			
		||||
    public int[] getBiomeDataAt(GeyserSession session, int x, int z) {
 | 
			
		||||
        if (session.getPlayerEntity() == null) {
 | 
			
		||||
            return new int[1024];
 | 
			
		||||
        }
 | 
			
		||||
        int[] biomeData = new int[1024];
 | 
			
		||||
        World world = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld();
 | 
			
		||||
        int chunkX = x << 4;
 | 
			
		||||
| 
						 | 
				
			
			@ -167,6 +179,56 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
 | 
			
		|||
        return biomeData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) {
 | 
			
		||||
        // Run as a task to prevent async issues
 | 
			
		||||
        Bukkit.getScheduler().runTask(this.plugin, () -> {
 | 
			
		||||
            Player bukkitPlayer;
 | 
			
		||||
            if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
 | 
			
		||||
            if (!(block.getState() instanceof Lectern)) {
 | 
			
		||||
                session.getConnector().getLogger().error("Lectern expected at: " + Vector3i.from(x, y, z).toString() + " but was not! " + block.toString());
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            Lectern lectern = (Lectern) block.getState();
 | 
			
		||||
            ItemStack itemStack = lectern.getInventory().getItem(0);
 | 
			
		||||
            if (itemStack == null || !(itemStack.getItemMeta() instanceof BookMeta)) {
 | 
			
		||||
                if (!isChunkLoad) {
 | 
			
		||||
                    // We need to update the lectern since it's not going to be updated otherwise
 | 
			
		||||
                    BlockEntityUtils.updateBlockEntity(session, LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z));
 | 
			
		||||
                }
 | 
			
		||||
                // We don't care; return
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            BookMeta bookMeta = (BookMeta) itemStack.getItemMeta();
 | 
			
		||||
            NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, bookMeta.getPageCount());
 | 
			
		||||
            lecternTag.putInt("page", lectern.getPage() / 2);
 | 
			
		||||
            NbtMapBuilder bookTag = NbtMap.builder()
 | 
			
		||||
                    .putByte("Count", (byte) itemStack.getAmount())
 | 
			
		||||
                    .putShort("Damage", (short) 0)
 | 
			
		||||
                    .putString("Name", "minecraft:writable_book");
 | 
			
		||||
            List<NbtMap> pages = new ArrayList<>();
 | 
			
		||||
            for (String page : bookMeta.getPages()) {
 | 
			
		||||
                NbtMapBuilder pageBuilder = NbtMap.builder()
 | 
			
		||||
                        .putString("photoname", "")
 | 
			
		||||
                        .putString("text", page);
 | 
			
		||||
                pages.add(pageBuilder.build());
 | 
			
		||||
            }
 | 
			
		||||
            bookTag.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, pages).build());
 | 
			
		||||
            lecternTag.putCompound("book", bookTag.build());
 | 
			
		||||
            NbtMap blockEntityTag = lecternTag.build();
 | 
			
		||||
            BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z));
 | 
			
		||||
        });
 | 
			
		||||
        return LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(); // Will be updated later
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean shouldExpectLecternHandled() {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
 | 
			
		||||
        return Boolean.parseBoolean(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID()));
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,9 +30,9 @@
 | 
			
		|||
            <scope>compile</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.github.CloudburstMC.Protocol</groupId>
 | 
			
		||||
            <groupId>com.nukkitx.protocol</groupId>
 | 
			
		||||
            <artifactId>bedrock-v422</artifactId>
 | 
			
		||||
            <version>d41b84e86c</version>
 | 
			
		||||
            <version>2.6.1-SNAPSHOT</version>
 | 
			
		||||
            <scope>compile</scope>
 | 
			
		||||
            <exclusions>
 | 
			
		||||
                <exclusion>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,6 +50,7 @@ import org.geysermc.connector.entity.attribute.AttributeType;
 | 
			
		|||
import org.geysermc.connector.entity.living.ArmorStandEntity;
 | 
			
		||||
import org.geysermc.connector.entity.player.PlayerEntity;
 | 
			
		||||
import org.geysermc.connector.entity.type.EntityType;
 | 
			
		||||
import org.geysermc.connector.inventory.PlayerInventory;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
 | 
			
		||||
import org.geysermc.connector.utils.AttributeUtils;
 | 
			
		||||
| 
						 | 
				
			
			@ -284,11 +285,12 @@ public class Entity {
 | 
			
		|||
 | 
			
		||||
                    // Shield code
 | 
			
		||||
                    if (session.getPlayerEntity().getEntityId() == entityId && metadata.getFlags().getFlag(EntityFlag.SNEAKING)) {
 | 
			
		||||
                        if ((session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() == ItemRegistry.SHIELD.getJavaId()) ||
 | 
			
		||||
                                (session.getInventoryCache().getPlayerInventory().getItem(45) != null && session.getInventoryCache().getPlayerInventory().getItem(45).getId() == ItemRegistry.SHIELD.getJavaId())) {
 | 
			
		||||
                        PlayerInventory playerInv = session.getPlayerInventory();
 | 
			
		||||
                        if ((playerInv.getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) ||
 | 
			
		||||
                                (playerInv.getOffhand().getJavaId() == ItemRegistry.SHIELD.getJavaId())) {
 | 
			
		||||
                            ClientPlayerUseItemPacket useItemPacket;
 | 
			
		||||
                            metadata.getFlags().setFlag(EntityFlag.BLOCKING, true);
 | 
			
		||||
                            if (session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() == ItemRegistry.SHIELD.getJavaId()) {
 | 
			
		||||
                            if (playerInv.getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) {
 | 
			
		||||
                                useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
 | 
			
		||||
                            }
 | 
			
		||||
                            // Else we just assume it's the offhand, to simplify logic and to assure the packet gets sent
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,6 +30,7 @@ import com.nukkitx.math.vector.Vector3f;
 | 
			
		|||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
 | 
			
		||||
import org.geysermc.connector.entity.living.animal.AnimalEntity;
 | 
			
		||||
import org.geysermc.connector.entity.type.EntityType;
 | 
			
		||||
| 
						 | 
				
			
			@ -40,6 +41,9 @@ public class AbstractHorseEntity extends AnimalEntity {
 | 
			
		|||
 | 
			
		||||
    public AbstractHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
 | 
			
		||||
        super(entityId, geyserId, entityType, position, motion, rotation);
 | 
			
		||||
 | 
			
		||||
        // Specifies the size of the entity's inventory. Required to place slots in the entity.
 | 
			
		||||
        metadata.put(EntityData.CONTAINER_BASE_SIZE, 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -75,6 +79,9 @@ public class AbstractHorseEntity extends AnimalEntity {
 | 
			
		|||
                entityEventPacket.setData(ItemRegistry.WHEAT.getBedrockId() << 16);
 | 
			
		||||
                session.sendUpstreamPacket(entityEventPacket);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Set container type if tamed
 | 
			
		||||
            metadata.put(EntityData.CONTAINER_TYPE, ((xd & 0x02) == 0x02) ? (byte) ContainerType.HORSE.getId() : (byte) 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Needed to control horses
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ package org.geysermc.connector.entity.living.animal.horse;
 | 
			
		|||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
 | 
			
		||||
import com.nukkitx.math.vector.Vector3f;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
 | 
			
		||||
import org.geysermc.connector.entity.type.EntityType;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +36,8 @@ public class ChestedHorseEntity extends AbstractHorseEntity {
 | 
			
		|||
 | 
			
		||||
    public ChestedHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
 | 
			
		||||
        super(entityId, geyserId, entityType, position, motion, rotation);
 | 
			
		||||
 | 
			
		||||
        metadata.put(EntityData.CONTAINER_BASE_SIZE, 16);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,6 +38,8 @@ public class LlamaEntity extends ChestedHorseEntity {
 | 
			
		|||
 | 
			
		||||
    public LlamaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
 | 
			
		||||
        super(entityId, geyserId, entityType, position, motion, rotation);
 | 
			
		||||
 | 
			
		||||
        metadata.put(EntityData.CONTAINER_STRENGTH_MODIFIER, 3); // Presumably 3 slots for every 1 strength
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -56,7 +58,7 @@ public class LlamaEntity extends ChestedHorseEntity {
 | 
			
		|||
                // The damage value is the dye color that Java sends us
 | 
			
		||||
                // Always going to be a carpet so we can hardcode 171 in BlockTranslator
 | 
			
		||||
                // The int then short conversion is required or we get a ClassCastException
 | 
			
		||||
                equipmentPacket.setChestplate(ItemData.of(BlockTranslator.CARPET, (short)((int) entityMetadata.getValue()), 1));
 | 
			
		||||
                equipmentPacket.setChestplate(ItemData.of(BlockTranslator.CARPET, (short) ((int) entityMetadata.getValue()), 1));
 | 
			
		||||
            } else {
 | 
			
		||||
                equipmentPacket.setChestplate(ItemData.AIR);
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.inventory;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Used to determine if rename packets should be sent.
 | 
			
		||||
 */
 | 
			
		||||
public class AnvilContainer extends Container {
 | 
			
		||||
    public AnvilContainer(String title, int id, int size, PlayerInventory playerInventory) {
 | 
			
		||||
        super(title, id, size, playerInventory);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -23,16 +23,18 @@
 | 
			
		|||
 * @link https://github.com/GeyserMC/Geyser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory.action;
 | 
			
		||||
package org.geysermc.connector.inventory;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.ClickItemParam;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.WindowActionParam;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
enum Click {
 | 
			
		||||
    LEFT(ClickItemParam.LEFT_CLICK),
 | 
			
		||||
    RIGHT(ClickItemParam.RIGHT_CLICK);
 | 
			
		||||
@Getter
 | 
			
		||||
@Setter
 | 
			
		||||
public class BeaconContainer extends Container {
 | 
			
		||||
    private int primaryId;
 | 
			
		||||
    private int secondaryId;
 | 
			
		||||
 | 
			
		||||
    public final WindowActionParam actionParam;
 | 
			
		||||
    public BeaconContainer(String title, int id, int size, PlayerInventory playerInventory) {
 | 
			
		||||
        super(title, id, size, playerInventory);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.inventory;
 | 
			
		||||
 | 
			
		||||
public class CartographyContainer extends Container {
 | 
			
		||||
    public CartographyContainer(String title, int id, int size, PlayerInventory playerInventory) {
 | 
			
		||||
        super(title, id, size, playerInventory);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,69 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.inventory;
 | 
			
		||||
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.NonNull;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Combination of {@link Inventory} and {@link PlayerInventory}
 | 
			
		||||
 */
 | 
			
		||||
@Getter
 | 
			
		||||
public class Container extends Inventory {
 | 
			
		||||
    private final PlayerInventory playerInventory;
 | 
			
		||||
    private final int containerSize;
 | 
			
		||||
 | 
			
		||||
    public Container(String title, int id, int size, PlayerInventory playerInventory) {
 | 
			
		||||
        super(title, id, size);
 | 
			
		||||
        this.playerInventory = playerInventory;
 | 
			
		||||
        this.containerSize = this.size + InventoryTranslator.PLAYER_INVENTORY_SIZE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public GeyserItemStack getItem(int slot) {
 | 
			
		||||
        if (slot < this.size) {
 | 
			
		||||
            return super.getItem(slot);
 | 
			
		||||
        } else {
 | 
			
		||||
            return playerInventory.getItem(slot - this.size + InventoryTranslator.PLAYER_INVENTORY_OFFSET);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) {
 | 
			
		||||
        if (slot < this.size) {
 | 
			
		||||
            super.setItem(slot, newItem, session);
 | 
			
		||||
        } else {
 | 
			
		||||
            playerInventory.setItem(slot - this.size + InventoryTranslator.PLAYER_INVENTORY_OFFSET, newItem, session);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getSize() {
 | 
			
		||||
        return this.containerSize;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,52 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.inventory;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.EnchantOptionData;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
 | 
			
		||||
public class EnchantingContainer extends Container {
 | 
			
		||||
    /**
 | 
			
		||||
     * A cache of what Bedrock sees
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    private final EnchantOptionData[] enchantOptions;
 | 
			
		||||
    /**
 | 
			
		||||
     * A mutable cache of what the server sends us
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    private final GeyserEnchantOption[] geyserEnchantOptions;
 | 
			
		||||
 | 
			
		||||
    public EnchantingContainer(String title, int id, int size, PlayerInventory playerInventory) {
 | 
			
		||||
        super(title, id, size, playerInventory);
 | 
			
		||||
 | 
			
		||||
        enchantOptions = new EnchantOptionData[3];
 | 
			
		||||
        geyserEnchantOptions = new GeyserEnchantOption[3];
 | 
			
		||||
        for (int i = 0; i < geyserEnchantOptions.length; i++) {
 | 
			
		||||
            geyserEnchantOptions[i] = new GeyserEnchantOption(i);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,78 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.inventory;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.EnchantData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.EnchantOptionData;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A mutable "wrapper" around {@link EnchantOptionData}
 | 
			
		||||
 */
 | 
			
		||||
@Setter
 | 
			
		||||
public class GeyserEnchantOption {
 | 
			
		||||
    private static final List<EnchantData> EMPTY = Collections.emptyList();
 | 
			
		||||
    /**
 | 
			
		||||
     * This: https://cdn.discordapp.com/attachments/613168850925649981/791030657169227816/unknown.png
 | 
			
		||||
     * is controlled by the server.
 | 
			
		||||
     * So, of course, we have to throw in some easter eggs. ;)
 | 
			
		||||
     */
 | 
			
		||||
    private static final List<String> ENCHANT_NAMES = Arrays.asList("tougher armor", "lukeeey", "fall better",
 | 
			
		||||
            "explode less", "camo toy", "breathe better", "rtm five one six", "armor stab", "water walk", "you are elsa",
 | 
			
		||||
            "tim two zero three", "fast walk nether", "oof ouch owie", "enemy on fire", "spider sad", "aj ferguson", "redned",
 | 
			
		||||
            "more items thx", "long sword reach", "fast tool", "give me block", "less breaky break", "cube craft",
 | 
			
		||||
            "strong arrow", "fist arrow", "spicy arrow", "many many arrows", "geyser", "come here fish", "i like this",
 | 
			
		||||
            "stabby stab", "supreme mortal", "avatar i guess", "more arrows", "fly finder seventeen", "in and out",
 | 
			
		||||
            "xp heals tools", "dragon proxy waz here");
 | 
			
		||||
 | 
			
		||||
    @Getter
 | 
			
		||||
    private final int javaIndex;
 | 
			
		||||
 | 
			
		||||
    private int xpCost = 0;
 | 
			
		||||
    private int javaEnchantIndex = -1;
 | 
			
		||||
    private int bedrockEnchantIndex = -1;
 | 
			
		||||
    private int enchantLevel = -1;
 | 
			
		||||
 | 
			
		||||
    public GeyserEnchantOption(int javaIndex) {
 | 
			
		||||
        this.javaIndex = javaIndex;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public EnchantOptionData build(GeyserSession session) {
 | 
			
		||||
        if (enchantLevel == -1) {
 | 
			
		||||
            // Should not be sent to the client, as it is supposed to be empty
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return new EnchantOptionData(xpCost, javaIndex + 16, EMPTY,
 | 
			
		||||
                Collections.singletonList(new EnchantData(bedrockEnchantIndex, enchantLevel)), EMPTY,
 | 
			
		||||
                javaEnchantIndex == -1 ? "unknown" : ENCHANT_NAMES.get(javaEnchantIndex), session.getNextItemNetId());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,122 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.inventory;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
 | 
			
		||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemEntry;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
public class GeyserItemStack {
 | 
			
		||||
    public static final GeyserItemStack EMPTY = new GeyserItemStack(0, 0, null);
 | 
			
		||||
 | 
			
		||||
    private final int javaId;
 | 
			
		||||
    private int amount;
 | 
			
		||||
    private CompoundTag nbt;
 | 
			
		||||
    private int netId;
 | 
			
		||||
 | 
			
		||||
    public GeyserItemStack(int javaId) {
 | 
			
		||||
        this(javaId, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public GeyserItemStack(int javaId, int amount) {
 | 
			
		||||
        this(javaId, amount, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public GeyserItemStack(int javaId, int amount, CompoundTag nbt) {
 | 
			
		||||
        this(javaId, amount, nbt, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public GeyserItemStack(int javaId, int amount, CompoundTag nbt, int netId) {
 | 
			
		||||
        this.javaId = javaId;
 | 
			
		||||
        this.amount = amount;
 | 
			
		||||
        this.nbt = nbt;
 | 
			
		||||
        this.netId = netId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getJavaId() {
 | 
			
		||||
        return isEmpty() ? 0 : javaId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getAmount() {
 | 
			
		||||
        return isEmpty() ? 0 : amount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public CompoundTag getNbt() {
 | 
			
		||||
        return isEmpty() ? null : nbt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setNetId(int netId) {
 | 
			
		||||
        this.netId = netId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getNetId() {
 | 
			
		||||
        return isEmpty() ? 0 : netId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void add(int add) {
 | 
			
		||||
        amount += add;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void sub(int sub) {
 | 
			
		||||
        amount -= sub;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static GeyserItemStack from(ItemStack itemStack) {
 | 
			
		||||
        return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getNbt());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ItemStack getItemStack() {
 | 
			
		||||
        return isEmpty() ? null : new ItemStack(javaId, amount, nbt);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ItemData getItemData(GeyserSession session) {
 | 
			
		||||
        ItemData itemData = ItemTranslator.translateToBedrock(session, getItemStack());
 | 
			
		||||
        itemData.setNetId(getNetId());
 | 
			
		||||
        return itemData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ItemEntry getItemEntry() {
 | 
			
		||||
        return ItemRegistry.ITEM_ENTRIES.get(getJavaId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isEmpty() {
 | 
			
		||||
        return amount <= 0 || javaId == 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public GeyserItemStack copy() {
 | 
			
		||||
        return copy(amount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public GeyserItemStack copy(int newAmount) {
 | 
			
		||||
        return isEmpty() ? EMPTY : new GeyserItemStack(javaId, newAmount, nbt == null ? null : nbt.clone(), netId);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -25,26 +25,19 @@
 | 
			
		|||
 | 
			
		||||
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.NonNull;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.atomic.AtomicInteger;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
public class Inventory {
 | 
			
		||||
 | 
			
		||||
    @Getter
 | 
			
		||||
    protected int id;
 | 
			
		||||
 | 
			
		||||
    @Getter
 | 
			
		||||
    @Setter
 | 
			
		||||
    protected boolean open;
 | 
			
		||||
 | 
			
		||||
    @Getter
 | 
			
		||||
    protected WindowType windowType;
 | 
			
		||||
 | 
			
		||||
    @Getter
 | 
			
		||||
    protected final int size;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -52,9 +45,11 @@ public class Inventory {
 | 
			
		|||
    @Setter
 | 
			
		||||
    protected String title;
 | 
			
		||||
 | 
			
		||||
    @Setter
 | 
			
		||||
    protected ItemStack[] items;
 | 
			
		||||
    protected GeyserItemStack[] items;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The location of the inventory block. Will either be a fake block above the player's head, or the actual block location
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    @Setter
 | 
			
		||||
    protected Vector3i holderPosition = Vector3i.ZERO;
 | 
			
		||||
| 
						 | 
				
			
			@ -64,27 +59,41 @@ public class Inventory {
 | 
			
		|||
    protected long holderId = -1;
 | 
			
		||||
 | 
			
		||||
    @Getter
 | 
			
		||||
    protected AtomicInteger transactionId = new AtomicInteger(1);
 | 
			
		||||
    protected short transactionId = 0;
 | 
			
		||||
 | 
			
		||||
    public Inventory(int id, WindowType windowType, int size) {
 | 
			
		||||
        this("Inventory", id, windowType, size);
 | 
			
		||||
    protected Inventory(int id, int size) {
 | 
			
		||||
        this("Inventory", id, size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Inventory(String title, int id, WindowType windowType, int size) {
 | 
			
		||||
    protected Inventory(String title, int id, int size) {
 | 
			
		||||
        this.title = title;
 | 
			
		||||
        this.id = id;
 | 
			
		||||
        this.windowType = windowType;
 | 
			
		||||
        this.size = size;
 | 
			
		||||
        this.items = new ItemStack[size];
 | 
			
		||||
        this.items = new GeyserItemStack[size];
 | 
			
		||||
        Arrays.fill(items, GeyserItemStack.EMPTY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ItemStack getItem(int slot) {
 | 
			
		||||
    public GeyserItemStack 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;
 | 
			
		||||
    public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) {
 | 
			
		||||
        GeyserItemStack oldItem = items[slot];
 | 
			
		||||
        updateItemNetId(oldItem, newItem, session);
 | 
			
		||||
        items[slot] = newItem;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected static void updateItemNetId(GeyserItemStack oldItem, GeyserItemStack newItem, GeyserSession session) {
 | 
			
		||||
        if (!newItem.isEmpty()) {
 | 
			
		||||
            if (newItem.getItemData(session).equals(oldItem.getItemData(session), false, false, false)) {
 | 
			
		||||
                newItem.setNetId(oldItem.getNetId());
 | 
			
		||||
            } else {
 | 
			
		||||
                newItem.setNetId(session.getNextItemNetId());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public short getNextTransactionId() {
 | 
			
		||||
        return ++transactionId;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.inventory;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.math.vector.Vector3i;
 | 
			
		||||
import com.nukkitx.nbt.NbtMap;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
 | 
			
		||||
public class LecternContainer extends Container {
 | 
			
		||||
    @Getter @Setter
 | 
			
		||||
    private int currentBedrockPage = 0;
 | 
			
		||||
    @Getter @Setter
 | 
			
		||||
    private NbtMap blockEntityTag;
 | 
			
		||||
    @Getter @Setter
 | 
			
		||||
    private Vector3i position;
 | 
			
		||||
 | 
			
		||||
    public LecternContainer(String title, int id, int size, PlayerInventory playerInventory) {
 | 
			
		||||
        super(title, id, size, playerInventory);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.inventory;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import org.geysermc.connector.entity.Entity;
 | 
			
		||||
 | 
			
		||||
@Getter
 | 
			
		||||
@Setter
 | 
			
		||||
public class MerchantContainer extends Container {
 | 
			
		||||
    private Entity villager;
 | 
			
		||||
    private VillagerTrade[] villagerTrades;
 | 
			
		||||
 | 
			
		||||
    public MerchantContainer(String title, int id, int size, PlayerInventory playerInventory) {
 | 
			
		||||
        super(title, id, size, playerInventory);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -25,9 +25,10 @@
 | 
			
		|||
 | 
			
		||||
package org.geysermc.connector.inventory;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.NonNull;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
 | 
			
		||||
public class PlayerInventory extends Inventory {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -40,20 +41,28 @@ public class PlayerInventory extends Inventory {
 | 
			
		|||
    private int heldItemSlot;
 | 
			
		||||
 | 
			
		||||
    @Getter
 | 
			
		||||
    private ItemStack cursor;
 | 
			
		||||
    @NonNull
 | 
			
		||||
    private GeyserItemStack cursor = GeyserItemStack.EMPTY;
 | 
			
		||||
 | 
			
		||||
    public PlayerInventory() {
 | 
			
		||||
        super(0, null, 46);
 | 
			
		||||
        super(0, 46);
 | 
			
		||||
        heldItemSlot = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setCursor(ItemStack stack) {
 | 
			
		||||
        if (stack != null && (stack.getId() == 0 || stack.getAmount() < 1))
 | 
			
		||||
            stack = null;
 | 
			
		||||
        cursor = stack;
 | 
			
		||||
    public void setCursor(@NonNull GeyserItemStack newCursor, GeyserSession session) {
 | 
			
		||||
        updateItemNetId(cursor, newCursor, session);
 | 
			
		||||
        cursor = newCursor;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ItemStack getItemInHand() {
 | 
			
		||||
    public GeyserItemStack getItemInHand() {
 | 
			
		||||
        return items[36 + heldItemSlot];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setItemInHand(@NonNull GeyserItemStack item) {
 | 
			
		||||
        items[36 + heldItemSlot] = item;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public GeyserItemStack getOffhand() {
 | 
			
		||||
        return items[45];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,39 +23,31 @@
 | 
			
		|||
 * @link https://github.com/GeyserMC/Geyser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.geysermc.connector.network.session.cache;
 | 
			
		||||
package org.geysermc.connector.inventory;
 | 
			
		||||
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.NonNull;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
 | 
			
		||||
public class InventoryCache {
 | 
			
		||||
 | 
			
		||||
    private GeyserSession session;
 | 
			
		||||
 | 
			
		||||
public class StonecutterContainer extends Container {
 | 
			
		||||
    /**
 | 
			
		||||
     * The button that has currently been pressed Java-side
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    @Setter
 | 
			
		||||
    private Inventory openInventory;
 | 
			
		||||
    private int stonecutterButton = -1;
 | 
			
		||||
 | 
			
		||||
    @Getter
 | 
			
		||||
    private Int2ObjectMap<Inventory> inventories = new Int2ObjectOpenHashMap<>();
 | 
			
		||||
 | 
			
		||||
    public InventoryCache(GeyserSession session) {
 | 
			
		||||
        this.session = session;
 | 
			
		||||
    public StonecutterContainer(String title, int id, int size, PlayerInventory playerInventory) {
 | 
			
		||||
        super(title, id, size, playerInventory);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Inventory getPlayerInventory() {
 | 
			
		||||
        return inventories.get(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void cacheInventory(Inventory inventory) {
 | 
			
		||||
        inventories.put(inventory.getId(), inventory);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void uncacheInventory(int id) {
 | 
			
		||||
        inventories.remove(id);
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) {
 | 
			
		||||
        if (slot == 0 && newItem.getJavaId() != items[slot].getJavaId()) {
 | 
			
		||||
            // The pressed stonecutter button output resets whenever the input item changes
 | 
			
		||||
            this.stonecutterButton = -1;
 | 
			
		||||
        }
 | 
			
		||||
        super.setItem(slot, newItem, session);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +32,7 @@ import com.github.steveice10.mc.protocol.MinecraftConstants;
 | 
			
		|||
import com.github.steveice10.mc.protocol.MinecraftProtocol;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.SubProtocol;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.recipe.Recipe;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.statistic.Statistic;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
 | 
			
		||||
| 
						 | 
				
			
			@ -53,12 +54,15 @@ import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
 | 
			
		|||
import com.nukkitx.protocol.bedrock.packet.*;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.IntList;
 | 
			
		||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
 | 
			
		||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
 | 
			
		||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
 | 
			
		||||
import it.unimi.dsi.fastutil.objects.Object2LongMap;
 | 
			
		||||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
 | 
			
		||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
 | 
			
		||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
 | 
			
		||||
import lombok.AccessLevel;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.NonNull;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +74,7 @@ import org.geysermc.connector.common.AuthType;
 | 
			
		|||
import org.geysermc.connector.entity.Entity;
 | 
			
		||||
import org.geysermc.connector.entity.player.SessionPlayerEntity;
 | 
			
		||||
import org.geysermc.connector.entity.player.SkullPlayerEntity;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.inventory.PlayerInventory;
 | 
			
		||||
import org.geysermc.connector.network.remote.RemoteServer;
 | 
			
		||||
import org.geysermc.connector.network.session.auth.AuthData;
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +85,7 @@ import org.geysermc.connector.network.translators.EntityIdentifierRegistry;
 | 
			
		|||
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
 | 
			
		||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.collision.CollisionManager;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.EnchantmentInventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
 | 
			
		||||
import org.geysermc.connector.skin.SkinManager;
 | 
			
		||||
import org.geysermc.connector.utils.*;
 | 
			
		||||
| 
						 | 
				
			
			@ -93,8 +98,7 @@ import java.security.NoSuchAlgorithmException;
 | 
			
		|||
import java.security.PublicKey;
 | 
			
		||||
import java.security.spec.InvalidKeySpecException;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.concurrent.ScheduledFuture;
 | 
			
		||||
import java.util.concurrent.*;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicInteger;
 | 
			
		||||
 | 
			
		||||
@Getter
 | 
			
		||||
| 
						 | 
				
			
			@ -110,16 +114,32 @@ public class GeyserSession implements CommandSender {
 | 
			
		|||
    private BedrockClientData clientData;
 | 
			
		||||
 | 
			
		||||
    private final SessionPlayerEntity playerEntity;
 | 
			
		||||
    private PlayerInventory inventory;
 | 
			
		||||
 | 
			
		||||
    private ChunkCache chunkCache;
 | 
			
		||||
    private EntityCache entityCache;
 | 
			
		||||
    private EntityEffectCache effectCache;
 | 
			
		||||
    private InventoryCache inventoryCache;
 | 
			
		||||
    private WorldCache worldCache;
 | 
			
		||||
    private WindowCache windowCache;
 | 
			
		||||
    private final Int2ObjectMap<TeleportCache> teleportMap = new Int2ObjectOpenHashMap<>();
 | 
			
		||||
 | 
			
		||||
    private final PlayerInventory playerInventory;
 | 
			
		||||
    @Setter
 | 
			
		||||
    private Inventory openInventory;
 | 
			
		||||
 | 
			
		||||
    @Setter
 | 
			
		||||
    private InventoryTranslator inventoryTranslator = InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Use {@link #getNextItemNetId()} instead for consistency
 | 
			
		||||
     */
 | 
			
		||||
    @Getter(AccessLevel.NONE)
 | 
			
		||||
    private final AtomicInteger itemNetId = new AtomicInteger(1);
 | 
			
		||||
 | 
			
		||||
    @Getter(AccessLevel.NONE)
 | 
			
		||||
    private final Object inventoryLock = new Object();
 | 
			
		||||
    @Getter(AccessLevel.NONE)
 | 
			
		||||
    private CompletableFuture<Void> inventoryFuture;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Stores session collision
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -134,6 +154,16 @@ public class GeyserSession implements CommandSender {
 | 
			
		|||
     */
 | 
			
		||||
    private final Object2LongMap<Vector3i> itemFrameCache = new Object2LongOpenHashMap<>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Stores a list of all lectern locations and their block entity tags.
 | 
			
		||||
     * See {@link org.geysermc.connector.network.translators.world.WorldManager#getLecternDataAt(GeyserSession, int, int, int, boolean)}
 | 
			
		||||
     * for more information.
 | 
			
		||||
     */
 | 
			
		||||
    private final List<Vector3i> lecternCache = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    @Setter
 | 
			
		||||
    private boolean droppingLecternBook;
 | 
			
		||||
 | 
			
		||||
    @Setter
 | 
			
		||||
    private Vector2i lastChunkPosition = null;
 | 
			
		||||
    private int renderDistance;
 | 
			
		||||
| 
						 | 
				
			
			@ -191,9 +221,6 @@ public class GeyserSession implements CommandSender {
 | 
			
		|||
    @Setter
 | 
			
		||||
    private Entity ridingVehicleEntity;
 | 
			
		||||
 | 
			
		||||
    @Setter
 | 
			
		||||
    private int craftSlot = 0;
 | 
			
		||||
 | 
			
		||||
    @Setter
 | 
			
		||||
    private long lastWindowCloseTime = 0;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -202,10 +229,16 @@ public class GeyserSession implements CommandSender {
 | 
			
		|||
    @Setter
 | 
			
		||||
    private long lastInteractedVillagerEid;
 | 
			
		||||
 | 
			
		||||
    @Setter
 | 
			
		||||
    private Int2ObjectMap<Recipe> craftingRecipes;
 | 
			
		||||
    private final Set<String> unlockedRecipes;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Stores the enchantment information the client has received if they are in an enchantment table GUI
 | 
			
		||||
     * Saves a list of all stonecutter recipes, for use in a stonecutter inventory.
 | 
			
		||||
     * The key is the Java ID of the item; the values are all the possible outputs' Java IDs sorted by their string identifier
 | 
			
		||||
     */
 | 
			
		||||
    private final EnchantmentInventoryTranslator.EnchantmentSlotData[] enchantmentSlotData = new EnchantmentInventoryTranslator.EnchantmentSlotData[3];
 | 
			
		||||
    @Setter
 | 
			
		||||
    private Int2ObjectMap<IntList> stonecutterRecipes;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The current attack speed of the player. Used for sending proper cooldown timings.
 | 
			
		||||
| 
						 | 
				
			
			@ -338,20 +371,24 @@ public class GeyserSession implements CommandSender {
 | 
			
		|||
        this.chunkCache = new ChunkCache(this);
 | 
			
		||||
        this.entityCache = new EntityCache(this);
 | 
			
		||||
        this.effectCache = new EntityEffectCache();
 | 
			
		||||
        this.inventoryCache = new InventoryCache(this);
 | 
			
		||||
        this.worldCache = new WorldCache(this);
 | 
			
		||||
        this.windowCache = new WindowCache(this);
 | 
			
		||||
 | 
			
		||||
        this.collisionManager = new CollisionManager(this);
 | 
			
		||||
 | 
			
		||||
        this.playerEntity = new SessionPlayerEntity(this);
 | 
			
		||||
        this.inventory = new PlayerInventory();
 | 
			
		||||
        this.worldCache = new WorldCache(this);
 | 
			
		||||
        this.windowCache = new WindowCache(this);
 | 
			
		||||
 | 
			
		||||
        this.playerInventory = new PlayerInventory();
 | 
			
		||||
        this.openInventory = null;
 | 
			
		||||
        this.inventoryFuture = CompletableFuture.completedFuture(null);
 | 
			
		||||
        this.craftingRecipes = new Int2ObjectOpenHashMap<>();
 | 
			
		||||
        this.unlockedRecipes = new ObjectOpenHashSet<>();
 | 
			
		||||
 | 
			
		||||
        this.spawned = false;
 | 
			
		||||
        this.loggedIn = false;
 | 
			
		||||
 | 
			
		||||
        this.inventoryCache.getInventories().put(0, inventory);
 | 
			
		||||
 | 
			
		||||
        connector.getPlayers().forEach(player -> this.emotes.addAll(player.getEmotes()));
 | 
			
		||||
 | 
			
		||||
        bedrockServerSession.addDisconnectHandler(disconnectReason -> {
 | 
			
		||||
| 
						 | 
				
			
			@ -599,7 +636,6 @@ public class GeyserSession implements CommandSender {
 | 
			
		|||
        this.entityCache = null;
 | 
			
		||||
        this.effectCache = null;
 | 
			
		||||
        this.worldCache = null;
 | 
			
		||||
        this.inventoryCache = null;
 | 
			
		||||
        this.windowCache = null;
 | 
			
		||||
 | 
			
		||||
        closed = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -712,15 +748,57 @@ public class GeyserSession implements CommandSender {
 | 
			
		|||
        startGamePacket.setLevelName(serverName);
 | 
			
		||||
 | 
			
		||||
        startGamePacket.setPremiumWorldTemplateId("00000000-0000-0000-0000-000000000000");
 | 
			
		||||
        // startGamePacket.setCurrentTick(0);
 | 
			
		||||
        startGamePacket.setEnchantmentSeed(0);
 | 
			
		||||
        startGamePacket.setMultiplayerCorrelationId("");
 | 
			
		||||
        startGamePacket.setItemEntries(ItemRegistry.ITEMS);
 | 
			
		||||
        startGamePacket.setVanillaVersion("*");
 | 
			
		||||
        startGamePacket.setInventoriesServerAuthoritative(true);
 | 
			
		||||
        startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT);
 | 
			
		||||
        upstream.sendPacket(startGamePacket);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds a new inventory task.
 | 
			
		||||
     * Inventory tasks are executed one at a time, in order.
 | 
			
		||||
     *
 | 
			
		||||
     * @param task the task to run
 | 
			
		||||
     */
 | 
			
		||||
    public void addInventoryTask(Runnable task) {
 | 
			
		||||
        synchronized (inventoryLock) {
 | 
			
		||||
            System.out.println("new task " + task.toString());
 | 
			
		||||
            inventoryFuture = inventoryFuture.thenRun(task).exceptionally(throwable -> {
 | 
			
		||||
                GeyserConnector.getInstance().getLogger().error("Error processing inventory task", throwable.getCause());
 | 
			
		||||
                return null;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds a new inventory task with a delay.
 | 
			
		||||
     * The delay is achieved by scheduling with the Geyser general thread pool.
 | 
			
		||||
     * Inventory tasks are executed one at a time, in order.
 | 
			
		||||
     *
 | 
			
		||||
     * @param task the delayed task to run
 | 
			
		||||
     * @param delayMillis delay in milliseconds
 | 
			
		||||
     */
 | 
			
		||||
    public void addInventoryTask(Runnable task, long delayMillis) {
 | 
			
		||||
        synchronized (inventoryLock) {
 | 
			
		||||
            System.out.println("new delayed task " + task.toString());
 | 
			
		||||
            Executor delayedExecutor = command -> GeyserConnector.getInstance().getGeneralThreadPool().schedule(command, delayMillis, TimeUnit.MILLISECONDS);
 | 
			
		||||
            inventoryFuture = inventoryFuture.thenRunAsync(task, delayedExecutor).exceptionally(throwable -> {
 | 
			
		||||
                GeyserConnector.getInstance().getLogger().error("Error processing inventory task", throwable.getCause());
 | 
			
		||||
                return null;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return the next Bedrock item network ID to use for a new item
 | 
			
		||||
     */
 | 
			
		||||
    public int getNextItemNetId() {
 | 
			
		||||
        return itemNetId.getAndIncrement();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void addTeleport(TeleportCache teleportCache) {
 | 
			
		||||
        teleportMap.put(teleportCache.getTeleportConfirmId(), teleportCache);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,24 +38,24 @@ public class BedrockContainerCloseTranslator extends PacketTranslator<ContainerC
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translate(ContainerClosePacket packet, GeyserSession session) {
 | 
			
		||||
        session.setLastWindowCloseTime(0);
 | 
			
		||||
        byte windowId = packet.getId();
 | 
			
		||||
        Inventory openInventory = session.getInventoryCache().getOpenInventory();
 | 
			
		||||
        if (windowId == -1) { //player inventory or crafting table
 | 
			
		||||
            if (openInventory != null) {
 | 
			
		||||
                windowId = (byte) openInventory.getId();
 | 
			
		||||
            } else {
 | 
			
		||||
                windowId = 0;
 | 
			
		||||
        session.addInventoryTask(() -> {
 | 
			
		||||
            session.setLastWindowCloseTime(0);
 | 
			
		||||
            byte windowId = packet.getId();
 | 
			
		||||
 | 
			
		||||
            if (windowId == -1 && session.getOpenInventory() != null) {
 | 
			
		||||
                windowId = (byte) session.getOpenInventory().getId();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (windowId == 0 || (openInventory != null && openInventory.getId() == windowId)) {
 | 
			
		||||
            ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
 | 
			
		||||
            session.getDownstream().getSession().send(closeWindowPacket);
 | 
			
		||||
            InventoryUtils.closeInventory(session, windowId);
 | 
			
		||||
        }
 | 
			
		||||
            Inventory openInventory = session.getOpenInventory();
 | 
			
		||||
            if (openInventory != null && windowId == openInventory.getId()) {
 | 
			
		||||
                System.out.println(packet);
 | 
			
		||||
                ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
 | 
			
		||||
                session.sendDownstreamPacket(closeWindowPacket);
 | 
			
		||||
                InventoryUtils.closeInventory(session, windowId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        //Client wants close confirmation
 | 
			
		||||
        session.sendUpstreamPacket(packet);
 | 
			
		||||
            //Client wants close confirmation
 | 
			
		||||
            session.sendUpstreamPacket(packet);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,10 @@
 | 
			
		|||
 | 
			
		||||
package org.geysermc.connector.network.translators.bedrock;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientRenameItemPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.FilterTextPacket;
 | 
			
		||||
import org.geysermc.connector.inventory.AnvilContainer;
 | 
			
		||||
import org.geysermc.connector.inventory.CartographyContainer;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.PacketTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.Translator;
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +42,18 @@ public class BedrockFilterTextTranslator extends PacketTranslator<FilterTextPack
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translate(FilterTextPacket packet, GeyserSession session) {
 | 
			
		||||
        if (session.getOpenInventory() instanceof CartographyContainer) {
 | 
			
		||||
            // We don't want to be able to rename in the cartography table
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        packet.setFromServer(true);
 | 
			
		||||
        session.sendUpstreamPacket(packet);
 | 
			
		||||
 | 
			
		||||
        if (session.getOpenInventory() instanceof AnvilContainer) {
 | 
			
		||||
            // Java Edition sends a packet every time an item is renamed even slightly in GUI. Fortunately, this works out for us now
 | 
			
		||||
            ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(packet.getText());
 | 
			
		||||
            System.out.println(renameItemPacket);
 | 
			
		||||
            session.sendDownstreamPacket(renameItemPacket);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,6 @@
 | 
			
		|||
 | 
			
		||||
package org.geysermc.connector.network.translators.bedrock;
 | 
			
		||||
 | 
			
		||||
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.GameMode;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
 | 
			
		||||
| 
						 | 
				
			
			@ -43,6 +42,8 @@ import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
 | 
			
		|||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventorySource;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
 | 
			
		||||
import org.geysermc.connector.entity.CommandBlockMinecartEntity;
 | 
			
		||||
| 
						 | 
				
			
			@ -50,17 +51,15 @@ import org.geysermc.connector.entity.Entity;
 | 
			
		|||
import org.geysermc.connector.entity.ItemFrameEntity;
 | 
			
		||||
import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity;
 | 
			
		||||
import org.geysermc.connector.entity.type.EntityType;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.inventory.GeyserItemStack;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.PacketTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.Translator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemEntry;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
 | 
			
		||||
import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler;
 | 
			
		||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
 | 
			
		||||
import org.geysermc.connector.utils.BlockUtils;
 | 
			
		||||
import org.geysermc.connector.utils.InventoryUtils;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -71,15 +70,36 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
 | 
			
		|||
    public void translate(InventoryTransactionPacket packet, GeyserSession session) {
 | 
			
		||||
        switch (packet.getTransactionType()) {
 | 
			
		||||
            case NORMAL:
 | 
			
		||||
                Inventory inventory = session.getInventoryCache().getOpenInventory();
 | 
			
		||||
                if (inventory == null) inventory = session.getInventory();
 | 
			
		||||
                InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()).translateActions(session, inventory, packet.getActions());
 | 
			
		||||
                System.out.println(packet);
 | 
			
		||||
                if (packet.getActions().size() == 2) {
 | 
			
		||||
                    InventoryActionData worldAction = packet.getActions().get(0);
 | 
			
		||||
                    InventoryActionData containerAction = packet.getActions().get(1);
 | 
			
		||||
                    if (worldAction.getSource().getType() == InventorySource.Type.WORLD_INTERACTION
 | 
			
		||||
                            && worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
 | 
			
		||||
                        session.addInventoryTask(() -> {
 | 
			
		||||
                            if (session.getPlayerInventory().getHeldItemSlot() != containerAction.getSlot())
 | 
			
		||||
                                return;
 | 
			
		||||
                            if (session.getPlayerInventory().getItemInHand().isEmpty())
 | 
			
		||||
                                return;
 | 
			
		||||
 | 
			
		||||
                            boolean dropAll = worldAction.getToItem().getCount() > 1;
 | 
			
		||||
                            ClientPlayerActionPacket dropAllPacket = new ClientPlayerActionPacket(
 | 
			
		||||
                                    dropAll ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM,
 | 
			
		||||
                                    new Position(0, 0, 0),
 | 
			
		||||
                                    BlockFace.DOWN
 | 
			
		||||
                            );
 | 
			
		||||
                            session.sendDownstreamPacket(dropAllPacket);
 | 
			
		||||
 | 
			
		||||
                            if (dropAll) {
 | 
			
		||||
                                session.getPlayerInventory().setItemInHand(GeyserItemStack.EMPTY);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                session.getPlayerInventory().getItemInHand().sub(1);
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            case INVENTORY_MISMATCH:
 | 
			
		||||
                Inventory inv = session.getInventoryCache().getOpenInventory();
 | 
			
		||||
                if (inv == null) inv = session.getInventory();
 | 
			
		||||
                InventoryTranslator.INVENTORY_TRANSLATORS.get(inv.getWindowType()).updateInventory(session, inv);
 | 
			
		||||
                InventoryUtils.updateCursor(session);
 | 
			
		||||
                break;
 | 
			
		||||
            case ITEM_USE:
 | 
			
		||||
                switch (packet.getActionType()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -165,9 +185,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
 | 
			
		|||
                        session.setInteracting(true);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 1:
 | 
			
		||||
                        ItemStack shieldSlot = session.getInventory().getItem(session.getInventory().getHeldItemSlot() + 36);
 | 
			
		||||
                        // Handled in Entity.java
 | 
			
		||||
                        if (shieldSlot != null && shieldSlot.getId() == ItemRegistry.SHIELD.getJavaId()) {
 | 
			
		||||
                        if (session.getPlayerInventory().getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) {
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket;
 | 
			
		||||
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.Translator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
 | 
			
		||||
 | 
			
		||||
@Translator(packet = ItemStackRequestPacket.class)
 | 
			
		||||
public class BedrockItemStackRequestTranslator extends PacketTranslator<ItemStackRequestPacket> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translate(ItemStackRequestPacket packet, GeyserSession session) {
 | 
			
		||||
        session.getConnector().getLogger().info(packet.toString());
 | 
			
		||||
        Inventory inventory = session.getOpenInventory();
 | 
			
		||||
        if (inventory == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        InventoryTranslator translator = session.getInventoryTranslator();
 | 
			
		||||
        session.addInventoryTask(() -> translator.translateRequests(session, inventory, packet.getRequests()));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,95 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.data.game.entity.metadata.Position;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket;
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket;
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
 | 
			
		||||
import com.nukkitx.math.vector.Vector3f;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.LecternUpdatePacket;
 | 
			
		||||
import org.geysermc.connector.inventory.LecternContainer;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.PacketTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.Translator;
 | 
			
		||||
import org.geysermc.connector.utils.InventoryUtils;
 | 
			
		||||
 | 
			
		||||
@Translator(packet = LecternUpdatePacket.class)
 | 
			
		||||
public class BedrockLecternUpdateTranslator extends PacketTranslator<LecternUpdatePacket> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translate(LecternUpdatePacket packet, GeyserSession session) {
 | 
			
		||||
        session.getConnector().getLogger().error(packet.toString());
 | 
			
		||||
        if (packet.isDroppingBook()) {
 | 
			
		||||
            // Bedrock drops the book outside of the GUI. Java drops it in the GUI
 | 
			
		||||
            // So, we enter the GUI and then drop it! :)
 | 
			
		||||
            session.setDroppingLecternBook(true);
 | 
			
		||||
 | 
			
		||||
            Vector3f diff = session.getPlayerEntity().getPosition().sub(packet.getBlockPosition().toFloat());
 | 
			
		||||
            System.out.println(diff);
 | 
			
		||||
            // Emulate an interact packet
 | 
			
		||||
            ClientPlayerPlaceBlockPacket blockPacket = new ClientPlayerPlaceBlockPacket(
 | 
			
		||||
                    new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()),
 | 
			
		||||
                    BlockFace.values()[0],
 | 
			
		||||
                    Hand.MAIN_HAND,
 | 
			
		||||
                    packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ(), //TODO
 | 
			
		||||
                    false);
 | 
			
		||||
            session.sendDownstreamPacket(blockPacket);
 | 
			
		||||
        } else {
 | 
			
		||||
            // Bedrock wants to either move a page or exit
 | 
			
		||||
            LecternContainer lecternContainer = (LecternContainer) session.getOpenInventory();
 | 
			
		||||
            if (lecternContainer.getCurrentBedrockPage() == packet.getPage()) {
 | 
			
		||||
                // The same page means Bedrock is closing the window
 | 
			
		||||
                ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(lecternContainer.getId());
 | 
			
		||||
                session.sendDownstreamPacket(closeWindowPacket);
 | 
			
		||||
                InventoryUtils.closeInventory(session, lecternContainer.getId());
 | 
			
		||||
            } else {
 | 
			
		||||
                // Each "page" Bedrock gives to us actually represents two pages (think opening a book and seeing two pages)
 | 
			
		||||
                // Each "page" on Java is just one page (think a spiral notebook folded back to only show one page)
 | 
			
		||||
                int newJavaPage = (packet.getPage() * 2);
 | 
			
		||||
                int currentJavaPage = (lecternContainer.getCurrentBedrockPage() * 2);
 | 
			
		||||
                // Send as many click button packets as we need to
 | 
			
		||||
                // Java has the option to specify exact page numbers by adding 100 to the number, but buttonId variable
 | 
			
		||||
                // is a byte and therefore this stops us at 128
 | 
			
		||||
                if (newJavaPage > currentJavaPage) {
 | 
			
		||||
                    for (int i = currentJavaPage; i < newJavaPage; i++) {
 | 
			
		||||
                        ClientClickWindowButtonPacket clickButtonPacket = new ClientClickWindowButtonPacket(session.getOpenInventory().getId(), 2);
 | 
			
		||||
                        System.out.println(clickButtonPacket);
 | 
			
		||||
                        session.sendDownstreamPacket(clickButtonPacket);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    for (int i = currentJavaPage; i > newJavaPage; i--) {
 | 
			
		||||
                        ClientClickWindowButtonPacket clickButtonPacket = new ClientClickWindowButtonPacket(session.getOpenInventory().getId(), 1);
 | 
			
		||||
                        System.out.println(clickButtonPacket);
 | 
			
		||||
                        session.sendDownstreamPacket(clickButtonPacket);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -40,12 +40,12 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
 | 
			
		|||
    @Override
 | 
			
		||||
    public void translate(MobEquipmentPacket packet, GeyserSession session) {
 | 
			
		||||
        if (!session.isSpawned() || packet.getHotbarSlot() > 8 ||
 | 
			
		||||
                packet.getContainerId() != ContainerId.INVENTORY || session.getInventory().getHeldItemSlot() == packet.getHotbarSlot()) {
 | 
			
		||||
                packet.getContainerId() != ContainerId.INVENTORY || session.getPlayerInventory().getHeldItemSlot() == packet.getHotbarSlot()) {
 | 
			
		||||
            // For the last condition - Don't update the slot if the slot is the same - not Java Edition behavior and messes with plugins such as Grief Prevention
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        session.getInventory().setHeldItemSlot(packet.getHotbarSlot());
 | 
			
		||||
        session.getPlayerInventory().setHeldItemSlot(packet.getHotbarSlot());
 | 
			
		||||
 | 
			
		||||
        ClientPlayerChangeHeldItemPacket changeHeldItemPacket = new ClientPlayerChangeHeldItemPacket(packet.getHotbarSlot());
 | 
			
		||||
        session.sendDownstreamPacket(changeHeldItemPacket);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,12 +26,13 @@
 | 
			
		|||
package org.geysermc.connector.network.translators.bedrock.entity;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientSelectTradePacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
 | 
			
		||||
import org.geysermc.connector.entity.Entity;
 | 
			
		||||
import org.geysermc.connector.inventory.GeyserItemStack;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.inventory.MerchantContainer;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.PacketTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.Translator;
 | 
			
		||||
| 
						 | 
				
			
			@ -47,20 +48,25 @@ public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPa
 | 
			
		|||
                session.sendUpstreamPacket(packet);
 | 
			
		||||
                return;
 | 
			
		||||
            case COMPLETE_TRADE:
 | 
			
		||||
                ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
 | 
			
		||||
                session.sendDownstreamPacket(selectTradePacket);
 | 
			
		||||
                session.addInventoryTask(() -> {
 | 
			
		||||
                    ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
 | 
			
		||||
                    session.sendDownstreamPacket(selectTradePacket);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                Entity villager = session.getPlayerEntity();
 | 
			
		||||
                Inventory openInventory = session.getInventoryCache().getOpenInventory();
 | 
			
		||||
                if (openInventory != null && openInventory.getWindowType() == WindowType.MERCHANT) {
 | 
			
		||||
                    VillagerTrade[] trades = session.getVillagerTrades();
 | 
			
		||||
                    if (trades != null && packet.getData() >= 0 && packet.getData() < trades.length) {
 | 
			
		||||
                        VillagerTrade trade = session.getVillagerTrades()[packet.getData()];
 | 
			
		||||
                        openInventory.setItem(2, trade.getOutput());
 | 
			
		||||
                        villager.getMetadata().put(EntityData.TRADE_XP, trade.getXp() + villager.getMetadata().getInt(EntityData.TRADE_XP));
 | 
			
		||||
                        villager.updateBedrockMetadata(session);
 | 
			
		||||
                session.addInventoryTask(() -> {
 | 
			
		||||
                    Entity villager = session.getPlayerEntity();
 | 
			
		||||
                    Inventory openInventory = session.getOpenInventory();
 | 
			
		||||
                    if (openInventory instanceof MerchantContainer) {
 | 
			
		||||
                        MerchantContainer merchantInventory = (MerchantContainer) openInventory;
 | 
			
		||||
                        VillagerTrade[] trades = merchantInventory.getVillagerTrades();
 | 
			
		||||
                        if (trades != null && packet.getData() >= 0 && packet.getData() < trades.length) {
 | 
			
		||||
                            VillagerTrade trade = merchantInventory.getVillagerTrades()[packet.getData()];
 | 
			
		||||
                            openInventory.setItem(2, GeyserItemStack.from(trade.getOutput()), session);
 | 
			
		||||
                            villager.getMetadata().put(EntityData.TRADE_XP, trade.getXp() + villager.getMetadata().getInt(EntityData.TRADE_XP));
 | 
			
		||||
                            villager.updateBedrockMetadata(session);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                }, 100);
 | 
			
		||||
                return;
 | 
			
		||||
        }
 | 
			
		||||
        session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,10 @@ import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
 | 
			
		|||
import com.nukkitx.protocol.bedrock.packet.InteractPacket;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import org.geysermc.connector.entity.Entity;
 | 
			
		||||
import org.geysermc.connector.entity.living.animal.horse.AbstractHorseEntity;
 | 
			
		||||
import org.geysermc.connector.entity.living.animal.horse.HorseEntity;
 | 
			
		||||
import org.geysermc.connector.entity.type.EntityType;
 | 
			
		||||
import org.geysermc.connector.inventory.GeyserItemStack;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.PacketTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.Translator;
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +101,7 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
 | 
			
		|||
 | 
			
		||||
        switch (packet.getAction()) {
 | 
			
		||||
            case INTERACT:
 | 
			
		||||
                if (session.getInventory().getItem(session.getInventory().getHeldItemSlot() + 36).getId() == ItemRegistry.SHIELD.getJavaId()) {
 | 
			
		||||
                if (session.getPlayerInventory().getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
 | 
			
		||||
| 
						 | 
				
			
			@ -122,7 +125,7 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
 | 
			
		|||
                    if (interactEntity == null)
 | 
			
		||||
                        return;
 | 
			
		||||
                    EntityDataMap entityMetadata = interactEntity.getMetadata();
 | 
			
		||||
                    ItemEntry itemEntry = session.getInventory().getItemInHand() == null ? ItemEntry.AIR : ItemRegistry.getItem(session.getInventory().getItemInHand());
 | 
			
		||||
                    ItemEntry itemEntry = session.getPlayerInventory().getItemInHand() == GeyserItemStack.EMPTY ? ItemEntry.AIR : ItemRegistry.getItem(session.getPlayerInventory().getItemInHand().getItemStack());
 | 
			
		||||
                    String javaIdentifierStripped = itemEntry.getJavaIdentifier().replace("minecraft:", "");
 | 
			
		||||
 | 
			
		||||
                    // TODO - in the future, update these in the metadata? So the client doesn't have to wiggle their cursor around for it to happen
 | 
			
		||||
| 
						 | 
				
			
			@ -136,8 +139,8 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
 | 
			
		|||
                            interactEntity.getEntityType() == EntityType.PIG || interactEntity.getEntityType() == EntityType.STRIDER)) {
 | 
			
		||||
                        // Entity can be saddled and the conditions meet (entity can be saddled and, if needed, is tamed)
 | 
			
		||||
                        interactiveTag = InteractiveTag.SADDLE;
 | 
			
		||||
                    } else if (javaIdentifierStripped.equals("name_tag") && session.getInventory().getItemInHand().getNbt() != null &&
 | 
			
		||||
                        session.getInventory().getItemInHand().getNbt().contains("display")) {
 | 
			
		||||
                    } else if (javaIdentifierStripped.equals("name_tag") && session.getPlayerInventory().getItemInHand().getNbt() != null &&
 | 
			
		||||
                        session.getPlayerInventory().getItemInHand().getNbt().contains("display")) {
 | 
			
		||||
                        // Holding a named name tag
 | 
			
		||||
                        interactiveTag = InteractiveTag.NAME;
 | 
			
		||||
                    } else if (javaIdentifierStripped.equals("lead") && LEASHABLE_MOB_TYPES.contains(interactEntity.getEntityType()) &&
 | 
			
		||||
| 
						 | 
				
			
			@ -210,6 +213,11 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
 | 
			
		|||
                            case SKELETON_HORSE:
 | 
			
		||||
                            case TRADER_LLAMA:
 | 
			
		||||
                            case ZOMBIE_HORSE:
 | 
			
		||||
                                boolean tamed = entityMetadata.getFlags().getFlag(EntityFlag.TAMED);
 | 
			
		||||
                                if (session.isSneaking() && tamed && (interactEntity instanceof HorseEntity || entityMetadata.getFlags().getFlag(EntityFlag.CHESTED))) {
 | 
			
		||||
                                    interactiveTag = InteractiveTag.OPEN_CONTAINER;
 | 
			
		||||
                                    break;
 | 
			
		||||
                                }
 | 
			
		||||
                                // have another switch statement as, while these share mount attributes they don't share food
 | 
			
		||||
                                switch (interactEntity.getEntityType()) {
 | 
			
		||||
                                    case LLAMA:
 | 
			
		||||
| 
						 | 
				
			
			@ -228,9 +236,9 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
 | 
			
		|||
                                }
 | 
			
		||||
                                if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY)) {
 | 
			
		||||
                                    // Can't ride a baby
 | 
			
		||||
                                    if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) {
 | 
			
		||||
                                    if (tamed) {
 | 
			
		||||
                                        interactiveTag = InteractiveTag.RIDE_HORSE;
 | 
			
		||||
                                    } else if (!entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && itemEntry.equals(ItemEntry.AIR)) {
 | 
			
		||||
                                    } else if (itemEntry.equals(ItemEntry.AIR)) {
 | 
			
		||||
                                        // Can't hide an untamed entity without having your hand empty
 | 
			
		||||
                                        interactiveTag = InteractiveTag.MOUNT;
 | 
			
		||||
                                    }
 | 
			
		||||
| 
						 | 
				
			
			@ -349,20 +357,30 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
 | 
			
		|||
                } else {
 | 
			
		||||
                    if (!session.getPlayerEntity().getMetadata().getString(EntityData.INTERACTIVE_TAG).isEmpty()) {
 | 
			
		||||
                        // No interactive tag should be sent
 | 
			
		||||
                        session.getPlayerEntity().getMetadata().remove(EntityData.INTERACTIVE_TAG);
 | 
			
		||||
                        session.getPlayerEntity().getMetadata().put(EntityData.INTERACTIVE_TAG, "");
 | 
			
		||||
                        session.getPlayerEntity().updateBedrockMetadata(session);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            case OPEN_INVENTORY:
 | 
			
		||||
                if (!session.getInventory().isOpen()) {
 | 
			
		||||
                    ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
 | 
			
		||||
                    containerOpenPacket.setId((byte) 0);
 | 
			
		||||
                    containerOpenPacket.setType(ContainerType.INVENTORY);
 | 
			
		||||
                    containerOpenPacket.setUniqueEntityId(-1);
 | 
			
		||||
                    containerOpenPacket.setBlockPosition(entity.getPosition().toInt());
 | 
			
		||||
                    session.sendUpstreamPacket(containerOpenPacket);
 | 
			
		||||
                    session.getInventory().setOpen(true);
 | 
			
		||||
                if (session.getOpenInventory() == null) {
 | 
			
		||||
                    Entity ridingEntity = session.getRidingVehicleEntity();
 | 
			
		||||
                    if (ridingEntity instanceof AbstractHorseEntity) {
 | 
			
		||||
                        if (ridingEntity.getMetadata().getFlags().getFlag(EntityFlag.TAMED)) {
 | 
			
		||||
                            // We should request to open the horse inventory instead
 | 
			
		||||
                            ClientPlayerStatePacket openHorseWindowPacket = new ClientPlayerStatePacket((int)session.getPlayerEntity().getEntityId(), PlayerState.OPEN_HORSE_INVENTORY);
 | 
			
		||||
                            session.sendDownstreamPacket(openHorseWindowPacket);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        session.setOpenInventory(session.getPlayerInventory());
 | 
			
		||||
 | 
			
		||||
                        ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
 | 
			
		||||
                        containerOpenPacket.setId((byte) 0);
 | 
			
		||||
                        containerOpenPacket.setType(ContainerType.INVENTORY);
 | 
			
		||||
                        containerOpenPacket.setUniqueEntityId(-1);
 | 
			
		||||
                        containerOpenPacket.setBlockPosition(entity.getPosition().toInt());
 | 
			
		||||
                        session.sendUpstreamPacket(containerOpenPacket);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,167 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.packet.ingame.client.window.ClientRenameItemPacket;
 | 
			
		||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
 | 
			
		||||
import com.google.gson.JsonSyntaxException;
 | 
			
		||||
import com.nukkitx.nbt.NbtMap;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.*;
 | 
			
		||||
import net.kyori.adventure.text.Component;
 | 
			
		||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
 | 
			
		||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
 | 
			
		||||
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;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
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.UI) {
 | 
			
		||||
            switch (action.getSlot()) {
 | 
			
		||||
                case 1:
 | 
			
		||||
                    return 0;
 | 
			
		||||
                case 2:
 | 
			
		||||
                    return 1;
 | 
			
		||||
                case 50:
 | 
			
		||||
                    return 2;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (action.getSource().getContainerId() == ContainerId.ANVIL_RESULT) {
 | 
			
		||||
            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;
 | 
			
		||||
            NbtMap tag = itemName.getTag();
 | 
			
		||||
            if (tag != null && tag.containsKey("display")) {
 | 
			
		||||
                String name = tag.getCompound("display").getString("Name");
 | 
			
		||||
                try {
 | 
			
		||||
                    Component component = GsonComponentSerializer.gson().deserialize(name);
 | 
			
		||||
                    rename = LegacyComponentSerializer.legacySection().serialize(component);
 | 
			
		||||
                } catch (JsonSyntaxException e) {
 | 
			
		||||
                    rename = name;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                rename = "";
 | 
			
		||||
            }
 | 
			
		||||
            ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename);
 | 
			
		||||
            session.sendDownstreamPacket(renameItemPacket);
 | 
			
		||||
        }
 | 
			
		||||
        if (anvilResult != null) {
 | 
			
		||||
            //Strip unnecessary actions
 | 
			
		||||
            List<InventoryActionData> strippedActions = actions.stream()
 | 
			
		||||
                    .filter(action -> action.getSource().getContainerId() == ContainerId.ANVIL_RESULT
 | 
			
		||||
                            || (action.getSource().getType() == InventorySource.Type.CONTAINER
 | 
			
		||||
                            && !(action.getSource().getContainerId() == ContainerId.UI && action.getSlot() != 0)))
 | 
			
		||||
                    .collect(Collectors.toList());
 | 
			
		||||
            super.translateActions(session, inventory, strippedActions);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        super.translateActions(session, inventory, actions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
 | 
			
		||||
        if (slot == 0) {
 | 
			
		||||
            ItemStack item = inventory.getItem(slot);
 | 
			
		||||
            if (item != null) {
 | 
			
		||||
                String rename;
 | 
			
		||||
                CompoundTag tag = item.getNbt();
 | 
			
		||||
                if (tag != null) {
 | 
			
		||||
                    CompoundTag displayTag = tag.get("display");
 | 
			
		||||
                    if (displayTag != null && displayTag.contains("Name")) {
 | 
			
		||||
                        String itemName = displayTag.get("Name").getValue().toString();
 | 
			
		||||
                        try {
 | 
			
		||||
                            Component component = GsonComponentSerializer.gson().deserialize(itemName);
 | 
			
		||||
                            rename = LegacyComponentSerializer.legacySection().serialize(component);
 | 
			
		||||
                        } catch (JsonSyntaxException e) {
 | 
			
		||||
                            rename = itemName;
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        rename = "";
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    rename = "";
 | 
			
		||||
                }
 | 
			
		||||
                ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename);
 | 
			
		||||
                session.sendDownstreamPacket(renameItemPacket);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        super.updateSlot(session, inventory, slot);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.inventory.ContainerSlotType;
 | 
			
		||||
import lombok.Value;
 | 
			
		||||
 | 
			
		||||
@Value
 | 
			
		||||
public class BedrockContainerSlot {
 | 
			
		||||
    ContainerSlotType container;
 | 
			
		||||
    int slot;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,264 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.ClientClickWindowButtonPacket;
 | 
			
		||||
import com.nukkitx.nbt.NbtMap;
 | 
			
		||||
import com.nukkitx.nbt.NbtMapBuilder;
 | 
			
		||||
import com.nukkitx.nbt.NbtType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import lombok.ToString;
 | 
			
		||||
import org.geysermc.connector.common.ChatColor;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
 | 
			
		||||
import org.geysermc.connector.utils.InventoryUtils;
 | 
			
		||||
import org.geysermc.connector.utils.LocaleUtils;
 | 
			
		||||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A temporary reconstruction of the enchantment table UI until our inventory rewrite is complete.
 | 
			
		||||
 * The enchantment table on Bedrock without server authoritative inventories doesn't tell us which button is pressed
 | 
			
		||||
 * when selecting an enchantment.
 | 
			
		||||
 */
 | 
			
		||||
public class EnchantmentInventoryTranslator extends BlockInventoryTranslator {
 | 
			
		||||
 | 
			
		||||
    private static final int DYE_ID = ItemRegistry.getItemEntry("minecraft:lapis_lazuli").getBedrockId();
 | 
			
		||||
    private static final int ENCHANTED_BOOK_ID = ItemRegistry.getItemEntry("minecraft:enchanted_book").getBedrockId();
 | 
			
		||||
 | 
			
		||||
    public EnchantmentInventoryTranslator(InventoryUpdater updater) {
 | 
			
		||||
        super(2, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, updater);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
 | 
			
		||||
        for (InventoryActionData action : actions) {
 | 
			
		||||
            if (action.getSource().getContainerId() == inventory.getId()) {
 | 
			
		||||
                // This is the hopper UI
 | 
			
		||||
                switch (action.getSlot()) {
 | 
			
		||||
                    case 1:
 | 
			
		||||
                        // Don't allow the slot to be put through if the item isn't lapis
 | 
			
		||||
                        if ((action.getToItem().getId() != DYE_ID) && action.getToItem() != ItemData.AIR) {
 | 
			
		||||
                            updateInventory(session, inventory);
 | 
			
		||||
                            InventoryUtils.updateCursor(session);
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 2:
 | 
			
		||||
                    case 3:
 | 
			
		||||
                    case 4:
 | 
			
		||||
                        // The books here act as buttons
 | 
			
		||||
                        ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), action.getSlot() - 2);
 | 
			
		||||
                        session.sendDownstreamPacket(packet);
 | 
			
		||||
                        updateInventory(session, inventory);
 | 
			
		||||
                        InventoryUtils.updateCursor(session);
 | 
			
		||||
                        return;
 | 
			
		||||
                    default:
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        super.translateActions(session, inventory, actions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateInventory(GeyserSession session, Inventory inventory) {
 | 
			
		||||
        super.updateInventory(session, inventory);
 | 
			
		||||
        List<ItemData> items = new ArrayList<>(5);
 | 
			
		||||
        items.add(ItemTranslator.translateToBedrock(session, inventory.getItem(0)));
 | 
			
		||||
        items.add(ItemTranslator.translateToBedrock(session, inventory.getItem(1)));
 | 
			
		||||
        for (int i = 0; i < 3; i++) {
 | 
			
		||||
            items.add(session.getEnchantmentSlotData()[i].getItem() != null ? session.getEnchantmentSlotData()[i].getItem() : createEnchantmentBook());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        InventoryContentPacket contentPacket = new InventoryContentPacket();
 | 
			
		||||
        contentPacket.setContainerId(inventory.getId());
 | 
			
		||||
        contentPacket.setContents(items);
 | 
			
		||||
        session.sendUpstreamPacket(contentPacket);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
 | 
			
		||||
        int bookSlotToUpdate;
 | 
			
		||||
        switch (key) {
 | 
			
		||||
            case 0:
 | 
			
		||||
            case 1:
 | 
			
		||||
            case 2:
 | 
			
		||||
                // Experience required
 | 
			
		||||
                bookSlotToUpdate = key;
 | 
			
		||||
                session.getEnchantmentSlotData()[bookSlotToUpdate].setExperienceRequired(value);
 | 
			
		||||
                break;
 | 
			
		||||
            case 4:
 | 
			
		||||
            case 5:
 | 
			
		||||
            case 6:
 | 
			
		||||
                // Enchantment name
 | 
			
		||||
                bookSlotToUpdate = key - 4;
 | 
			
		||||
                if (value != -1) {
 | 
			
		||||
                    session.getEnchantmentSlotData()[bookSlotToUpdate].setEnchantmentType(EnchantmentTableEnchantments.values()[value - 1]);
 | 
			
		||||
                } else {
 | 
			
		||||
                    // -1 means no enchantment specified
 | 
			
		||||
                    session.getEnchantmentSlotData()[bookSlotToUpdate].setEnchantmentType(null);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            case 7:
 | 
			
		||||
            case 8:
 | 
			
		||||
            case 9:
 | 
			
		||||
                // Enchantment level
 | 
			
		||||
                bookSlotToUpdate = key - 7;
 | 
			
		||||
                session.getEnchantmentSlotData()[bookSlotToUpdate].setEnchantmentLevel(value);
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                return;
 | 
			
		||||
        }
 | 
			
		||||
        updateEnchantmentBook(session, inventory, bookSlotToUpdate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void openInventory(GeyserSession session, Inventory inventory) {
 | 
			
		||||
        super.openInventory(session, inventory);
 | 
			
		||||
        for (int i = 0; i < session.getEnchantmentSlotData().length; i++) {
 | 
			
		||||
            session.getEnchantmentSlotData()[i] = new EnchantmentSlotData();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void closeInventory(GeyserSession session, Inventory inventory) {
 | 
			
		||||
        super.closeInventory(session, inventory);
 | 
			
		||||
        Arrays.fill(session.getEnchantmentSlotData(), null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ItemData createEnchantmentBook() {
 | 
			
		||||
        NbtMapBuilder root = NbtMap.builder();
 | 
			
		||||
        NbtMapBuilder display = NbtMap.builder();
 | 
			
		||||
 | 
			
		||||
        display.putString("Name", ChatColor.RESET + "No Enchantment");
 | 
			
		||||
 | 
			
		||||
        root.put("display", display.build());
 | 
			
		||||
        return ItemData.of(ENCHANTED_BOOK_ID, (short) 0, 1, root.build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void updateEnchantmentBook(GeyserSession session, Inventory inventory, int slot) {
 | 
			
		||||
        NbtMapBuilder root = NbtMap.builder();
 | 
			
		||||
        NbtMapBuilder display = NbtMap.builder();
 | 
			
		||||
        EnchantmentSlotData data = session.getEnchantmentSlotData()[slot];
 | 
			
		||||
        if (data.getEnchantmentType() != null) {
 | 
			
		||||
            display.putString("Name", ChatColor.ITALIC + data.getEnchantmentType().toString(session) +
 | 
			
		||||
                    (data.getEnchantmentLevel() != -1 ? " " + toRomanNumeral(session, data.getEnchantmentLevel()) : "") + "?");
 | 
			
		||||
        } else {
 | 
			
		||||
            display.putString("Name", ChatColor.RESET + "No Enchantment");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        display.putList("Lore", NbtType.STRING, Collections.singletonList(ChatColor.DARK_GRAY + data.getExperienceRequired() + "xp"));
 | 
			
		||||
        root.put("display", display.build());
 | 
			
		||||
        ItemData book = ItemData.of(ENCHANTED_BOOK_ID, (short) 0, 1, root.build());
 | 
			
		||||
 | 
			
		||||
        InventorySlotPacket slotPacket = new InventorySlotPacket();
 | 
			
		||||
        slotPacket.setContainerId(inventory.getId());
 | 
			
		||||
        slotPacket.setSlot(slot + 2);
 | 
			
		||||
        slotPacket.setItem(book);
 | 
			
		||||
        session.sendUpstreamPacket(slotPacket);
 | 
			
		||||
        data.setItem(book);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String toRomanNumeral(GeyserSession session, int level) {
 | 
			
		||||
        return LocaleUtils.getLocaleString("enchantment.level." + level,
 | 
			
		||||
                session.getLocale());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Stores the data of each slot in an enchantment table
 | 
			
		||||
     */
 | 
			
		||||
    @NoArgsConstructor
 | 
			
		||||
    @Getter
 | 
			
		||||
    @Setter
 | 
			
		||||
    @ToString
 | 
			
		||||
    public static class EnchantmentSlotData {
 | 
			
		||||
        private EnchantmentTableEnchantments enchantmentType = null;
 | 
			
		||||
        private int enchantmentLevel = 0;
 | 
			
		||||
        private int experienceRequired = 0;
 | 
			
		||||
        private ItemData item;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Classifies enchantments by Java order
 | 
			
		||||
     */
 | 
			
		||||
    public enum EnchantmentTableEnchantments {
 | 
			
		||||
        PROTECTION,
 | 
			
		||||
        FIRE_PROTECTION,
 | 
			
		||||
        FEATHER_FALLING,
 | 
			
		||||
        BLAST_PROTECTION,
 | 
			
		||||
        PROJECTILE_PROTECTION,
 | 
			
		||||
        RESPIRATION,
 | 
			
		||||
        AQUA_AFFINITY,
 | 
			
		||||
        THORNS,
 | 
			
		||||
        DEPTH_STRIDER,
 | 
			
		||||
        FROST_WALKER,
 | 
			
		||||
        BINDING_CURSE,
 | 
			
		||||
        SHARPNESS,
 | 
			
		||||
        SMITE,
 | 
			
		||||
        BANE_OF_ARTHROPODS,
 | 
			
		||||
        KNOCKBACK,
 | 
			
		||||
        FIRE_ASPECT,
 | 
			
		||||
        LOOTING,
 | 
			
		||||
        SWEEPING,
 | 
			
		||||
        EFFICIENCY,
 | 
			
		||||
        SILK_TOUCH,
 | 
			
		||||
        UNBREAKING,
 | 
			
		||||
        FORTUNE,
 | 
			
		||||
        POWER,
 | 
			
		||||
        PUNCH,
 | 
			
		||||
        FLAME,
 | 
			
		||||
        INFINITY,
 | 
			
		||||
        LUCK_OF_THE_SEA,
 | 
			
		||||
        LURE,
 | 
			
		||||
        LOYALTY,
 | 
			
		||||
        IMPALING,
 | 
			
		||||
        RIPTIDE,
 | 
			
		||||
        CHANNELING,
 | 
			
		||||
        MENDING,
 | 
			
		||||
        VANISHING_CURSE, // After this is not documented
 | 
			
		||||
        MULTISHOT,
 | 
			
		||||
        PIERCING,
 | 
			
		||||
        QUICK_CHARGE,
 | 
			
		||||
        SOUL_SPEED;
 | 
			
		||||
 | 
			
		||||
        public String toString(GeyserSession session) {
 | 
			
		||||
            return LocaleUtils.getLocaleString("enchantment.minecraft." + this.toString().toLowerCase(),
 | 
			
		||||
                    session.getLocale());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -25,52 +25,91 @@
 | 
			
		|||
 | 
			
		||||
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.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.data.game.window.WindowType;
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket;
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientPrepareCraftingGridPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.*;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.IntSet;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import org.geysermc.connector.inventory.CartographyContainer;
 | 
			
		||||
import org.geysermc.connector.inventory.GeyserItemStack;
 | 
			
		||||
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.updater.ContainerInventoryUpdater;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.click.Click;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.click.ClickPlan;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.translators.*;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.translators.chest.DoubleChestInventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.translators.chest.SingleChestInventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.translators.furnace.BlastFurnaceInventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.translators.furnace.FurnaceInventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.translators.furnace.SmokerInventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
 | 
			
		||||
import org.geysermc.connector.utils.InventoryUtils;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public abstract class InventoryTranslator {
 | 
			
		||||
 | 
			
		||||
    public static final InventoryTranslator PLAYER_INVENTORY_TRANSLATOR = new PlayerInventoryTranslator();
 | 
			
		||||
    public static final Map<WindowType, InventoryTranslator> INVENTORY_TRANSLATORS = new HashMap<WindowType, InventoryTranslator>() {
 | 
			
		||||
        {
 | 
			
		||||
            put(null, new PlayerInventoryTranslator()); //player inventory
 | 
			
		||||
            /* Player Inventory */
 | 
			
		||||
            put(null, PLAYER_INVENTORY_TRANSLATOR);
 | 
			
		||||
 | 
			
		||||
            /* Chest UIs */
 | 
			
		||||
            put(WindowType.GENERIC_9X1, new SingleChestInventoryTranslator(9));
 | 
			
		||||
            put(WindowType.GENERIC_9X2, new SingleChestInventoryTranslator(18));
 | 
			
		||||
            put(WindowType.GENERIC_9X3, new SingleChestInventoryTranslator(27));
 | 
			
		||||
            put(WindowType.GENERIC_9X4, new DoubleChestInventoryTranslator(36));
 | 
			
		||||
            put(WindowType.GENERIC_9X5, new DoubleChestInventoryTranslator(45));
 | 
			
		||||
            put(WindowType.GENERIC_9X6, new DoubleChestInventoryTranslator(54));
 | 
			
		||||
            put(WindowType.BREWING_STAND, new BrewingInventoryTranslator());
 | 
			
		||||
 | 
			
		||||
            /* Furnaces */
 | 
			
		||||
            put(WindowType.FURNACE, new FurnaceInventoryTranslator());
 | 
			
		||||
            put(WindowType.BLAST_FURNACE, new BlastFurnaceInventoryTranslator());
 | 
			
		||||
            put(WindowType.SMOKER, new SmokerInventoryTranslator());
 | 
			
		||||
 | 
			
		||||
            /* Specific Inventories */
 | 
			
		||||
            put(WindowType.ANVIL, new AnvilInventoryTranslator());
 | 
			
		||||
            put(WindowType.BEACON, new BeaconInventoryTranslator());
 | 
			
		||||
            put(WindowType.BREWING_STAND, new BrewingInventoryTranslator());
 | 
			
		||||
            put(WindowType.CARTOGRAPHY, new CartographyInventoryTranslator());
 | 
			
		||||
            put(WindowType.CRAFTING, new CraftingInventoryTranslator());
 | 
			
		||||
            //put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); //FIXME
 | 
			
		||||
            put(WindowType.ENCHANTMENT, new EnchantingInventoryTranslator());
 | 
			
		||||
            put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator());
 | 
			
		||||
            put(WindowType.LOOM, new LoomInventoryTranslator());
 | 
			
		||||
            put(WindowType.MERCHANT, new MerchantInventoryTranslator());
 | 
			
		||||
            //put(WindowType.SMITHING, new SmithingInventoryTranslator()); //TODO for server authoritative inventories
 | 
			
		||||
            put(WindowType.SHULKER_BOX, new ShulkerInventoryTranslator());
 | 
			
		||||
            put(WindowType.SMITHING, new SmithingInventoryTranslator());
 | 
			
		||||
            put(WindowType.STONECUTTER, new StonecutterInventoryTranslator());
 | 
			
		||||
 | 
			
		||||
            InventoryTranslator furnace = new FurnaceInventoryTranslator();
 | 
			
		||||
            put(WindowType.FURNACE, furnace);
 | 
			
		||||
            put(WindowType.BLAST_FURNACE, furnace);
 | 
			
		||||
            put(WindowType.SMOKER, furnace);
 | 
			
		||||
            /* Generics */
 | 
			
		||||
            put(WindowType.GENERIC_3X3, new GenericBlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER));
 | 
			
		||||
            put(WindowType.HOPPER, new GenericBlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER));
 | 
			
		||||
 | 
			
		||||
            InventoryUpdater containerUpdater = new ContainerInventoryUpdater();
 | 
			
		||||
            put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator(containerUpdater)); //TODO
 | 
			
		||||
            put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER, containerUpdater));
 | 
			
		||||
            put(WindowType.HOPPER, new BlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, containerUpdater));
 | 
			
		||||
            put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER, containerUpdater));
 | 
			
		||||
            //put(WindowType.BEACON, new BlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO
 | 
			
		||||
            /* Lectern */
 | 
			
		||||
            put(WindowType.LECTERN, new LecternInventoryTranslator());
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public static final int PLAYER_INVENTORY_SIZE = 36;
 | 
			
		||||
    public static final int PLAYER_INVENTORY_OFFSET = 9;
 | 
			
		||||
    private static final int MAX_ITEM_STACK_SIZE = 64;
 | 
			
		||||
    public final int size;
 | 
			
		||||
 | 
			
		||||
    public abstract void prepareInventory(GeyserSession session, Inventory inventory);
 | 
			
		||||
| 
						 | 
				
			
			@ -79,8 +118,790 @@ public abstract class InventoryTranslator {
 | 
			
		|||
    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 int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData);
 | 
			
		||||
    public abstract int javaSlotToBedrock(int javaSlot); //TODO
 | 
			
		||||
    public abstract BedrockContainerSlot javaSlotToBedrockContainer(int javaSlot); //TODO
 | 
			
		||||
    public abstract SlotType getSlotType(int javaSlot);
 | 
			
		||||
    public abstract void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions);
 | 
			
		||||
    public abstract Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Should be overwritten in cases where specific inventories should reject an item being in a specific spot.
 | 
			
		||||
     * For examples, looms use this to reject items that are dyes in Bedrock but not in Java.
 | 
			
		||||
     *
 | 
			
		||||
     * The source/destination slot will be -1 if the cursor is the slot
 | 
			
		||||
     *
 | 
			
		||||
     * @return true if this transfer should be rejected
 | 
			
		||||
     */
 | 
			
		||||
    public boolean shouldRejectItemPlace(GeyserSession session, Inventory inventory, int javaSourceSlot, int javaDestinationSlot) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Should be overrided if this request matches a certain criteria and shouldn't be treated normally.
 | 
			
		||||
     * E.G. anvil renaming or enchanting
 | 
			
		||||
     */
 | 
			
		||||
    public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * If {@link #shouldHandleRequestFirst(StackRequestActionData, Inventory)} returns true, this will be called
 | 
			
		||||
     */
 | 
			
		||||
    public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void translateRequests(GeyserSession session, Inventory inventory, List<ItemStackRequestPacket.Request> requests) {
 | 
			
		||||
        ItemStackResponsePacket responsePacket = new ItemStackResponsePacket();
 | 
			
		||||
        for (ItemStackRequestPacket.Request request : requests) {
 | 
			
		||||
            if (request.getActions().length > 0) {
 | 
			
		||||
                StackRequestActionData firstAction = request.getActions()[0];
 | 
			
		||||
                if (shouldHandleRequestFirst(firstAction, inventory)) {
 | 
			
		||||
                    // Some special request that shouldn't be processed normally
 | 
			
		||||
                    responsePacket.getEntries().add(translateSpecialRequest(session, inventory, request));
 | 
			
		||||
                } else if (firstAction.getType() == StackRequestActionType.CRAFT_RECIPE || firstAction.getType() == StackRequestActionType.CRAFT_RECIPE_AUTO) {
 | 
			
		||||
                    responsePacket.getEntries().add(translateCraftingRequest(session, inventory, request));
 | 
			
		||||
                } else if (firstAction.getType() == StackRequestActionType.CRAFT_CREATIVE) {
 | 
			
		||||
                    // This is also used for pulling items out of creative
 | 
			
		||||
                    responsePacket.getEntries().add(translateCreativeRequest(session, inventory, request));
 | 
			
		||||
                } else {
 | 
			
		||||
                    responsePacket.getEntries().add(translateRequest(session, inventory, request));
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                responsePacket.getEntries().add(rejectRequest(request));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        session.sendUpstreamPacket(responsePacket);
 | 
			
		||||
        System.out.println(responsePacket);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ItemStackResponsePacket.Response translateRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) {
 | 
			
		||||
        System.out.println(request);
 | 
			
		||||
        ClickPlan plan = new ClickPlan(session, this, inventory);
 | 
			
		||||
        IntSet affectedSlots = new IntOpenHashSet();
 | 
			
		||||
        for (StackRequestActionData action : request.getActions()) {
 | 
			
		||||
            GeyserItemStack cursor = session.getPlayerInventory().getCursor();
 | 
			
		||||
            switch (action.getType()) {
 | 
			
		||||
                case TAKE:
 | 
			
		||||
                case PLACE: {
 | 
			
		||||
                    TransferStackRequestActionData transferAction = (TransferStackRequestActionData) action;
 | 
			
		||||
                    if (!(checkNetId(session, inventory, transferAction.getSource()) && checkNetId(session, inventory, transferAction.getDestination()))) {
 | 
			
		||||
                        if (session.getGameMode().equals(GameMode.CREATIVE) && transferAction.getSource().getContainer() == ContainerSlotType.CRAFTING_INPUT &&
 | 
			
		||||
                                transferAction.getSource().getSlot() >= 28 && transferAction.getSource().getSlot() <= 31) {
 | 
			
		||||
                            return rejectRequest(request, false);
 | 
			
		||||
                        }
 | 
			
		||||
                        session.getConnector().getLogger().error("DEBUG: About to reject request.");
 | 
			
		||||
                        session.getConnector().getLogger().error("Source: " + transferAction.getSource().toString() + " Result: " + checkNetId(session, inventory, transferAction.getSource()));
 | 
			
		||||
                        session.getConnector().getLogger().error("Destination: " + transferAction.getDestination().toString() + " Result: " + checkNetId(session, inventory, transferAction.getDestination()));
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    int sourceSlot = bedrockSlotToJava(transferAction.getSource());
 | 
			
		||||
                    int destSlot = bedrockSlotToJava(transferAction.getDestination());
 | 
			
		||||
 | 
			
		||||
                    if (shouldRejectItemPlace(session, inventory, isCursor(transferAction.getSource()) ? -1 : sourceSlot,
 | 
			
		||||
                            isCursor(transferAction.getDestination()) ? -1 : destSlot)) {
 | 
			
		||||
                        // This item would not be here in Java
 | 
			
		||||
                        return rejectRequest(request, false);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (isCursor(transferAction.getSource()) && isCursor(transferAction.getDestination())) { //???
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    } else if (session.getGameMode().equals(GameMode.CREATIVE) && inventory instanceof PlayerInventory) { // TODO: does the Java server use this stuff all the time in creative?
 | 
			
		||||
                        // Creative acts a little differently because it just edits slots
 | 
			
		||||
                        boolean sourceIsCursor = isCursor(transferAction.getSource());
 | 
			
		||||
                        boolean destIsCursor = isCursor(transferAction.getDestination());
 | 
			
		||||
 | 
			
		||||
                        GeyserItemStack sourceItem = sourceIsCursor ? session.getPlayerInventory().getCursor() :
 | 
			
		||||
                                inventory.getItem(sourceSlot);
 | 
			
		||||
                        GeyserItemStack newItem = sourceItem.copy();
 | 
			
		||||
                        if (sourceIsCursor) {
 | 
			
		||||
                            GeyserItemStack destItem = inventory.getItem(destSlot);
 | 
			
		||||
                            if (destItem.getJavaId() == sourceItem.getJavaId()) {
 | 
			
		||||
                                // Combining items
 | 
			
		||||
                                int itemsLeftOver = destItem.getAmount() + transferAction.getCount();
 | 
			
		||||
                                if (itemsLeftOver > MAX_ITEM_STACK_SIZE) {
 | 
			
		||||
                                    // Items will remain in cursor because destination slot gets set to 64
 | 
			
		||||
                                    destItem.setAmount(MAX_ITEM_STACK_SIZE);
 | 
			
		||||
                                    sourceItem.setAmount(itemsLeftOver - MAX_ITEM_STACK_SIZE);
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    // Cursor will be emptied
 | 
			
		||||
                                    destItem.setAmount(itemsLeftOver);
 | 
			
		||||
                                    session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY, session);
 | 
			
		||||
                                }
 | 
			
		||||
                                ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket(
 | 
			
		||||
                                        destSlot,
 | 
			
		||||
                                        destItem.getItemStack()
 | 
			
		||||
                                );
 | 
			
		||||
                                session.sendDownstreamPacket(creativeActionPacket);
 | 
			
		||||
                                affectedSlots.add(destSlot);
 | 
			
		||||
                                break;
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            // Delete the source since we're moving it
 | 
			
		||||
                            inventory.setItem(sourceSlot, GeyserItemStack.EMPTY, session);
 | 
			
		||||
                            ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket(
 | 
			
		||||
                                    sourceSlot,
 | 
			
		||||
                                    new ItemStack(0)
 | 
			
		||||
                            );
 | 
			
		||||
                            session.sendDownstreamPacket(creativeActionPacket);
 | 
			
		||||
                            affectedSlots.add(sourceSlot);
 | 
			
		||||
                        }
 | 
			
		||||
                        // Update the item count with however much the client took
 | 
			
		||||
                        newItem.setAmount(transferAction.getCount());
 | 
			
		||||
                        // Remove that amount from the existing item
 | 
			
		||||
                        sourceItem.setAmount(sourceItem.getAmount() - transferAction.getCount());
 | 
			
		||||
                        if (sourceItem.isEmpty()) {
 | 
			
		||||
                            // Item is basically deleted
 | 
			
		||||
                            if (sourceIsCursor) {
 | 
			
		||||
                                session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY, session);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                inventory.setItem(sourceSlot, GeyserItemStack.EMPTY, session);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        if (destIsCursor) {
 | 
			
		||||
                            session.getPlayerInventory().setCursor(newItem, session);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            inventory.setItem(destSlot, newItem, session);
 | 
			
		||||
                        }
 | 
			
		||||
                        GeyserItemStack itemToUpdate = destIsCursor ? sourceItem : newItem;
 | 
			
		||||
                        // The Java server doesn't care about what's in the mouse in creative mode, so we just need to track
 | 
			
		||||
                        // which inventory slot the client modified
 | 
			
		||||
                        ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket(
 | 
			
		||||
                                destIsCursor ? sourceSlot : destSlot,
 | 
			
		||||
                                itemToUpdate.isEmpty() ? new ItemStack(0) : itemToUpdate.getItemStack()
 | 
			
		||||
                        );
 | 
			
		||||
                        session.sendDownstreamPacket(creativeActionPacket);
 | 
			
		||||
                        System.out.println(creativeActionPacket);
 | 
			
		||||
 | 
			
		||||
                        if (!sourceIsCursor) { // Cursor is always added for us as an affected slot
 | 
			
		||||
                            affectedSlots.add(sourceSlot);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (!destIsCursor) {
 | 
			
		||||
                            affectedSlots.add(destSlot);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    } else if (isCursor(transferAction.getSource())) { //releasing cursor
 | 
			
		||||
                        int sourceAmount = cursor.getAmount();
 | 
			
		||||
                        if (transferAction.getCount() == sourceAmount) { //release all
 | 
			
		||||
                            plan.add(Click.LEFT, destSlot);
 | 
			
		||||
                        } else { //release some
 | 
			
		||||
                            for (int i = 0; i < transferAction.getCount(); i++) {
 | 
			
		||||
                                plan.add(Click.RIGHT, destSlot);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (isCursor(transferAction.getDestination())) { //picking up into cursor
 | 
			
		||||
                        GeyserItemStack sourceItem = plan.getItem(sourceSlot);
 | 
			
		||||
                        int sourceAmount = sourceItem.getAmount();
 | 
			
		||||
                        if (cursor.isEmpty()) { //picking up into empty cursor
 | 
			
		||||
                            if (transferAction.getCount() == sourceAmount) { //pickup all
 | 
			
		||||
                                plan.add(Click.LEFT, sourceSlot);
 | 
			
		||||
                            } else if (transferAction.getCount() == sourceAmount - (sourceAmount / 2)) { //larger half; simple right click
 | 
			
		||||
                                plan.add(Click.RIGHT, sourceSlot);
 | 
			
		||||
                            } else { //pickup some; not a simple right click
 | 
			
		||||
                                plan.add(Click.LEFT, sourceSlot); //first pickup all
 | 
			
		||||
                                for (int i = 0; i < sourceAmount - transferAction.getCount(); i++) {
 | 
			
		||||
                                    plan.add(Click.RIGHT, sourceSlot); //release extra items back into source slot
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        } else { //pickup into non-empty cursor
 | 
			
		||||
                            if (!InventoryUtils.canStack(cursor, plan.getItem(sourceSlot))) { //doesn't make sense, reject
 | 
			
		||||
                                return rejectRequest(request);
 | 
			
		||||
                            }
 | 
			
		||||
                            if (transferAction.getCount() != sourceAmount) {
 | 
			
		||||
                                int tempSlot = findTempSlot(inventory, cursor, false, sourceSlot);
 | 
			
		||||
                                if (tempSlot == -1) {
 | 
			
		||||
                                    return rejectRequest(request);
 | 
			
		||||
                                }
 | 
			
		||||
                                plan.add(Click.LEFT, tempSlot); //place cursor into temp slot
 | 
			
		||||
                                plan.add(Click.LEFT, sourceSlot); //pickup source items into cursor
 | 
			
		||||
                                for (int i = 0; i < transferAction.getCount(); i++) {
 | 
			
		||||
                                    plan.add(Click.RIGHT, tempSlot); //partially transfer source items into temp slot (original cursor)
 | 
			
		||||
                                }
 | 
			
		||||
                                plan.add(Click.LEFT, sourceSlot); //return remaining source items
 | 
			
		||||
                                plan.add(Click.LEFT, tempSlot); //retrieve original cursor items from temp slot
 | 
			
		||||
                            } else {
 | 
			
		||||
                                if (getSlotType(sourceSlot).equals(SlotType.NORMAL)) {
 | 
			
		||||
                                    plan.add(Click.LEFT, sourceSlot); //release cursor onto source slot
 | 
			
		||||
                                }
 | 
			
		||||
                                plan.add(Click.LEFT, sourceSlot); //pickup combined cursor and source
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else { //transfer from one slot to another
 | 
			
		||||
                        int tempSlot = -1;
 | 
			
		||||
                        if (!cursor.isEmpty()) {
 | 
			
		||||
                            tempSlot = findTempSlot(inventory, cursor, false, sourceSlot, destSlot);
 | 
			
		||||
                            if (tempSlot == -1) {
 | 
			
		||||
                                return rejectRequest(request);
 | 
			
		||||
                            }
 | 
			
		||||
                            plan.add(Click.LEFT, tempSlot); //place cursor into temp slot
 | 
			
		||||
                        }
 | 
			
		||||
                        int sourceAmount = plan.getItem(sourceSlot).getAmount();
 | 
			
		||||
                        if (transferAction.getCount() == sourceAmount) { //transfer all
 | 
			
		||||
                            plan.add(Click.LEFT, sourceSlot); //pickup source
 | 
			
		||||
                            plan.add(Click.LEFT, destSlot); //let go of all items and done
 | 
			
		||||
                        } else { //transfer some
 | 
			
		||||
                            //try to transfer items with least clicks possible
 | 
			
		||||
                            int halfSource = sourceAmount - (sourceAmount / 2); //larger half
 | 
			
		||||
                            int holding;
 | 
			
		||||
                            if (plan.getCursor().isEmpty() && transferAction.getCount() <= halfSource) { //faster to take only half. CURSOR MUST BE EMPTY
 | 
			
		||||
                                plan.add(Click.RIGHT, sourceSlot);
 | 
			
		||||
                                holding = halfSource;
 | 
			
		||||
                            } else { //need all
 | 
			
		||||
                                plan.add(Click.LEFT, sourceSlot);
 | 
			
		||||
                                holding = sourceAmount;
 | 
			
		||||
                            }
 | 
			
		||||
                            if (transferAction.getCount() > holding / 2) { //faster to release extra items onto source or dest slot?
 | 
			
		||||
                                for (int i = 0; i < holding - transferAction.getCount(); i++) {
 | 
			
		||||
                                    plan.add(Click.RIGHT, sourceSlot); //prepare cursor
 | 
			
		||||
                                }
 | 
			
		||||
                                plan.add(Click.LEFT, destSlot); //release cursor onto dest slot
 | 
			
		||||
                            } else {
 | 
			
		||||
                                for (int i = 0; i < transferAction.getCount(); i++) {
 | 
			
		||||
                                    plan.add(Click.RIGHT, destSlot); //right click until transfer goal is met
 | 
			
		||||
                                }
 | 
			
		||||
                                plan.add(Click.LEFT, sourceSlot); //return extra items to source slot
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        if (tempSlot != -1) {
 | 
			
		||||
                            plan.add(Click.LEFT, tempSlot); //retrieve original cursor
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case SWAP: { //TODO
 | 
			
		||||
                    SwapStackRequestActionData swapAction = (SwapStackRequestActionData) action;
 | 
			
		||||
                    if (!(checkNetId(session, inventory, swapAction.getSource()) && checkNetId(session, inventory, swapAction.getDestination())))
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
 | 
			
		||||
                    if (session.getGameMode().equals(GameMode.CREATIVE) && inventory instanceof PlayerInventory) {
 | 
			
		||||
                        int destSlot = bedrockSlotToJava(swapAction.getDestination());
 | 
			
		||||
                        GeyserItemStack oldSourceItem;
 | 
			
		||||
                        GeyserItemStack oldDestinationItem = inventory.getItem(destSlot);
 | 
			
		||||
                        if (isCursor(swapAction.getSource())) {
 | 
			
		||||
                            oldSourceItem = session.getPlayerInventory().getCursor();
 | 
			
		||||
                            session.getPlayerInventory().setCursor(oldDestinationItem, session);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            int sourceSlot = bedrockSlotToJava(swapAction.getSource());
 | 
			
		||||
                            oldSourceItem = inventory.getItem(sourceSlot);
 | 
			
		||||
                            ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket(
 | 
			
		||||
                                    sourceSlot,
 | 
			
		||||
                                    oldDestinationItem.isEmpty() ? new ItemStack(0) : oldDestinationItem.getItemStack() // isEmpty check... just in case
 | 
			
		||||
                            );
 | 
			
		||||
                            System.out.println(creativeActionPacket);
 | 
			
		||||
                            session.sendDownstreamPacket(creativeActionPacket);
 | 
			
		||||
                            inventory.setItem(sourceSlot, oldDestinationItem, session);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (isCursor(swapAction.getDestination())) {
 | 
			
		||||
                            session.getPlayerInventory().setCursor(oldSourceItem, session);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket(
 | 
			
		||||
                                    destSlot,
 | 
			
		||||
                                    oldSourceItem.isEmpty() ? new ItemStack(0) : oldSourceItem.getItemStack()
 | 
			
		||||
                            );
 | 
			
		||||
                            System.out.println(creativeActionPacket);
 | 
			
		||||
                            session.sendDownstreamPacket(creativeActionPacket);
 | 
			
		||||
                            inventory.setItem(destSlot, oldSourceItem, session);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    } else if (isCursor(swapAction.getSource()) && isCursor(swapAction.getDestination())) { //???
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    } else if (isCursor(swapAction.getSource())) { //swap cursor
 | 
			
		||||
                        int destSlot = bedrockSlotToJava(swapAction.getDestination());
 | 
			
		||||
                        if (InventoryUtils.canStack(cursor, plan.getItem(destSlot))) { //TODO: cannot simply swap if cursor stacks with slot (temp slot)
 | 
			
		||||
                            return rejectRequest(request);
 | 
			
		||||
                        }
 | 
			
		||||
                        plan.add(Click.LEFT, destSlot);
 | 
			
		||||
                    } else if (isCursor(swapAction.getDestination())) { //swap cursor
 | 
			
		||||
                        int sourceSlot = bedrockSlotToJava(swapAction.getSource());
 | 
			
		||||
                        if (InventoryUtils.canStack(cursor, plan.getItem(sourceSlot))) { //TODO
 | 
			
		||||
                            return rejectRequest(request);
 | 
			
		||||
                        }
 | 
			
		||||
                        plan.add(Click.LEFT, sourceSlot);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        int sourceSlot = bedrockSlotToJava(swapAction.getSource());
 | 
			
		||||
                        int destSlot = bedrockSlotToJava(swapAction.getDestination());
 | 
			
		||||
                        if (!cursor.isEmpty()) { //TODO: (temp slot)
 | 
			
		||||
                            return rejectRequest(request);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (sourceSlot == destSlot) { //doesn't make sense
 | 
			
		||||
                            return rejectRequest(request);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (InventoryUtils.canStack(plan.getItem(sourceSlot), plan.getItem(destSlot))) { //TODO: (temp slot)
 | 
			
		||||
                            return rejectRequest(request);
 | 
			
		||||
                        }
 | 
			
		||||
                        plan.add(Click.LEFT, sourceSlot); //pickup source into cursor
 | 
			
		||||
                        plan.add(Click.LEFT, destSlot); //swap cursor with dest slot
 | 
			
		||||
                        plan.add(Click.LEFT, sourceSlot); //release cursor onto source
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case DROP: {
 | 
			
		||||
                    DropStackRequestActionData dropAction = (DropStackRequestActionData) action;
 | 
			
		||||
                    if (!checkNetId(session, inventory, dropAction.getSource()))
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
 | 
			
		||||
                    if (isCursor(dropAction.getSource())) { //clicking outside of window
 | 
			
		||||
                        if (session.getGameMode() == GameMode.CREATIVE && inventory instanceof PlayerInventory) {
 | 
			
		||||
                            GeyserItemStack cursorItem = session.getPlayerInventory().getCursor();
 | 
			
		||||
                            GeyserItemStack droppingItem = cursorItem.copy();
 | 
			
		||||
                            // Subtract the cursor item by however much is being dropped
 | 
			
		||||
                            cursorItem.setAmount(cursorItem.getAmount() - dropAction.getCount());
 | 
			
		||||
                            if (cursorItem.isEmpty()) {
 | 
			
		||||
                                // Cursor item no longer exists
 | 
			
		||||
                                session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY, session);
 | 
			
		||||
                            }
 | 
			
		||||
                            droppingItem.setAmount(dropAction.getCount());
 | 
			
		||||
                            ClientCreativeInventoryActionPacket packet = new ClientCreativeInventoryActionPacket(
 | 
			
		||||
                                    Click.OUTSIDE_SLOT,
 | 
			
		||||
                                    droppingItem.getItemStack()
 | 
			
		||||
                            );
 | 
			
		||||
                            System.out.println(packet.toString());
 | 
			
		||||
                            session.sendDownstreamPacket(packet);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            int sourceAmount = plan.getCursor().getAmount();
 | 
			
		||||
                            if (dropAction.getCount() == sourceAmount) { //drop all
 | 
			
		||||
                                plan.add(Click.LEFT_OUTSIDE, Click.OUTSIDE_SLOT);
 | 
			
		||||
                            } else { //drop some
 | 
			
		||||
                                for (int i = 0; i < dropAction.getCount(); i++) {
 | 
			
		||||
                                    plan.add(Click.RIGHT_OUTSIDE, Click.OUTSIDE_SLOT); //drop one until goal is met
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else { //dropping from inventory
 | 
			
		||||
                        int sourceSlot = bedrockSlotToJava(dropAction.getSource());
 | 
			
		||||
                        int sourceAmount = plan.getItem(sourceSlot).getAmount();
 | 
			
		||||
                        if (dropAction.getCount() == sourceAmount && sourceAmount > 1) { //dropping all? (prefer DROP_ONE if only one)
 | 
			
		||||
                            plan.add(Click.DROP_ALL, sourceSlot);
 | 
			
		||||
                        } else { //drop some
 | 
			
		||||
                            for (int i = 0; i < dropAction.getCount(); i++) {
 | 
			
		||||
                                plan.add(Click.DROP_ONE, sourceSlot); //drop one until goal is met
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case CRAFT_CREATIVE: {
 | 
			
		||||
                    CraftCreativeStackRequestActionData creativeAction = (CraftCreativeStackRequestActionData) action;
 | 
			
		||||
                    System.out.println(creativeAction.getCreativeItemNetworkId());
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case DESTROY: {
 | 
			
		||||
                    // Only called when a creative client wants to destroy an item... I think - Camotoy
 | 
			
		||||
                    //TODO there is a Count here we don't use
 | 
			
		||||
                    DestroyStackRequestActionData destroyAction = (DestroyStackRequestActionData) action;
 | 
			
		||||
                    if (!session.getGameMode().equals(GameMode.CREATIVE)) {
 | 
			
		||||
                        // If this happens, let's throw an error and figure out why.
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (!isCursor(destroyAction.getSource())) {
 | 
			
		||||
                        // Item exists; let's remove it from the inventory
 | 
			
		||||
                        int javaSlot = bedrockSlotToJava(destroyAction.getSource());
 | 
			
		||||
                        ClientCreativeInventoryActionPacket destroyItemPacket = new ClientCreativeInventoryActionPacket(
 | 
			
		||||
                                javaSlot,
 | 
			
		||||
                                new ItemStack(0)
 | 
			
		||||
                        );
 | 
			
		||||
                        session.sendDownstreamPacket(destroyItemPacket);
 | 
			
		||||
                        System.out.println(destroyItemPacket);
 | 
			
		||||
                        inventory.setItem(javaSlot, GeyserItemStack.EMPTY, session);
 | 
			
		||||
                        affectedSlots.add(javaSlot);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // Just sync up the item on our end, since the server doesn't care what's in our cursor
 | 
			
		||||
                        session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY, session);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                // The following three tend to be called for UI inventories
 | 
			
		||||
                case CONSUME: {
 | 
			
		||||
                    if (inventory instanceof CartographyContainer) {
 | 
			
		||||
                        // TODO add this for more inventories? Only seems to glitch out the cartography table, though.
 | 
			
		||||
                        ConsumeStackRequestActionData consumeData = (ConsumeStackRequestActionData) action;
 | 
			
		||||
                        int sourceSlot = bedrockSlotToJava(consumeData.getSource());
 | 
			
		||||
                        if (sourceSlot == 0 && inventory.getItem(1).isEmpty()) {
 | 
			
		||||
                            // Java doesn't allow an item to be renamed; this is why CARTOGRAPHY_ADDITIONAL could remain empty for Bedrock
 | 
			
		||||
                            // We check this during slot 0 since setting the inventory slots here messes up shouldRejectItemPlace
 | 
			
		||||
                            return rejectRequest(request, false);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        GeyserItemStack item = inventory.getItem(sourceSlot);
 | 
			
		||||
                        item.setAmount(item.getAmount() - consumeData.getCount());
 | 
			
		||||
                        if (item.isEmpty()) {
 | 
			
		||||
                            inventory.setItem(sourceSlot, GeyserItemStack.EMPTY, session);
 | 
			
		||||
                        }
 | 
			
		||||
                        affectedSlots.add(sourceSlot);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case CRAFT_NON_IMPLEMENTED_DEPRECATED: {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case CRAFT_RESULTS_DEPRECATED: {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case CRAFT_RECIPE_OPTIONAL: {
 | 
			
		||||
                    // Anvils and cartography tables will handle this
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                default:
 | 
			
		||||
                    return rejectRequest(request);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        plan.execute(false);
 | 
			
		||||
        affectedSlots.addAll(plan.getAffectedSlots());
 | 
			
		||||
        return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public ItemStackResponsePacket.Response translateCraftingRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) {
 | 
			
		||||
        System.out.println(request);
 | 
			
		||||
 | 
			
		||||
        int recipeId = 0;
 | 
			
		||||
        int resultSize = 0;
 | 
			
		||||
        int timesCrafted = 0;
 | 
			
		||||
        boolean autoCraft = false;
 | 
			
		||||
        CraftState craftState = CraftState.START;
 | 
			
		||||
 | 
			
		||||
        int leftover = 0;
 | 
			
		||||
        ClickPlan plan = new ClickPlan(session, this, inventory);
 | 
			
		||||
        for (StackRequestActionData action : request.getActions()) {
 | 
			
		||||
            switch (action.getType()) {
 | 
			
		||||
                case CRAFT_RECIPE: {
 | 
			
		||||
                    CraftRecipeStackRequestActionData craftAction = (CraftRecipeStackRequestActionData) action;
 | 
			
		||||
                    if (craftState != CraftState.START) {
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
                    craftState = CraftState.RECIPE_ID;
 | 
			
		||||
                    recipeId = craftAction.getRecipeNetworkId();
 | 
			
		||||
                    autoCraft = false;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case CRAFT_RECIPE_AUTO: {
 | 
			
		||||
                    AutoCraftRecipeStackRequestActionData autoCraftAction = (AutoCraftRecipeStackRequestActionData) action;
 | 
			
		||||
                    if (craftState != CraftState.START) {
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
                    craftState = CraftState.RECIPE_ID;
 | 
			
		||||
 | 
			
		||||
                    recipeId = autoCraftAction.getRecipeNetworkId();
 | 
			
		||||
                    if (!plan.getCursor().isEmpty()) {
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
                    //reject if crafting grid is not clear
 | 
			
		||||
                    int gridSize = inventory.getId() == 0 ? 4 : 9;
 | 
			
		||||
                    for (int i = 1; i <= gridSize; i++) {
 | 
			
		||||
                        if (!inventory.getItem(i).isEmpty()) {
 | 
			
		||||
                            return rejectRequest(request);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    autoCraft = true;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case CRAFT_RESULTS_DEPRECATED: {
 | 
			
		||||
                    CraftResultsDeprecatedStackRequestActionData deprecatedCraftAction = (CraftResultsDeprecatedStackRequestActionData) action;
 | 
			
		||||
                    if (craftState != CraftState.RECIPE_ID) {
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
                    craftState = CraftState.DEPRECATED;
 | 
			
		||||
 | 
			
		||||
                    if (deprecatedCraftAction.getResultItems().length != 1) {
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
                    resultSize = deprecatedCraftAction.getResultItems()[0].getCount();
 | 
			
		||||
                    timesCrafted = deprecatedCraftAction.getTimesCrafted();
 | 
			
		||||
                    if (resultSize <= 0 || timesCrafted <= 0) {
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case CONSUME: {
 | 
			
		||||
                    ConsumeStackRequestActionData consumeAction = (ConsumeStackRequestActionData) action;
 | 
			
		||||
                    if (craftState != CraftState.DEPRECATED && craftState != CraftState.INGREDIENTS) {
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
                    craftState = CraftState.INGREDIENTS;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case TAKE:
 | 
			
		||||
                case PLACE: {
 | 
			
		||||
                    TransferStackRequestActionData transferAction = (TransferStackRequestActionData) action;
 | 
			
		||||
                    if (craftState != CraftState.INGREDIENTS && craftState != CraftState.TRANSFER) {
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
                    craftState = CraftState.TRANSFER;
 | 
			
		||||
 | 
			
		||||
                    if (transferAction.getSource().getContainer() != ContainerSlotType.CREATIVE_OUTPUT) {
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (transferAction.getCount() <= 0) {
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    int sourceSlot = bedrockSlotToJava(transferAction.getSource());
 | 
			
		||||
                    int destSlot = bedrockSlotToJava(transferAction.getDestination());
 | 
			
		||||
 | 
			
		||||
                    if (autoCraft) {
 | 
			
		||||
                        Recipe recipe = session.getCraftingRecipes().get(recipeId);
 | 
			
		||||
                        //cannot use java recipe book if recipe is locked
 | 
			
		||||
                        if (recipe == null || !session.getUnlockedRecipes().contains(recipe.getIdentifier())) {
 | 
			
		||||
                            return rejectRequest(request);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        boolean cursorDest = isCursor(transferAction.getDestination());
 | 
			
		||||
                        boolean makeAll = timesCrafted > 1;
 | 
			
		||||
                        if (cursorDest) {
 | 
			
		||||
                            makeAll = false;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        ClientPrepareCraftingGridPacket prepareCraftingPacket = new ClientPrepareCraftingGridPacket(inventory.getId(), recipe.getIdentifier(), makeAll);
 | 
			
		||||
                        session.sendDownstreamPacket(prepareCraftingPacket);
 | 
			
		||||
 | 
			
		||||
                        ItemStack output = null;
 | 
			
		||||
                        switch (recipe.getType()) {
 | 
			
		||||
                            case CRAFTING_SHAPED:
 | 
			
		||||
                                output = ((ShapedRecipeData)recipe.getData()).getResult();
 | 
			
		||||
                                break;
 | 
			
		||||
                            case CRAFTING_SHAPELESS:
 | 
			
		||||
                                output = ((ShapelessRecipeData)recipe.getData()).getResult();
 | 
			
		||||
                                break;
 | 
			
		||||
                        }
 | 
			
		||||
                        inventory.setItem(0, GeyserItemStack.from(output), session);
 | 
			
		||||
 | 
			
		||||
                        plan.add(cursorDest ? Click.LEFT : Click.LEFT_SHIFT, 0);
 | 
			
		||||
                        plan.execute(true);
 | 
			
		||||
 | 
			
		||||
                        return acceptRequest(request, makeContainerEntries(session, inventory, Collections.emptySet()));
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (isCursor(transferAction.getDestination())) {
 | 
			
		||||
                        plan.add(Click.LEFT, sourceSlot);
 | 
			
		||||
                        craftState = CraftState.DONE;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        if (leftover != 0) {
 | 
			
		||||
                            if (transferAction.getCount() > leftover) {
 | 
			
		||||
                                return rejectRequest(request);
 | 
			
		||||
                            }
 | 
			
		||||
                            if (transferAction.getCount() == leftover) {
 | 
			
		||||
                                plan.add(Click.LEFT, destSlot);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                for (int i = 0; i < transferAction.getCount(); i++) {
 | 
			
		||||
                                    plan.add(Click.RIGHT, destSlot);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            leftover -= transferAction.getCount();
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        int remainder = transferAction.getCount() % resultSize;
 | 
			
		||||
                        int timesToCraft = transferAction.getCount() / resultSize;
 | 
			
		||||
                        for (int i = 0; i < timesToCraft; i++) {
 | 
			
		||||
                            plan.add(Click.LEFT, sourceSlot);
 | 
			
		||||
                            plan.add(Click.LEFT, destSlot);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (remainder > 0) {
 | 
			
		||||
                            plan.add(Click.LEFT, 0);
 | 
			
		||||
                            for (int i = 0; i < remainder; i++) {
 | 
			
		||||
                                plan.add(Click.RIGHT, destSlot);
 | 
			
		||||
                            }
 | 
			
		||||
                            leftover = resultSize - remainder;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                default:
 | 
			
		||||
                    return rejectRequest(request);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        plan.execute(false);
 | 
			
		||||
        Set<Integer> affectedSlots = plan.getAffectedSlots();
 | 
			
		||||
        affectedSlots.addAll(Arrays.asList(1, 2, 3, 4)); //TODO: crafting grid
 | 
			
		||||
        return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ItemStackResponsePacket.Response translateCreativeRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) {
 | 
			
		||||
        int creativeId = 0;
 | 
			
		||||
        CraftState craftState = CraftState.START;
 | 
			
		||||
        for (StackRequestActionData action : request.getActions()) {
 | 
			
		||||
            switch (action.getType()) {
 | 
			
		||||
                case CRAFT_CREATIVE: {
 | 
			
		||||
                    CraftCreativeStackRequestActionData creativeAction = (CraftCreativeStackRequestActionData) action;
 | 
			
		||||
                    if (craftState != CraftState.START) {
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
                    craftState = CraftState.RECIPE_ID;
 | 
			
		||||
 | 
			
		||||
                    creativeId = creativeAction.getCreativeItemNetworkId();
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case CRAFT_RESULTS_DEPRECATED: {
 | 
			
		||||
                    CraftResultsDeprecatedStackRequestActionData deprecatedCraftAction = (CraftResultsDeprecatedStackRequestActionData) action;
 | 
			
		||||
                    if (craftState != CraftState.RECIPE_ID) {
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
                    craftState = CraftState.DEPRECATED;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case TAKE:
 | 
			
		||||
                case PLACE: {
 | 
			
		||||
                    TransferStackRequestActionData transferAction = (TransferStackRequestActionData) action;
 | 
			
		||||
                    if (craftState != CraftState.DEPRECATED) {
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
                    craftState = CraftState.TRANSFER;
 | 
			
		||||
 | 
			
		||||
                    if (transferAction.getSource().getContainer() != ContainerSlotType.CREATIVE_OUTPUT) {
 | 
			
		||||
                        return rejectRequest(request);
 | 
			
		||||
                    }
 | 
			
		||||
                    // Reference the creative items list we send to the client to know what it's asking of us
 | 
			
		||||
                    ItemData creativeItem = ItemRegistry.CREATIVE_ITEMS[creativeId - 1];
 | 
			
		||||
                    // Get the correct count
 | 
			
		||||
                    creativeItem = ItemData.of(creativeItem.getId(), creativeItem.getDamage(), transferAction.getCount(),  creativeItem.getTag());
 | 
			
		||||
                    ItemStack javaCreativeItem = ItemTranslator.translateToJava(creativeItem);
 | 
			
		||||
 | 
			
		||||
                    if (isCursor(transferAction.getDestination())) {
 | 
			
		||||
                        session.getPlayerInventory().setCursor(GeyserItemStack.from(javaCreativeItem), session);
 | 
			
		||||
                        return acceptRequest(request, Collections.singletonList(
 | 
			
		||||
                                new ItemStackResponsePacket.ContainerEntry(ContainerSlotType.CURSOR,
 | 
			
		||||
                                        Collections.singletonList(makeItemEntry(0, session.getPlayerInventory().getCursor())))));
 | 
			
		||||
                    } else {
 | 
			
		||||
                        int javaSlot = bedrockSlotToJava(transferAction.getDestination());
 | 
			
		||||
                        GeyserItemStack existingItem = inventory.getItem(javaSlot);
 | 
			
		||||
                        if (existingItem.getJavaId() == javaCreativeItem.getId()) {
 | 
			
		||||
                            // Adding more to an existing item
 | 
			
		||||
                            existingItem.setAmount(existingItem.getAmount() + transferAction.getCount());
 | 
			
		||||
                            javaCreativeItem = existingItem.getItemStack();
 | 
			
		||||
                        } else {
 | 
			
		||||
                            inventory.setItem(javaSlot, GeyserItemStack.from(javaCreativeItem), session);
 | 
			
		||||
                        }
 | 
			
		||||
                        ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket(
 | 
			
		||||
                                javaSlot,
 | 
			
		||||
                                javaCreativeItem
 | 
			
		||||
                        );
 | 
			
		||||
                        session.sendDownstreamPacket(creativeActionPacket);
 | 
			
		||||
                        System.out.println(creativeActionPacket);
 | 
			
		||||
                        Set<Integer> affectedSlots = Collections.singleton(javaSlot);
 | 
			
		||||
                        return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                default:
 | 
			
		||||
                    return rejectRequest(request);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return rejectRequest(request);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ItemStackResponsePacket.Response acceptRequest(ItemStackRequestPacket.Request request, List<ItemStackResponsePacket.ContainerEntry> containerEntries) {
 | 
			
		||||
        return new ItemStackResponsePacket.Response(ItemStackResponsePacket.ResponseStatus.OK, request.getRequestId(), containerEntries);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ItemStackResponsePacket.Response rejectRequest(ItemStackRequestPacket.Request request) {
 | 
			
		||||
        return rejectRequest(request, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ItemStackResponsePacket.Response rejectRequest(ItemStackRequestPacket.Request request, boolean throwError) {
 | 
			
		||||
        if (throwError) {
 | 
			
		||||
            // Currently for debugging, but might be worth it to keep in the future if something goes terribly wrong.
 | 
			
		||||
            new Throwable("DEBUGGING: ItemStackRequest rejected").printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
        return new ItemStackResponsePacket.Response(ItemStackResponsePacket.ResponseStatus.ERROR, request.getRequestId(), Collections.emptyList());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean checkNetId(GeyserSession session, Inventory inventory, StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        if (slotInfoData.getStackNetworkId() < 0)
 | 
			
		||||
            return true;
 | 
			
		||||
//        if (slotInfoData.getContainer() == ContainerSlotType.CURSOR) //TODO: temporary
 | 
			
		||||
//            return true;
 | 
			
		||||
 | 
			
		||||
        GeyserItemStack currentItem = isCursor(slotInfoData) ? session.getPlayerInventory().getCursor() : inventory.getItem(bedrockSlotToJava(slotInfoData));
 | 
			
		||||
        return currentItem.getNetId() == slotInfoData.getStackNetworkId();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Try to find a slot that can temporarily store the given item.
 | 
			
		||||
     * Only looks in the main inventory and hotbar (excluding offhand).
 | 
			
		||||
     * Only slots that are empty or contain a different type of item are valid.
 | 
			
		||||
     *
 | 
			
		||||
     * @return java id for the temporary slot, or -1 if no viable slot was found
 | 
			
		||||
     */
 | 
			
		||||
    //TODO: compatibility for simulated inventory (ClickPlan)
 | 
			
		||||
    private static int findTempSlot(Inventory inventory, GeyserItemStack item, boolean emptyOnly, int... slotBlacklist) {
 | 
			
		||||
        int offset = inventory.getId() == 0 ? 1 : 0; //offhand is not a viable temp slot
 | 
			
		||||
        HashSet<GeyserItemStack> itemBlacklist = new HashSet<>(slotBlacklist.length + 1);
 | 
			
		||||
        itemBlacklist.add(item);
 | 
			
		||||
 | 
			
		||||
        IntSet potentialSlots = new IntOpenHashSet(36);
 | 
			
		||||
        for (int i = inventory.getSize() - (36 + offset); i < inventory.getSize() - offset; i++) {
 | 
			
		||||
            potentialSlots.add(i);
 | 
			
		||||
        }
 | 
			
		||||
        for (int i : slotBlacklist) {
 | 
			
		||||
            potentialSlots.remove(i);
 | 
			
		||||
            GeyserItemStack blacklistedItem = inventory.getItem(i);
 | 
			
		||||
            if (!blacklistedItem.isEmpty()) {
 | 
			
		||||
                itemBlacklist.add(blacklistedItem);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (int i : potentialSlots) {
 | 
			
		||||
            GeyserItemStack testItem = inventory.getItem(i);
 | 
			
		||||
            if ((emptyOnly && !testItem.isEmpty())) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            boolean viable = true;
 | 
			
		||||
            for (GeyserItemStack blacklistedItem : itemBlacklist) {
 | 
			
		||||
                if (InventoryUtils.canStack(testItem, blacklistedItem)) {
 | 
			
		||||
                    viable = false;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (!viable) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            System.out.println("TEMP SLOT CHOSEN: " + i + " => " + inventory.getItem(i));
 | 
			
		||||
            return i;
 | 
			
		||||
        }
 | 
			
		||||
        //could not find a viable temp slot
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public List<ItemStackResponsePacket.ContainerEntry> makeContainerEntries(GeyserSession session, Inventory inventory, Set<Integer> affectedSlots) {
 | 
			
		||||
        Map<ContainerSlotType, List<ItemStackResponsePacket.ItemEntry>> containerMap = new HashMap<>();
 | 
			
		||||
        for (int slot : affectedSlots) {
 | 
			
		||||
            BedrockContainerSlot bedrockSlot = javaSlotToBedrockContainer(slot);
 | 
			
		||||
            List<ItemStackResponsePacket.ItemEntry> list = containerMap.computeIfAbsent(bedrockSlot.getContainer(), k -> new ArrayList<>());
 | 
			
		||||
            list.add(makeItemEntry(bedrockSlot.getSlot(), inventory.getItem(slot)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        List<ItemStackResponsePacket.ContainerEntry> containerEntries = new ArrayList<>();
 | 
			
		||||
        for (Map.Entry<ContainerSlotType, List<ItemStackResponsePacket.ItemEntry>> entry : containerMap.entrySet()) {
 | 
			
		||||
            containerEntries.add(new ItemStackResponsePacket.ContainerEntry(entry.getKey(), entry.getValue()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ItemStackResponsePacket.ItemEntry cursorEntry = makeItemEntry(0, session.getPlayerInventory().getCursor());
 | 
			
		||||
        containerEntries.add(new ItemStackResponsePacket.ContainerEntry(ContainerSlotType.CURSOR, Collections.singletonList(cursorEntry)));
 | 
			
		||||
 | 
			
		||||
        return containerEntries;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ItemStackResponsePacket.ItemEntry makeItemEntry(int bedrockSlot, GeyserItemStack itemStack) {
 | 
			
		||||
        ItemStackResponsePacket.ItemEntry itemEntry;
 | 
			
		||||
        if (!itemStack.isEmpty()) {
 | 
			
		||||
            itemEntry = new ItemStackResponsePacket.ItemEntry((byte) bedrockSlot, (byte) bedrockSlot, (byte) itemStack.getAmount(), itemStack.getNetId(), "");
 | 
			
		||||
        } else {
 | 
			
		||||
            itemEntry = new ItemStackResponsePacket.ItemEntry((byte) bedrockSlot, (byte) bedrockSlot, (byte) 0, 0, "");
 | 
			
		||||
        }
 | 
			
		||||
        return itemEntry;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static boolean isCursor(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        return slotInfoData.getContainer() == ContainerSlotType.CURSOR;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private enum CraftState {
 | 
			
		||||
        START,
 | 
			
		||||
        RECIPE_ID,
 | 
			
		||||
        DEPRECATED,
 | 
			
		||||
        INGREDIENTS,
 | 
			
		||||
        TRANSFER,
 | 
			
		||||
        DONE
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,125 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.sendDownstreamPacket(clickPacket);
 | 
			
		||||
            session.sendDownstreamPacket(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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,338 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.inventory.ContainerId;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventorySource;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
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.network.translators.item.ItemTranslator;
 | 
			
		||||
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) {
 | 
			
		||||
                return;
 | 
			
		||||
            } else if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION) {
 | 
			
		||||
                worldAction = action;
 | 
			
		||||
            } else if (action.getSource().getContainerId() == ContainerId.UI && action.getSlot() == 0) {
 | 
			
		||||
                cursorAction = action;
 | 
			
		||||
                ItemData translatedCursor = ItemTranslator.translateToBedrock(session, session.getInventory().getCursor());
 | 
			
		||||
                if (!translatedCursor.equals(action.getFromItem())) {
 | 
			
		||||
                    refresh = true;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                containerAction = action;
 | 
			
		||||
                ItemData translatedItem = ItemTranslator.translateToBedrock(session, 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.sendDownstreamPacket(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.sendDownstreamPacket(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.sendDownstreamPacket(dropPacket);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        ItemStack item = inventory.getItem(javaSlot);
 | 
			
		||||
                        if (item != null) {
 | 
			
		||||
                            inventory.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.sendDownstreamPacket(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.sendDownstreamPacket(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), false);
 | 
			
		||||
                        if (cursorSlot != -1) {
 | 
			
		||||
                            plan.add(Click.LEFT, cursorSlot);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            translator.updateInventory(session, inventory);
 | 
			
		||||
                            InventoryUtils.updateCursor(session);
 | 
			
		||||
                            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),
 | 
			
		||||
                        translator.getSlotType(fromSlot) == SlotType.OUTPUT);
 | 
			
		||||
                if (cursorSlot != -1) {
 | 
			
		||||
                    plan.add(Click.LEFT, cursorSlot);
 | 
			
		||||
                } else {
 | 
			
		||||
                    translator.updateInventory(session, inventory);
 | 
			
		||||
                    InventoryUtils.updateCursor(session);
 | 
			
		||||
                    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.sendDownstreamPacket(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, boolean emptyOnly) {
 | 
			
		||||
        /*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) {
 | 
			
		||||
                if (emptyOnly) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.click;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.*;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public enum Click {
 | 
			
		||||
    LEFT(WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK),
 | 
			
		||||
    RIGHT(WindowAction.CLICK_ITEM, ClickItemParam.RIGHT_CLICK),
 | 
			
		||||
    LEFT_SHIFT(WindowAction.SHIFT_CLICK_ITEM, ShiftClickItemParam.LEFT_CLICK),
 | 
			
		||||
    DROP_ONE(WindowAction.DROP_ITEM, DropItemParam.DROP_FROM_SELECTED),
 | 
			
		||||
    DROP_ALL(WindowAction.DROP_ITEM, DropItemParam.DROP_SELECTED_STACK),
 | 
			
		||||
    LEFT_OUTSIDE(WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK),
 | 
			
		||||
    RIGHT_OUTSIDE(WindowAction.CLICK_ITEM, ClickItemParam.RIGHT_CLICK);
 | 
			
		||||
 | 
			
		||||
    public static final int OUTSIDE_SLOT = -999;
 | 
			
		||||
 | 
			
		||||
    public final WindowAction windowAction;
 | 
			
		||||
    public final WindowActionParam actionParam;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,239 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.click;
 | 
			
		||||
 | 
			
		||||
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 it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.IntSet;
 | 
			
		||||
import lombok.Value;
 | 
			
		||||
import org.geysermc.connector.inventory.GeyserItemStack;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
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.*;
 | 
			
		||||
 | 
			
		||||
public class ClickPlan {
 | 
			
		||||
    private final List<ClickAction> plan = new ArrayList<>();
 | 
			
		||||
    private final Int2ObjectMap<GeyserItemStack> simulatedItems;
 | 
			
		||||
    private GeyserItemStack simulatedCursor;
 | 
			
		||||
    private boolean simulating;
 | 
			
		||||
 | 
			
		||||
    private final GeyserSession session;
 | 
			
		||||
    private final InventoryTranslator translator;
 | 
			
		||||
    private final Inventory inventory;
 | 
			
		||||
 | 
			
		||||
    public ClickPlan(GeyserSession session, InventoryTranslator translator, Inventory inventory) {
 | 
			
		||||
        this.session = session;
 | 
			
		||||
        this.translator = translator;
 | 
			
		||||
        this.inventory = inventory;
 | 
			
		||||
 | 
			
		||||
        this.simulatedItems = new Int2ObjectOpenHashMap<>(inventory.getSize());
 | 
			
		||||
        this.simulatedCursor = session.getPlayerInventory().getCursor().copy();
 | 
			
		||||
        this.simulating = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void resetSimulation() {
 | 
			
		||||
        this.simulatedItems.clear();
 | 
			
		||||
        this.simulatedCursor = session.getPlayerInventory().getCursor().copy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void add(Click click, int slot) {
 | 
			
		||||
        if (!simulating)
 | 
			
		||||
            throw new UnsupportedOperationException("ClickPlan already executed");
 | 
			
		||||
 | 
			
		||||
        if (click == Click.LEFT_OUTSIDE || click == Click.RIGHT_OUTSIDE) {
 | 
			
		||||
            slot = Click.OUTSIDE_SLOT;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ClickAction action = new ClickAction(click, slot);
 | 
			
		||||
        plan.add(action);
 | 
			
		||||
        simulateAction(action);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void execute(boolean refresh) {
 | 
			
		||||
        //update geyser inventory after simulation to avoid net id desync
 | 
			
		||||
        resetSimulation();
 | 
			
		||||
        ListIterator<ClickAction> planIter = plan.listIterator();
 | 
			
		||||
        while (planIter.hasNext()) {
 | 
			
		||||
            ClickAction action = planIter.next();
 | 
			
		||||
 | 
			
		||||
            if (action.slot != Click.OUTSIDE_SLOT && translator.getSlotType(action.slot) != SlotType.NORMAL) {
 | 
			
		||||
                refresh = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ItemStack clickedItemStack;
 | 
			
		||||
            if (!planIter.hasNext() && refresh) {
 | 
			
		||||
                clickedItemStack = InventoryUtils.REFRESH_ITEM;
 | 
			
		||||
            } else if (action.click.windowAction == WindowAction.DROP_ITEM || action.slot == Click.OUTSIDE_SLOT) {
 | 
			
		||||
                clickedItemStack = null;
 | 
			
		||||
            } else {
 | 
			
		||||
                clickedItemStack = getItem(action.slot).getItemStack();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            short actionId = inventory.getNextTransactionId();
 | 
			
		||||
            ClientWindowActionPacket clickPacket = new ClientWindowActionPacket(
 | 
			
		||||
                    inventory.getId(),
 | 
			
		||||
                    actionId,
 | 
			
		||||
                    action.slot,
 | 
			
		||||
                    clickedItemStack,
 | 
			
		||||
                    action.click.windowAction,
 | 
			
		||||
                    action.click.actionParam
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            simulateAction(action);
 | 
			
		||||
 | 
			
		||||
            session.sendDownstreamPacket(clickPacket);
 | 
			
		||||
            if (clickedItemStack == InventoryUtils.REFRESH_ITEM) {
 | 
			
		||||
                session.sendDownstreamPacket(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true));
 | 
			
		||||
            }
 | 
			
		||||
            System.out.println(clickPacket);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        session.getPlayerInventory().setCursor(simulatedCursor, session);
 | 
			
		||||
        for (Int2ObjectMap.Entry<GeyserItemStack> simulatedSlot : simulatedItems.int2ObjectEntrySet()) {
 | 
			
		||||
            inventory.setItem(simulatedSlot.getIntKey(), simulatedSlot.getValue(), session);
 | 
			
		||||
        }
 | 
			
		||||
        simulating = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public GeyserItemStack getItem(int slot) {
 | 
			
		||||
        return simulatedItems.computeIfAbsent(slot, k -> inventory.getItem(slot).copy());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public GeyserItemStack getCursor() {
 | 
			
		||||
        return simulatedCursor;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setItem(int slot, GeyserItemStack item) {
 | 
			
		||||
        if (simulating) {
 | 
			
		||||
            simulatedItems.put(slot, item);
 | 
			
		||||
        } else {
 | 
			
		||||
            inventory.setItem(slot, item, session);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setCursor(GeyserItemStack item) {
 | 
			
		||||
        if (simulating) {
 | 
			
		||||
            simulatedCursor = item;
 | 
			
		||||
        } else {
 | 
			
		||||
            session.getPlayerInventory().setCursor(item, session);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void simulateAction(ClickAction action) {
 | 
			
		||||
        GeyserItemStack cursor = simulating ? getCursor() : session.getPlayerInventory().getCursor();
 | 
			
		||||
        switch (action.click) {
 | 
			
		||||
            case LEFT_OUTSIDE:
 | 
			
		||||
                setCursor(GeyserItemStack.EMPTY);
 | 
			
		||||
                return;
 | 
			
		||||
            case RIGHT_OUTSIDE:
 | 
			
		||||
                if (!cursor.isEmpty()) {
 | 
			
		||||
                    cursor.sub(1);
 | 
			
		||||
                }
 | 
			
		||||
                return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        GeyserItemStack clicked = simulating ? getItem(action.slot) : inventory.getItem(action.slot);
 | 
			
		||||
        if (translator.getSlotType(action.slot) == SlotType.OUTPUT) {
 | 
			
		||||
            switch (action.click) {
 | 
			
		||||
                case LEFT:
 | 
			
		||||
                case RIGHT:
 | 
			
		||||
                    if (cursor.isEmpty() && !clicked.isEmpty()) {
 | 
			
		||||
                        setCursor(clicked.copy());
 | 
			
		||||
                    } else if (InventoryUtils.canStack(cursor, clicked)) {
 | 
			
		||||
                        cursor.add(clicked.getAmount());
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            switch (action.click) {
 | 
			
		||||
                case LEFT:
 | 
			
		||||
                    if (!InventoryUtils.canStack(cursor, clicked)) {
 | 
			
		||||
                        setCursor(clicked);
 | 
			
		||||
                        setItem(action.slot, cursor);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        setCursor(GeyserItemStack.EMPTY);
 | 
			
		||||
                        clicked.add(cursor.getAmount());
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case RIGHT:
 | 
			
		||||
                    if (cursor.isEmpty() && !clicked.isEmpty()) {
 | 
			
		||||
                        int half = clicked.getAmount() / 2; //smaller half
 | 
			
		||||
                        setCursor(clicked.copy(clicked.getAmount() - half)); //larger half
 | 
			
		||||
                        clicked.setAmount(half);
 | 
			
		||||
                    } else if (!cursor.isEmpty() && clicked.isEmpty()) {
 | 
			
		||||
                        cursor.sub(1);
 | 
			
		||||
                        setItem(action.slot, cursor.copy(1));
 | 
			
		||||
                    } else if (InventoryUtils.canStack(cursor, clicked)) {
 | 
			
		||||
                        cursor.sub(1);
 | 
			
		||||
                        clicked.add(1);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case LEFT_SHIFT:
 | 
			
		||||
                    //TODO
 | 
			
		||||
                    break;
 | 
			
		||||
                case DROP_ONE:
 | 
			
		||||
                    if (!clicked.isEmpty()) {
 | 
			
		||||
                        clicked.sub(1);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case DROP_ALL:
 | 
			
		||||
                    setItem(action.slot, GeyserItemStack.EMPTY);
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return a new set of all affected slots. This isn't a constant variable; it's newly generated each time it is run.
 | 
			
		||||
     */
 | 
			
		||||
    public IntSet getAffectedSlots() {
 | 
			
		||||
        IntSet affectedSlots = new IntOpenHashSet();
 | 
			
		||||
        for (ClickAction action : plan) {
 | 
			
		||||
            if (translator.getSlotType(action.slot) == SlotType.NORMAL && action.slot != Click.OUTSIDE_SLOT) {
 | 
			
		||||
                affectedSlots.add(action.slot);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return affectedSlots;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Value
 | 
			
		||||
    private static class ClickAction {
 | 
			
		||||
        Click click;
 | 
			
		||||
        /**
 | 
			
		||||
         * Java slot
 | 
			
		||||
         */
 | 
			
		||||
        int slot;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +38,9 @@ import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		|||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Manages the fake block we implement for each inventory.
 | 
			
		||||
 */
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public class BlockInventoryHolder extends InventoryHolder {
 | 
			
		||||
    private final int blockId;
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +48,11 @@ public class BlockInventoryHolder extends InventoryHolder {
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
 | 
			
		||||
        //TODO: Improve on this (for example, multiple block states). We need this for the beacon.
 | 
			
		||||
        if (BlockTranslator.getBedrockBlockId(session.getConnector().getWorldManager().getBlockAt(session, session.getLastInteractionPosition())) == blockId) {
 | 
			
		||||
            inventory.setHolderPosition(session.getLastInteractionPosition());
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        Vector3i position = session.getPlayerEntity().getPosition().toInt();
 | 
			
		||||
        position = position.add(Vector3i.UP);
 | 
			
		||||
        UpdateBlockPacket blockPacket = new UpdateBlockPacket();
 | 
			
		||||
| 
						 | 
				
			
			@ -79,12 +87,16 @@ public class BlockInventoryHolder extends InventoryHolder {
 | 
			
		|||
    @Override
 | 
			
		||||
    public void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
 | 
			
		||||
        Vector3i holderPos = inventory.getHolderPosition();
 | 
			
		||||
        if (holderPos.equals(session.getLastInteractionPosition())) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        Position pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
 | 
			
		||||
        int realBlock = session.getConnector().getWorldManager().getBlockAt(session, pos.getX(), pos.getY(), pos.getZ());
 | 
			
		||||
        UpdateBlockPacket blockPacket = new UpdateBlockPacket();
 | 
			
		||||
        blockPacket.setDataLayer(0);
 | 
			
		||||
        blockPacket.setBlockPosition(holderPos);
 | 
			
		||||
        blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
 | 
			
		||||
        blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
 | 
			
		||||
        session.sendUpstreamPacket(blockPacket);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,25 +23,33 @@
 | 
			
		|||
 * @link https://github.com/GeyserMC/Geyser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory;
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory.translators;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.world.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;
 | 
			
		||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
 | 
			
		||||
 | 
			
		||||
public class BlockInventoryTranslator extends BaseInventoryTranslator {
 | 
			
		||||
/**
 | 
			
		||||
 * Provided as a base for any inventory that requires a block for opening it
 | 
			
		||||
 */
 | 
			
		||||
public abstract class AbstractBlockInventoryTranslator extends BaseInventoryTranslator {
 | 
			
		||||
    private final InventoryHolder holder;
 | 
			
		||||
    private final InventoryUpdater updater;
 | 
			
		||||
 | 
			
		||||
    public BlockInventoryTranslator(int size, String javaBlockIdentifier, ContainerType containerType, InventoryUpdater updater) {
 | 
			
		||||
    /**
 | 
			
		||||
     * @param size the amount of slots that the inventory adds alongside the base inventory slots
 | 
			
		||||
     * @param javaBlockIdentifier a Java block identifier that is used as a temporary block
 | 
			
		||||
     * @param containerType the container type of this inventory
 | 
			
		||||
     * @param updater updater
 | 
			
		||||
     */
 | 
			
		||||
    public AbstractBlockInventoryTranslator(int size, String javaBlockIdentifier, ContainerType containerType, InventoryUpdater updater) {
 | 
			
		||||
        super(size);
 | 
			
		||||
        int javaBlockState = BlockTranslator.getJavaBlockState(javaBlockIdentifier);
 | 
			
		||||
        int blockId = BlockTranslator.getBedrockBlockId(javaBlockState);
 | 
			
		||||
        this.holder = new BlockInventoryHolder(blockId, containerType);
 | 
			
		||||
        this.holder = new BlockInventoryHolder(BlockTranslator.getBedrockBlockId(javaBlockState), containerType);
 | 
			
		||||
        this.updater = updater;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,87 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
 | 
			
		||||
import org.geysermc.connector.inventory.AnvilContainer;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.inventory.PlayerInventory;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater;
 | 
			
		||||
 | 
			
		||||
public class AnvilInventoryTranslator extends AbstractBlockInventoryTranslator {
 | 
			
		||||
    public AnvilInventoryTranslator() {
 | 
			
		||||
        super(3, "minecraft:anvil[facing=north]", ContainerType.ANVIL, UIInventoryUpdater.INSTANCE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.ANVIL_INPUT) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.ANVIL_MATERIAL) {
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.ANVIL_RESULT || slotInfoData.getContainer() == ContainerSlotType.CREATIVE_OUTPUT) {
 | 
			
		||||
            return 2;
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(slotInfoData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        switch (slot) {
 | 
			
		||||
            case 0:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.ANVIL_INPUT, 1);
 | 
			
		||||
            case 1:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.ANVIL_MATERIAL, 2);
 | 
			
		||||
            case 2:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.ANVIL_RESULT, 50);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @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 Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) {
 | 
			
		||||
        return new AnvilContainer(name, windowId, this.size, playerInventory);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -23,18 +23,21 @@
 | 
			
		|||
 * @link https://github.com/GeyserMC/Geyser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory;
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory.translators;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
 | 
			
		||||
import org.geysermc.connector.inventory.Container;
 | 
			
		||||
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.action.InventoryActionDataTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.SlotType;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public abstract class BaseInventoryTranslator extends InventoryTranslator{
 | 
			
		||||
    BaseInventoryTranslator(int size) {
 | 
			
		||||
public abstract class BaseInventoryTranslator extends InventoryTranslator {
 | 
			
		||||
    public BaseInventoryTranslator(int size) {
 | 
			
		||||
        super(size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -44,15 +47,18 @@ public abstract class BaseInventoryTranslator extends InventoryTranslator{
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    @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;
 | 
			
		||||
            }
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        int slotnum = slotInfoData.getSlot();
 | 
			
		||||
        switch (slotInfoData.getContainer()) {
 | 
			
		||||
            case HOTBAR_AND_INVENTORY:
 | 
			
		||||
            case HOTBAR:
 | 
			
		||||
            case INVENTORY:
 | 
			
		||||
                //hotbar
 | 
			
		||||
                if (slotnum >= 9) {
 | 
			
		||||
                    return slotnum + this.size - 9;
 | 
			
		||||
                } else {
 | 
			
		||||
                    return slotnum + this.size + 27;
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
        return slotnum;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -70,13 +76,26 @@ public abstract class BaseInventoryTranslator extends InventoryTranslator{
 | 
			
		|||
        return slot;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        if (slot >= this.size) {
 | 
			
		||||
            final int tmp = slot - this.size;
 | 
			
		||||
            if (tmp < 27) {
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.INVENTORY, tmp + 9);
 | 
			
		||||
            } else {
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.HOTBAR, tmp - 27);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        throw new IllegalArgumentException("Unknown bedrock 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);
 | 
			
		||||
    public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) {
 | 
			
		||||
        return new Container(name, windowId, this.size, playerInventory);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,134 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientSetBeaconEffectPacket;
 | 
			
		||||
import com.nukkitx.math.vector.Vector3i;
 | 
			
		||||
import com.nukkitx.nbt.NbtMap;
 | 
			
		||||
import com.nukkitx.nbt.NbtMapBuilder;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.BeaconPaymentStackRequestActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket;
 | 
			
		||||
import org.geysermc.connector.inventory.BeaconContainer;
 | 
			
		||||
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.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater;
 | 
			
		||||
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
 | 
			
		||||
public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator {
 | 
			
		||||
    public BeaconInventoryTranslator() {
 | 
			
		||||
        super(1, "minecraft:beacon", ContainerType.BEACON, UIInventoryUpdater.INSTANCE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
 | 
			
		||||
        //FIXME?: Beacon graphics look weird after inputting an item. This might be a Bedrock bug, since it resets to nothing
 | 
			
		||||
        // on BDS
 | 
			
		||||
        BeaconContainer beaconContainer = (BeaconContainer) inventory;
 | 
			
		||||
        switch (key) {
 | 
			
		||||
            case 0:
 | 
			
		||||
                // Power - beacon doesn't use this, and uses the block position instead
 | 
			
		||||
                break;
 | 
			
		||||
            case 1:
 | 
			
		||||
                beaconContainer.setPrimaryId(value == -1 ? 0 : value);
 | 
			
		||||
                break;
 | 
			
		||||
            case 2:
 | 
			
		||||
                beaconContainer.setSecondaryId(value == -1 ? 0 : value);
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Send a block entity data packet update to the fake beacon inventory
 | 
			
		||||
        Vector3i position = inventory.getHolderPosition();
 | 
			
		||||
        NbtMapBuilder builder = NbtMap.builder()
 | 
			
		||||
                .putInt("x", position.getX())
 | 
			
		||||
                .putInt("y", position.getY())
 | 
			
		||||
                .putInt("z", position.getZ())
 | 
			
		||||
                .putString("CustomName", inventory.getTitle())
 | 
			
		||||
                .putString("id", "Beacon")
 | 
			
		||||
                .putInt("primary", beaconContainer.getPrimaryId())
 | 
			
		||||
                .putInt("secondary", beaconContainer.getSecondaryId());
 | 
			
		||||
 | 
			
		||||
        BlockEntityDataPacket packet = new BlockEntityDataPacket();
 | 
			
		||||
        packet.setBlockPosition(position);
 | 
			
		||||
        packet.setData(builder.build());
 | 
			
		||||
        System.out.println(packet.toString());
 | 
			
		||||
        session.sendUpstreamPacket(packet);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) {
 | 
			
		||||
        return action.getType() == StackRequestActionType.BEACON_PAYMENT;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) {
 | 
			
		||||
        // Input a beacon payment
 | 
			
		||||
        BeaconPaymentStackRequestActionData beaconPayment = (BeaconPaymentStackRequestActionData) request.getActions()[0];
 | 
			
		||||
        ClientSetBeaconEffectPacket packet = new ClientSetBeaconEffectPacket(beaconPayment.getPrimaryEffect(), beaconPayment.getSecondaryEffect());
 | 
			
		||||
        System.out.println(packet.toString());
 | 
			
		||||
        session.sendDownstreamPacket(packet);
 | 
			
		||||
        return acceptRequest(request, makeContainerEntries(session, inventory, Collections.emptySet()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.BEACON_PAYMENT) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(slotInfoData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        if (slot == 0) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.BEACON_PAYMENT, 27);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int javaSlotToBedrock(int slot) {
 | 
			
		||||
        if (slot == 0) {
 | 
			
		||||
            return 27;
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrock(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) {
 | 
			
		||||
        return new BeaconContainer(name, windowId, this.size, playerInventory);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -23,18 +23,20 @@
 | 
			
		|||
 * @link https://github.com/GeyserMC/Geyser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory;
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory.translators;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
 | 
			
		||||
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.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
 | 
			
		||||
 | 
			
		||||
public class BrewingInventoryTranslator extends BlockInventoryTranslator {
 | 
			
		||||
public class BrewingInventoryTranslator extends AbstractBlockInventoryTranslator {
 | 
			
		||||
    public BrewingInventoryTranslator() {
 | 
			
		||||
        super(5, "minecraft:brewing_stand[has_bottle_0=false,has_bottle_1=false,has_bottle_2=false]", ContainerType.BREWING_STAND, new ContainerInventoryUpdater());
 | 
			
		||||
        super(5, "minecraft:brewing_stand[has_bottle_0=false,has_bottle_1=false,has_bottle_2=false]", ContainerType.BREWING_STAND, ContainerInventoryUpdater.INSTANCE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -66,20 +68,18 @@ public class BrewingInventoryTranslator extends BlockInventoryTranslator {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    @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;
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        System.out.println("Brewing stand: " + slotInfoData);
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.BREWING_INPUT) {
 | 
			
		||||
            // Ingredient
 | 
			
		||||
            // TODO: This hasn't worked and then suddenly, it did.
 | 
			
		||||
            return 3;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.BREWING_RESULT) {
 | 
			
		||||
            // Potions
 | 
			
		||||
            return slotInfoData.getSlot() - 1;
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(slotInfoData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -96,4 +96,18 @@ public class BrewingInventoryTranslator extends BlockInventoryTranslator {
 | 
			
		|||
        }
 | 
			
		||||
        return super.javaSlotToBedrock(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        if (slot == 0 || slot == 1 || slot == 2) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.BREWING_RESULT, javaSlotToBedrock(slot));
 | 
			
		||||
        }
 | 
			
		||||
        if (slot == 3) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.BREWING_INPUT, 0);
 | 
			
		||||
        }
 | 
			
		||||
        if (slot == 4) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.BREWING_FUEL, 4);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,103 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
 | 
			
		||||
import org.geysermc.connector.inventory.CartographyContainer;
 | 
			
		||||
import org.geysermc.connector.inventory.GeyserItemStack;
 | 
			
		||||
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.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater;
 | 
			
		||||
 | 
			
		||||
public class CartographyInventoryTranslator extends AbstractBlockInventoryTranslator {
 | 
			
		||||
    public CartographyInventoryTranslator() {
 | 
			
		||||
        super(3, "minecraft:cartography_table", ContainerType.CARTOGRAPHY, UIInventoryUpdater.INSTANCE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean shouldRejectItemPlace(GeyserSession session, Inventory inventory, int javaSourceSlot, int javaDestinationSlot) {
 | 
			
		||||
        if (javaDestinationSlot == 0) {
 | 
			
		||||
            // Bedrock Edition can use paper in slot 0
 | 
			
		||||
            GeyserItemStack itemStack = javaSourceSlot == -1 ? session.getPlayerInventory().getCursor() : inventory.getItem(javaSourceSlot);
 | 
			
		||||
            return itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:paper");
 | 
			
		||||
        } else if (javaDestinationSlot == 1) {
 | 
			
		||||
            // Bedrock Edition can use a compass to create locator maps in the ADDITIONAL slot
 | 
			
		||||
            GeyserItemStack itemStack = javaSourceSlot == -1 ? session.getPlayerInventory().getCursor() : inventory.getItem(javaSourceSlot);
 | 
			
		||||
            return itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:compass");
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.CARTOGRAPHY_INPUT) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.CARTOGRAPHY_ADDITIONAL) {
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.CARTOGRAPHY_RESULT || slotInfoData.getContainer() == ContainerSlotType.CREATIVE_OUTPUT) {
 | 
			
		||||
            return 2;
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(slotInfoData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        switch (slot) {
 | 
			
		||||
            case 0:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.CARTOGRAPHY_INPUT, 12);
 | 
			
		||||
            case 1:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.CARTOGRAPHY_ADDITIONAL, 13);
 | 
			
		||||
            case 2:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.CARTOGRAPHY_RESULT, 50);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int javaSlotToBedrock(int slot) {
 | 
			
		||||
        switch (slot) {
 | 
			
		||||
            case 0:
 | 
			
		||||
                return 12;
 | 
			
		||||
            case 1:
 | 
			
		||||
                return 13;
 | 
			
		||||
            case 2:
 | 
			
		||||
                return 50;
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrock(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) {
 | 
			
		||||
        return new CartographyContainer(name, windowId, this.size, playerInventory);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -23,47 +23,18 @@
 | 
			
		|||
 * @link https://github.com/GeyserMC/Geyser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory;
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory.translators;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventorySource;
 | 
			
		||||
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.utils.InventoryUtils;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.SlotType;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class CraftingInventoryTranslator extends BlockInventoryTranslator {
 | 
			
		||||
public class CraftingInventoryTranslator extends AbstractBlockInventoryTranslator {
 | 
			
		||||
    public CraftingInventoryTranslator() {
 | 
			
		||||
        super(10, "minecraft:crafting_table", ContainerType.WORKBENCH, new CursorInventoryUpdater());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(InventoryActionData action) {
 | 
			
		||||
        if (action.getSlot() == 50) {
 | 
			
		||||
            // Slot 50 is used for crafting with a controller.
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (action.getSource().getContainerId() == ContainerId.UI) {
 | 
			
		||||
            int slotnum = action.getSlot();
 | 
			
		||||
            if (slotnum >= 32 && 42 >= slotnum) {
 | 
			
		||||
                return slotnum - 31;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(action);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int javaSlotToBedrock(int slot) {
 | 
			
		||||
        if (slot < size) {
 | 
			
		||||
            return slot == 0 ? 50 : slot + 31;
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrock(slot);
 | 
			
		||||
        super(10, "minecraft:crafting_table", ContainerType.WORKBENCH, UIInventoryUpdater.INSTANCE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -74,16 +45,34 @@ public class CraftingInventoryTranslator extends BlockInventoryTranslator {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    @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;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        if (slot >= 1 && slot <= 9) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.CRAFTING_INPUT, slot + 31);
 | 
			
		||||
        }
 | 
			
		||||
        super.translateActions(session, inventory, actions);
 | 
			
		||||
        if (slot == 0) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.CRAFTING_OUTPUT, 0);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.CRAFTING_INPUT) {
 | 
			
		||||
            // Java goes from 1 - 9, left to right then up to down
 | 
			
		||||
            // Bedrock is the same, but it starts from 32.
 | 
			
		||||
            return slotInfoData.getSlot() - 31;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.CRAFTING_OUTPUT || slotInfoData.getContainer() == ContainerSlotType.CREATIVE_OUTPUT) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(slotInfoData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int javaSlotToBedrock(int slot) {
 | 
			
		||||
        if (slot < size) {
 | 
			
		||||
            return slot == 0 ? 50 : slot + 31;
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrock(slot);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,219 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.EnchantOptionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.CraftRecipeStackRequestActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.PlayerEnchantOptionsPacket;
 | 
			
		||||
import org.geysermc.connector.inventory.EnchantingContainer;
 | 
			
		||||
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.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.Enchantment;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
 | 
			
		||||
public class EnchantingInventoryTranslator extends AbstractBlockInventoryTranslator {
 | 
			
		||||
    public EnchantingInventoryTranslator() {
 | 
			
		||||
        super(2, "minecraft:enchanting_table", ContainerType.ENCHANTMENT, UIInventoryUpdater.INSTANCE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
 | 
			
		||||
        int slotToUpdate;
 | 
			
		||||
        EnchantingContainer enchantingInventory = (EnchantingContainer) inventory;
 | 
			
		||||
        boolean shouldUpdate = false;
 | 
			
		||||
        switch (key) {
 | 
			
		||||
            case 0:
 | 
			
		||||
            case 1:
 | 
			
		||||
            case 2:
 | 
			
		||||
                // Experience required
 | 
			
		||||
                slotToUpdate = key;
 | 
			
		||||
                enchantingInventory.getGeyserEnchantOptions()[slotToUpdate].setXpCost(value);
 | 
			
		||||
                break;
 | 
			
		||||
            case 4:
 | 
			
		||||
            case 5:
 | 
			
		||||
            case 6:
 | 
			
		||||
                // Enchantment type
 | 
			
		||||
                slotToUpdate = key - 4;
 | 
			
		||||
                int index = value;
 | 
			
		||||
                if (index != -1) {
 | 
			
		||||
                    Enchantment enchantment = Enchantment.getByJavaIdentifier("minecraft:" + JavaEnchantment.values()[index].name().toLowerCase());
 | 
			
		||||
                    if (enchantment != null) {
 | 
			
		||||
                        // Convert the Java enchantment index to Bedrock's
 | 
			
		||||
                        index = enchantment.ordinal();
 | 
			
		||||
                    } else {
 | 
			
		||||
                        index = -1;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                enchantingInventory.getGeyserEnchantOptions()[slotToUpdate].setJavaEnchantIndex(value);
 | 
			
		||||
                enchantingInventory.getGeyserEnchantOptions()[slotToUpdate].setBedrockEnchantIndex(index);
 | 
			
		||||
                break;
 | 
			
		||||
            case 7:
 | 
			
		||||
            case 8:
 | 
			
		||||
            case 9:
 | 
			
		||||
                // Enchantment level
 | 
			
		||||
                slotToUpdate = key - 7;
 | 
			
		||||
                enchantingInventory.getGeyserEnchantOptions()[slotToUpdate].setEnchantLevel(value);
 | 
			
		||||
                shouldUpdate = true; // Java sends each property as its own packet, so let's only update after all properties have been sent
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                return;
 | 
			
		||||
        }
 | 
			
		||||
        if (shouldUpdate) {
 | 
			
		||||
            enchantingInventory.getEnchantOptions()[slotToUpdate] = enchantingInventory.getGeyserEnchantOptions()[slotToUpdate].build(session);
 | 
			
		||||
            PlayerEnchantOptionsPacket packet = new PlayerEnchantOptionsPacket();
 | 
			
		||||
            packet.getOptions().addAll(Arrays.asList(enchantingInventory.getEnchantOptions()));
 | 
			
		||||
            System.out.println(packet);
 | 
			
		||||
            session.sendUpstreamPacket(packet);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) {
 | 
			
		||||
        return action.getType() == StackRequestActionType.CRAFT_RECIPE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) {
 | 
			
		||||
        // Client has requested an item to be enchanted
 | 
			
		||||
        CraftRecipeStackRequestActionData craftRecipeData = (CraftRecipeStackRequestActionData) request.getActions()[0];
 | 
			
		||||
        EnchantingContainer enchantingInventory = (EnchantingContainer) inventory;
 | 
			
		||||
        int javaSlot = -1;
 | 
			
		||||
        for (int i = 0; i < enchantingInventory.getEnchantOptions().length; i++) {
 | 
			
		||||
            EnchantOptionData enchantData = enchantingInventory.getEnchantOptions()[i];
 | 
			
		||||
            if (enchantData != null) {
 | 
			
		||||
                if (craftRecipeData.getRecipeNetworkId() == enchantData.getEnchantNetId()) {
 | 
			
		||||
                    // Enchant net ID is how we differentiate between what item Bedrock wants
 | 
			
		||||
                    javaSlot = enchantingInventory.getGeyserEnchantOptions()[i].getJavaIndex();
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (javaSlot == -1) {
 | 
			
		||||
            // Slot should be determined as 0, 1, or 2
 | 
			
		||||
            return rejectRequest(request);
 | 
			
		||||
        }
 | 
			
		||||
        ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), javaSlot);
 | 
			
		||||
        System.out.println(packet);
 | 
			
		||||
        session.sendDownstreamPacket(packet);
 | 
			
		||||
        return acceptRequest(request, makeContainerEntries(session, inventory, Collections.emptySet()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.ENCHANTING_INPUT) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.ENCHANTING_LAPIS) {
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(slotInfoData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        if (slot == 0) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.ENCHANTING_INPUT, 14);
 | 
			
		||||
        }
 | 
			
		||||
        if (slot == 1) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.ENCHANTING_LAPIS, 15);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int javaSlotToBedrock(int slot) {
 | 
			
		||||
        if (slot == 0) {
 | 
			
		||||
            return 14;
 | 
			
		||||
        }
 | 
			
		||||
        if (slot == 1) {
 | 
			
		||||
            return 15;
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrock(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) {
 | 
			
		||||
        return new EnchantingContainer(name, windowId, this.size, playerInventory);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Enchantments classified by their Java index
 | 
			
		||||
     */
 | 
			
		||||
    public enum JavaEnchantment {
 | 
			
		||||
        PROTECTION,
 | 
			
		||||
        FIRE_PROTECTION,
 | 
			
		||||
        FEATHER_FALLING,
 | 
			
		||||
        BLAST_PROTECTION,
 | 
			
		||||
        PROJECTILE_PROTECTION,
 | 
			
		||||
        RESPIRATION,
 | 
			
		||||
        AQUA_AFFINITY,
 | 
			
		||||
        THORNS,
 | 
			
		||||
        DEPTH_STRIDER,
 | 
			
		||||
        FROST_WALKER,
 | 
			
		||||
        BINDING_CURSE,
 | 
			
		||||
        SOUL_SPEED,
 | 
			
		||||
        SHARPNESS,
 | 
			
		||||
        SMITE,
 | 
			
		||||
        BANE_OF_ARTHROPODS,
 | 
			
		||||
        KNOCKBACK,
 | 
			
		||||
        FIRE_ASPECT,
 | 
			
		||||
        LOOTING,
 | 
			
		||||
        SWEEPING,
 | 
			
		||||
        EFFICIENCY,
 | 
			
		||||
        SILK_TOUCH,
 | 
			
		||||
        UNBREAKING,
 | 
			
		||||
        FORTUNE,
 | 
			
		||||
        POWER,
 | 
			
		||||
        PUNCH,
 | 
			
		||||
        FLAME,
 | 
			
		||||
        INFINITY,
 | 
			
		||||
        LUCK_OF_THE_SEA,
 | 
			
		||||
        LURE,
 | 
			
		||||
        LOYALTY,
 | 
			
		||||
        IMPALING,
 | 
			
		||||
        RIPTIDE,
 | 
			
		||||
        CHANNELING,
 | 
			
		||||
        MULTISHOT,
 | 
			
		||||
        QUICK_CHARGE,
 | 
			
		||||
        PIERCING,
 | 
			
		||||
        MENDING,
 | 
			
		||||
        VANISHING_CURSE
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Implemented on top of any block that does not have special properties implemented
 | 
			
		||||
 */
 | 
			
		||||
public class GenericBlockInventoryTranslator extends AbstractBlockInventoryTranslator {
 | 
			
		||||
    public GenericBlockInventoryTranslator(int size, String javaBlockIdentifier, ContainerType containerType) {
 | 
			
		||||
        super(size, javaBlockIdentifier, containerType, ContainerInventoryUpdater.INSTANCE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int javaSlot) {
 | 
			
		||||
        if (javaSlot < this.size) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.CONTAINER, javaSlot);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(javaSlot);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -23,34 +23,44 @@
 | 
			
		|||
 * @link https://github.com/GeyserMC/Geyser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory;
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory.translators;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
 | 
			
		||||
 | 
			
		||||
public class GrindstoneInventoryTranslator extends BlockInventoryTranslator {
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater;
 | 
			
		||||
 | 
			
		||||
public class GrindstoneInventoryTranslator extends AbstractBlockInventoryTranslator {
 | 
			
		||||
    public GrindstoneInventoryTranslator() {
 | 
			
		||||
        super(3, "minecraft:grindstone[face=floor,facing=north]", ContainerType.GRINDSTONE, new CursorInventoryUpdater());
 | 
			
		||||
        super(3, "minecraft:grindstone[face=floor,facing=north]", ContainerType.GRINDSTONE, UIInventoryUpdater.INSTANCE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(InventoryActionData action) {
 | 
			
		||||
        final int slot = super.bedrockSlotToJava(action);
 | 
			
		||||
        if (action.getSource().getContainerId() == ContainerId.UI) {
 | 
			
		||||
            switch (slot) {
 | 
			
		||||
                case 16:
 | 
			
		||||
                    return 0;
 | 
			
		||||
                case 17:
 | 
			
		||||
                    return 1;
 | 
			
		||||
                case 50:
 | 
			
		||||
                    return 2;
 | 
			
		||||
                default:
 | 
			
		||||
                    return slot;
 | 
			
		||||
            }
 | 
			
		||||
        } return slot;
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.GRINDSTONE_INPUT) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.GRINDSTONE_ADDITIONAL) {
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.GRINDSTONE_RESULT || slotInfoData.getContainer() == ContainerSlotType.CREATIVE_OUTPUT) {
 | 
			
		||||
            return 2;
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(slotInfoData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        switch (slot) {
 | 
			
		||||
            case 0:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.GRINDSTONE_INPUT, 16);
 | 
			
		||||
            case 1:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.GRINDSTONE_ADDITIONAL, 17);
 | 
			
		||||
            case 2:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.GRINDSTONE_RESULT, 50);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -65,5 +75,4 @@ public class GrindstoneInventoryTranslator extends BlockInventoryTranslator {
 | 
			
		|||
        }
 | 
			
		||||
        return super.javaSlotToBedrock(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,158 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket;
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
 | 
			
		||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
 | 
			
		||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
 | 
			
		||||
import com.nukkitx.math.vector.Vector3i;
 | 
			
		||||
import com.nukkitx.nbt.NbtMap;
 | 
			
		||||
import com.nukkitx.nbt.NbtMapBuilder;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
 | 
			
		||||
import org.geysermc.connector.inventory.GeyserItemStack;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.inventory.LecternContainer;
 | 
			
		||||
import org.geysermc.connector.inventory.PlayerInventory;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
 | 
			
		||||
import org.geysermc.connector.utils.BlockEntityUtils;
 | 
			
		||||
import org.geysermc.connector.utils.InventoryUtils;
 | 
			
		||||
 | 
			
		||||
public class LecternInventoryTranslator extends BaseInventoryTranslator {
 | 
			
		||||
    private final InventoryUpdater updater;
 | 
			
		||||
 | 
			
		||||
    public LecternInventoryTranslator() {
 | 
			
		||||
        super(1);
 | 
			
		||||
        this.updater = new LecternInventoryUpdater();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @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) {
 | 
			
		||||
        if (key == 0) { // Lectern page update
 | 
			
		||||
            LecternContainer lecternContainer = (LecternContainer) inventory;
 | 
			
		||||
            lecternContainer.setCurrentBedrockPage(value / 2);
 | 
			
		||||
            lecternContainer.setBlockEntityTag(lecternContainer.getBlockEntityTag().toBuilder().putInt("page", lecternContainer.getCurrentBedrockPage()).build());
 | 
			
		||||
            BlockEntityUtils.updateBlockEntity(session, lecternContainer.getBlockEntityTag(), lecternContainer.getPosition());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateInventory(GeyserSession session, Inventory inventory) {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
 | 
			
		||||
        this.updater.updateSlot(this, session, inventory, slot);
 | 
			
		||||
        if (slot == 0) {
 | 
			
		||||
            LecternContainer lecternContainer = (LecternContainer) inventory;
 | 
			
		||||
            if (session.isDroppingLecternBook()) {
 | 
			
		||||
                // We have to enter the inventory GUI to eject the book
 | 
			
		||||
                ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), 3);
 | 
			
		||||
                session.sendDownstreamPacket(packet);
 | 
			
		||||
                session.setDroppingLecternBook(false);
 | 
			
		||||
                InventoryUtils.closeInventory(session, inventory.getId());
 | 
			
		||||
            } else if (lecternContainer.getBlockEntityTag() == null) {
 | 
			
		||||
                // If the method returns true, this is already handled for us
 | 
			
		||||
                GeyserItemStack geyserItemStack = inventory.getItem(0);
 | 
			
		||||
                CompoundTag tag = geyserItemStack.getNbt();
 | 
			
		||||
                if (tag != null) {
 | 
			
		||||
                    // Position has to be the last interacted position... right?
 | 
			
		||||
                    Vector3i position = session.getLastInteractionPosition();
 | 
			
		||||
                    // shouldRefresh means that we should boot out the client on our side because their lectern GUI isn't updated yet
 | 
			
		||||
                    boolean shouldRefresh = !session.getConnector().getWorldManager().shouldExpectLecternHandled() && !session.getLecternCache().contains(position);
 | 
			
		||||
                    int pagesSize = ((ListTag) tag.get("pages")).size();
 | 
			
		||||
                    ItemData itemData = geyserItemStack.getItemData(session);
 | 
			
		||||
                    NbtMapBuilder lecternTag = getBaseLecternTag(position.getX(), position.getY(), position.getZ(), pagesSize);
 | 
			
		||||
                    lecternTag.putCompound("book", NbtMap.builder()
 | 
			
		||||
                            .putByte("Count", (byte) itemData.getCount())
 | 
			
		||||
                            .putShort("Damage", (short) 0)
 | 
			
		||||
                            .putString("Name", "minecraft:written_book")
 | 
			
		||||
                            .putCompound("tag", itemData.getTag())
 | 
			
		||||
                            .build());
 | 
			
		||||
                    lecternTag.putInt("page", lecternContainer.getCurrentBedrockPage());
 | 
			
		||||
                    NbtMap blockEntityTag = lecternTag.build();
 | 
			
		||||
                    // Even with serverside access to lecterns, we don't easily know which lectern this is, so we need to rebuild
 | 
			
		||||
                    // the block entity tag
 | 
			
		||||
                    lecternContainer.setBlockEntityTag(blockEntityTag);
 | 
			
		||||
                    lecternContainer.setPosition(position);
 | 
			
		||||
                    if (shouldRefresh) {
 | 
			
		||||
                        // Update the lectern because it's not updated client-side
 | 
			
		||||
                        BlockEntityUtils.updateBlockEntity(session, blockEntityTag, position);
 | 
			
		||||
                        session.getLecternCache().add(position);
 | 
			
		||||
                        // Close the window - we will reopen it once the client has this data synced
 | 
			
		||||
                        ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(lecternContainer.getId());
 | 
			
		||||
                        session.sendDownstreamPacket(closeWindowPacket);
 | 
			
		||||
                        InventoryUtils.closeInventory(session, inventory.getId());
 | 
			
		||||
                        session.getConnector().getLogger().warning("Closing inventory");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) {
 | 
			
		||||
        return new LecternContainer(name, windowId, this.size, playerInventory);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static NbtMapBuilder getBaseLecternTag(int x, int y, int z, int totalPages) {
 | 
			
		||||
        NbtMapBuilder builder = NbtMap.builder()
 | 
			
		||||
                .putInt("x", x)
 | 
			
		||||
                .putInt("y", y)
 | 
			
		||||
                .putInt("z", z)
 | 
			
		||||
                .putString("id", "Lectern");
 | 
			
		||||
        if (totalPages != 0) {
 | 
			
		||||
            builder.putByte("hasBook", (byte) 1);
 | 
			
		||||
            builder.putInt("totalPages", totalPages);
 | 
			
		||||
        } else {
 | 
			
		||||
            // Not usually needed, but helps with kicking out Bedrock players from reading the UI
 | 
			
		||||
            builder.putByte("hasBook", (byte) 0);
 | 
			
		||||
        }
 | 
			
		||||
        return builder;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class LecternInventoryUpdater extends InventoryUpdater {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,220 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket;
 | 
			
		||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
 | 
			
		||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
 | 
			
		||||
import com.nukkitx.nbt.NbtMap;
 | 
			
		||||
import com.nukkitx.nbt.NbtType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.CraftResultsDeprecatedStackRequestActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket;
 | 
			
		||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
 | 
			
		||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
 | 
			
		||||
import org.geysermc.connector.inventory.GeyserItemStack;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.translators.BannerTranslator;
 | 
			
		||||
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator {
 | 
			
		||||
    /**
 | 
			
		||||
     * A map of Bedrock patterns to Java index. Used to request for a specific banner pattern.
 | 
			
		||||
     */
 | 
			
		||||
    private static final Object2IntMap<String> PATTERN_TO_INDEX = new Object2IntOpenHashMap<>();
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        // Added from left-to-right then up-to-down in the order Java presents it
 | 
			
		||||
        int index = 1;
 | 
			
		||||
        PATTERN_TO_INDEX.put("bl", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("br", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("tl", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("tr", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("bs", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("ts", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("ls", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("rs", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("cs", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("ms", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("drs", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("dls", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("ss", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("cr", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("sc", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("bt", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("tt", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("bts", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("tts", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("ld", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("rd", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("lud", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("rud", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("mc", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("mr", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("vh", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("hh", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("vhr", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("hhb", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("bo", index++);
 | 
			
		||||
        index++; // Bordure indented, does not appear to exist in Bedrock?
 | 
			
		||||
        PATTERN_TO_INDEX.put("gra", index++);
 | 
			
		||||
        PATTERN_TO_INDEX.put("gru", index);
 | 
			
		||||
        // Bricks do not appear to be a pattern on Bedrock, either
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public LoomInventoryTranslator() {
 | 
			
		||||
        super(4, "minecraft:loom[facing=north]", ContainerType.LOOM, UIInventoryUpdater.INSTANCE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean shouldRejectItemPlace(GeyserSession session, Inventory inventory, int javaSourceSlot, int javaDestinationSlot) {
 | 
			
		||||
        if (javaDestinationSlot != 1) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        GeyserItemStack itemStack = javaSourceSlot == -1 ? session.getPlayerInventory().getCursor() : inventory.getItem(javaSourceSlot);
 | 
			
		||||
        if (itemStack.isEmpty()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Reject the item if Bedrock is attempting to put in a dye that is not a dye in Java Edition
 | 
			
		||||
        return !itemStack.getItemEntry().getJavaIdentifier().endsWith("_dye");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) {
 | 
			
		||||
        // If the LOOM_MATERIAL slot is not empty, we are crafting a pattern that does not come from an item
 | 
			
		||||
        return action.getType() == StackRequestActionType.CRAFT_NON_IMPLEMENTED_DEPRECATED && inventory.getItem(2).isEmpty();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) {
 | 
			
		||||
        // TODO: I anticipate this will be changed in the future to use something non-deprecated. Keep an eye out.
 | 
			
		||||
        StackRequestActionData data = request.getActions()[1];
 | 
			
		||||
        if (!(data instanceof CraftResultsDeprecatedStackRequestActionData)) {
 | 
			
		||||
            return rejectRequest(request);
 | 
			
		||||
        }
 | 
			
		||||
        CraftResultsDeprecatedStackRequestActionData craftData = (CraftResultsDeprecatedStackRequestActionData) data;
 | 
			
		||||
        // Get the patterns compound tag
 | 
			
		||||
        List<NbtMap> newBlockEntityTag = craftData.getResultItems()[0].getTag().getList("Patterns", NbtType.COMPOUND);
 | 
			
		||||
        // Get the pattern that the Bedrock client requests - the last pattern in the Patterns list
 | 
			
		||||
        NbtMap pattern = newBlockEntityTag.get(newBlockEntityTag.size() - 1);
 | 
			
		||||
        // Get the Java index of this pattern
 | 
			
		||||
        int index = PATTERN_TO_INDEX.getOrDefault(pattern.getString("Pattern"), -1);
 | 
			
		||||
        if (index == -1) {
 | 
			
		||||
            return rejectRequest(request);
 | 
			
		||||
        }
 | 
			
		||||
        // Java's formula: 4 * row + col
 | 
			
		||||
        // And the Java loom window has a fixed row/width of four
 | 
			
		||||
        // So... Number / 4 = row (so we don't have to bother there), and number % 4 is our column, which leads us back to our index. :)
 | 
			
		||||
        ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), index);
 | 
			
		||||
        System.out.println(packet);
 | 
			
		||||
        session.sendDownstreamPacket(packet);
 | 
			
		||||
 | 
			
		||||
        GeyserItemStack inputCopy = inventory.getItem(0).copy();
 | 
			
		||||
        inputCopy.setNetId(session.getNextItemNetId());
 | 
			
		||||
        // Add the pattern manually, for better item synchronization
 | 
			
		||||
        if (inputCopy.getNbt() == null) {
 | 
			
		||||
            inputCopy.setNbt(new CompoundTag(""));
 | 
			
		||||
        }
 | 
			
		||||
        CompoundTag blockEntityTag = inputCopy.getNbt().get("BlockEntityTag");
 | 
			
		||||
        CompoundTag javaBannerPattern = BannerTranslator.getJavaBannerPattern(pattern);
 | 
			
		||||
        if (blockEntityTag != null) {
 | 
			
		||||
            ListTag patternsList = blockEntityTag.get("Patterns");
 | 
			
		||||
            if (patternsList != null) {
 | 
			
		||||
                patternsList.add(javaBannerPattern);
 | 
			
		||||
            } else {
 | 
			
		||||
                patternsList = new ListTag("Patterns", Collections.singletonList(javaBannerPattern));
 | 
			
		||||
                blockEntityTag.put(patternsList);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            blockEntityTag = new CompoundTag("BlockEntityTag");
 | 
			
		||||
            ListTag patternsList = new ListTag("Patterns", Collections.singletonList(javaBannerPattern));
 | 
			
		||||
            blockEntityTag.put(patternsList);
 | 
			
		||||
            inputCopy.getNbt().put(blockEntityTag);
 | 
			
		||||
        }
 | 
			
		||||
        // Set the new item as the output
 | 
			
		||||
        inventory.setItem(3, inputCopy, session);
 | 
			
		||||
 | 
			
		||||
        return translateRequest(session, inventory, request);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.LOOM_INPUT) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.LOOM_DYE) {
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.LOOM_MATERIAL) {
 | 
			
		||||
            return 2;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.LOOM_RESULT || slotInfoData.getContainer() == ContainerSlotType.CREATIVE_OUTPUT) {
 | 
			
		||||
            return 3;
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(slotInfoData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        switch (slot) {
 | 
			
		||||
            case 0:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.LOOM_INPUT, 9);
 | 
			
		||||
            case 1:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.LOOM_DYE, 10);
 | 
			
		||||
            case 2:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.LOOM_MATERIAL, 11);
 | 
			
		||||
            case 3:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.LOOM_RESULT, 50);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int javaSlotToBedrock(int slot) {
 | 
			
		||||
        switch (slot) {
 | 
			
		||||
            case 0:
 | 
			
		||||
                return 9;
 | 
			
		||||
            case 1:
 | 
			
		||||
                return 10;
 | 
			
		||||
            case 2:
 | 
			
		||||
                return 11;
 | 
			
		||||
            case 3:
 | 
			
		||||
                return 50;
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrock(slot);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,154 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
 | 
			
		||||
import com.nukkitx.math.vector.Vector3f;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.*;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket;
 | 
			
		||||
import org.geysermc.connector.entity.Entity;
 | 
			
		||||
import org.geysermc.connector.entity.type.EntityType;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.inventory.MerchantContainer;
 | 
			
		||||
import org.geysermc.connector.inventory.PlayerInventory;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.SlotType;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater;
 | 
			
		||||
 | 
			
		||||
public class MerchantInventoryTranslator extends BaseInventoryTranslator {
 | 
			
		||||
    private final InventoryUpdater updater;
 | 
			
		||||
 | 
			
		||||
    public MerchantInventoryTranslator() {
 | 
			
		||||
        super(3);
 | 
			
		||||
        this.updater = UIInventoryUpdater.INSTANCE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int javaSlotToBedrock(int slot) {
 | 
			
		||||
        switch (slot) {
 | 
			
		||||
            case 0:
 | 
			
		||||
                return 4;
 | 
			
		||||
            case 1:
 | 
			
		||||
                return 5;
 | 
			
		||||
            case 2:
 | 
			
		||||
                return 50;
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrock(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        switch (slot) {
 | 
			
		||||
            case 0:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.TRADE2_INGREDIENT1, 4);
 | 
			
		||||
            case 1:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.TRADE2_INGREDIENT2, 5);
 | 
			
		||||
            case 2:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.TRADE2_RESULT, 50);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        switch (slotInfoData.getContainer()) {
 | 
			
		||||
            case TRADE2_INGREDIENT1:
 | 
			
		||||
                return 0;
 | 
			
		||||
            case TRADE2_INGREDIENT2:
 | 
			
		||||
                return 1;
 | 
			
		||||
            case TRADE2_RESULT:
 | 
			
		||||
            case CREATIVE_OUTPUT:
 | 
			
		||||
                return 2;
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(slotInfoData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public SlotType getSlotType(int javaSlot) {
 | 
			
		||||
        if (javaSlot == 2) {
 | 
			
		||||
            return SlotType.OUTPUT;
 | 
			
		||||
        }
 | 
			
		||||
        return SlotType.NORMAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void prepareInventory(GeyserSession session, Inventory inventory) {
 | 
			
		||||
        MerchantContainer merchantInventory = (MerchantContainer) inventory;
 | 
			
		||||
        if (merchantInventory.getVillager() == null) {
 | 
			
		||||
            long geyserId = session.getEntityCache().getNextEntityId().incrementAndGet();
 | 
			
		||||
            Vector3f pos = session.getPlayerEntity().getPosition().sub(0, 3, 0);
 | 
			
		||||
 | 
			
		||||
            EntityDataMap metadata = new EntityDataMap();
 | 
			
		||||
            metadata.put(EntityData.SCALE, 0f);
 | 
			
		||||
            metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0f);
 | 
			
		||||
            metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0f);
 | 
			
		||||
 | 
			
		||||
            Entity villager = new Entity(0, geyserId, EntityType.VILLAGER, pos, Vector3f.ZERO, Vector3f.ZERO);
 | 
			
		||||
            villager.setMetadata(metadata);
 | 
			
		||||
            villager.spawnEntity(session);
 | 
			
		||||
 | 
			
		||||
            SetEntityLinkPacket linkPacket = new SetEntityLinkPacket();
 | 
			
		||||
            EntityLinkData.Type type = EntityLinkData.Type.PASSENGER;
 | 
			
		||||
            linkPacket.setEntityLink(new EntityLinkData(session.getPlayerEntity().getGeyserId(), geyserId, type, true, false));
 | 
			
		||||
            session.sendUpstreamPacket(linkPacket);
 | 
			
		||||
 | 
			
		||||
            merchantInventory.setVillager(villager);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void openInventory(GeyserSession session, Inventory inventory) {
 | 
			
		||||
        //Handled in JavaTradeListTranslator
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void closeInventory(GeyserSession session, Inventory inventory) {
 | 
			
		||||
        MerchantContainer merchantInventory = (MerchantContainer) inventory;
 | 
			
		||||
        if (merchantInventory.getVillager() != null) {
 | 
			
		||||
            merchantInventory.getVillager().despawnEntity(session);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @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 Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) {
 | 
			
		||||
        return new MerchantContainer(name, windowId, this.size, playerInventory);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -23,20 +23,20 @@
 | 
			
		|||
 * @link https://github.com/GeyserMC/Geyser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory;
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory.translators;
 | 
			
		||||
 | 
			
		||||
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.inventory.ContainerId;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventorySource;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.*;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket;
 | 
			
		||||
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.action.InventoryActionDataTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.SlotType;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
 | 
			
		||||
import org.geysermc.connector.utils.InventoryUtils;
 | 
			
		||||
import org.geysermc.connector.utils.LanguageUtils;
 | 
			
		||||
| 
						 | 
				
			
			@ -61,11 +61,11 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
 | 
			
		|||
        ItemData[] contents = new ItemData[36];
 | 
			
		||||
        // Inventory
 | 
			
		||||
        for (int i = 9; i < 36; i++) {
 | 
			
		||||
            contents[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
 | 
			
		||||
            contents[i] = inventory.getItem(i).getItemData(session);
 | 
			
		||||
        }
 | 
			
		||||
        // Hotbar
 | 
			
		||||
        for (int i = 36; i < 45; i++) {
 | 
			
		||||
            contents[i - 36] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
 | 
			
		||||
            contents[i - 36] = inventory.getItem(i).getItemData(session);
 | 
			
		||||
        }
 | 
			
		||||
        inventoryContentPacket.setContents(Arrays.asList(contents));
 | 
			
		||||
        session.sendUpstreamPacket(inventoryContentPacket);
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +75,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
 | 
			
		|||
        armorContentPacket.setContainerId(ContainerId.ARMOR);
 | 
			
		||||
        contents = new ItemData[4];
 | 
			
		||||
        for (int i = 5; i < 9; i++) {
 | 
			
		||||
            contents[i - 5] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
 | 
			
		||||
            contents[i - 5] = inventory.getItem(i).getItemData(session);
 | 
			
		||||
        }
 | 
			
		||||
        armorContentPacket.setContents(Arrays.asList(contents));
 | 
			
		||||
        session.sendUpstreamPacket(armorContentPacket);
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +83,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
 | 
			
		|||
        // Offhand
 | 
			
		||||
        InventoryContentPacket offhandPacket = new InventoryContentPacket();
 | 
			
		||||
        offhandPacket.setContainerId(ContainerId.OFFHAND);
 | 
			
		||||
        offhandPacket.setContents(Collections.singletonList(ItemTranslator.translateToBedrock(session, inventory.getItem(45))));
 | 
			
		||||
        offhandPacket.setContents(Collections.singletonList(inventory.getItem(45).getItemData(session)));
 | 
			
		||||
        session.sendUpstreamPacket(offhandPacket);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +102,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
 | 
			
		|||
            if (session.getGameMode() == GameMode.CREATIVE) {
 | 
			
		||||
                slotPacket.setItem(UNUSUABLE_CRAFTING_SPACE_BLOCK);
 | 
			
		||||
            }else{
 | 
			
		||||
                slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i)));
 | 
			
		||||
                slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i).getItemStack()));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            session.sendUpstreamPacket(slotPacket);
 | 
			
		||||
| 
						 | 
				
			
			@ -127,21 +127,23 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
 | 
			
		|||
                slotPacket.setContainerId(ContainerId.UI);
 | 
			
		||||
                slotPacket.setSlot(slot + 27);
 | 
			
		||||
            }
 | 
			
		||||
            slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(slot)));
 | 
			
		||||
            slotPacket.setItem(inventory.getItem(slot).getItemData(session));
 | 
			
		||||
            session.sendUpstreamPacket(slotPacket);
 | 
			
		||||
        } else if (slot == 45) {
 | 
			
		||||
            InventoryContentPacket offhandPacket = new InventoryContentPacket();
 | 
			
		||||
            offhandPacket.setContainerId(ContainerId.OFFHAND);
 | 
			
		||||
            offhandPacket.setContents(Collections.singletonList(ItemTranslator.translateToBedrock(session, inventory.getItem(slot))));
 | 
			
		||||
            offhandPacket.setContents(Collections.singletonList(inventory.getItem(slot).getItemData(session)));
 | 
			
		||||
            session.sendUpstreamPacket(offhandPacket);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(InventoryActionData action) {
 | 
			
		||||
        int slotnum = action.getSlot();
 | 
			
		||||
        switch (action.getSource().getContainerId()) {
 | 
			
		||||
            case ContainerId.INVENTORY:
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        int slotnum = slotInfoData.getSlot();
 | 
			
		||||
        switch (slotInfoData.getContainer()) {
 | 
			
		||||
            case HOTBAR_AND_INVENTORY:
 | 
			
		||||
            case HOTBAR:
 | 
			
		||||
            case INVENTORY:
 | 
			
		||||
                // Inventory
 | 
			
		||||
                if (slotnum >= 9 && slotnum <= 35) {
 | 
			
		||||
                    return slotnum;
 | 
			
		||||
| 
						 | 
				
			
			@ -151,19 +153,19 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
 | 
			
		|||
                    return slotnum + 36;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            case ContainerId.ARMOR:
 | 
			
		||||
            case ARMOR:
 | 
			
		||||
                if (slotnum >= 0 && slotnum <= 3) {
 | 
			
		||||
                    return slotnum + 5;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            case ContainerId.OFFHAND:
 | 
			
		||||
            case OFFHAND:
 | 
			
		||||
                return 45;
 | 
			
		||||
            case ContainerId.UI:
 | 
			
		||||
            case CRAFTING_INPUT:
 | 
			
		||||
                if (slotnum >= 28 && 31 >= slotnum) {
 | 
			
		||||
                    return slotnum - 27;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            case ContainerId.CRAFTING_RESULT:
 | 
			
		||||
            case CREATIVE_OUTPUT:
 | 
			
		||||
                return 0;
 | 
			
		||||
        }
 | 
			
		||||
        return slotnum;
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +173,26 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int javaSlotToBedrock(int slot) {
 | 
			
		||||
        return slot;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        if (slot >= 36 && slot <= 44) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.HOTBAR, slot - 36);
 | 
			
		||||
        } else if (slot >= 9 && slot <= 35) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.INVENTORY, slot);
 | 
			
		||||
        } else if (slot >= 5 && slot <= 8) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.ARMOR, slot - 5);
 | 
			
		||||
        } else if (slot == 45) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.OFFHAND, 1);
 | 
			
		||||
        } else if (slot >= 1 && slot <= 4) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.CRAFTING_INPUT, slot + 27);
 | 
			
		||||
        } else if (slot == 0) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.CRAFTING_OUTPUT, 0);
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new IllegalArgumentException("Unknown bedrock slot");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -182,52 +203,13 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    @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.UI && (action.getSlot() >= 28 && 31 >= action.getSlot())) {
 | 
			
		||||
                    updateInventory(session, inventory);
 | 
			
		||||
                    InventoryUtils.updateCursor(session);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
    public void translateRequests(GeyserSession session, Inventory inventory, List<ItemStackRequestPacket.Request> requests) {
 | 
			
		||||
        super.translateRequests(session, inventory, requests);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
            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 = ItemTranslator.translateToJava(action.getToItem());
 | 
			
		||||
                        }
 | 
			
		||||
                        ClientCreativeInventoryActionPacket creativePacket = new ClientCreativeInventoryActionPacket(javaSlot, javaItem);
 | 
			
		||||
                        session.sendDownstreamPacket(creativePacket);
 | 
			
		||||
                        inventory.setItem(javaSlot, javaItem);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ContainerId.UI:
 | 
			
		||||
                        if (action.getSlot() == 0) {
 | 
			
		||||
                            session.getInventory().setCursor(ItemTranslator.translateToJava(action.getToItem()));
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ContainerId.NONE:
 | 
			
		||||
                        if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION
 | 
			
		||||
                                && action.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
 | 
			
		||||
                            javaItem = ItemTranslator.translateToJava(action.getToItem());
 | 
			
		||||
                            ClientCreativeInventoryActionPacket creativeDropPacket = new ClientCreativeInventoryActionPacket(-1, javaItem);
 | 
			
		||||
                            session.sendDownstreamPacket(creativeDropPacket);
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        InventoryActionDataTranslator.translate(this, session, inventory, actions);
 | 
			
		||||
    @Override
 | 
			
		||||
    public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) {
 | 
			
		||||
        throw new UnsupportedOperationException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
 | 
			
		||||
 | 
			
		||||
public class ShulkerInventoryTranslator extends AbstractBlockInventoryTranslator {
 | 
			
		||||
    public ShulkerInventoryTranslator() {
 | 
			
		||||
        super(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER, ContainerInventoryUpdater.INSTANCE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int javaSlot) {
 | 
			
		||||
        if (javaSlot < this.size) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.SHULKER, javaSlot);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(javaSlot);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -23,34 +23,44 @@
 | 
			
		|||
 * @link https://github.com/GeyserMC/Geyser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory;
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory.translators;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
 | 
			
		||||
 | 
			
		||||
public class SmithingInventoryTranslator extends BlockInventoryTranslator {
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater;
 | 
			
		||||
 | 
			
		||||
public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslator {
 | 
			
		||||
    public SmithingInventoryTranslator() {
 | 
			
		||||
        super(3, "minecraft:smithing_table", ContainerType.SMITHING_TABLE, new CursorInventoryUpdater());
 | 
			
		||||
        super(3, "minecraft:smithing_table", ContainerType.SMITHING_TABLE, UIInventoryUpdater.INSTANCE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(InventoryActionData action) {
 | 
			
		||||
        final int slot = super.bedrockSlotToJava(action);
 | 
			
		||||
        if (action.getSource().getContainerId() == ContainerId.UI) {
 | 
			
		||||
            switch (slot) {
 | 
			
		||||
                case 51:
 | 
			
		||||
                    return 0;
 | 
			
		||||
                case 52:
 | 
			
		||||
                    return 1;
 | 
			
		||||
                case 50:
 | 
			
		||||
                    return 2;
 | 
			
		||||
                default:
 | 
			
		||||
                    return slot;
 | 
			
		||||
            }
 | 
			
		||||
        } return slot;
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.SMITHING_TABLE_INPUT) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.SMITHING_TABLE_MATERIAL) {
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.SMITHING_TABLE_RESULT || slotInfoData.getContainer() == ContainerSlotType.CREATIVE_OUTPUT) {
 | 
			
		||||
            return 2;
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(slotInfoData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        switch (slot) {
 | 
			
		||||
            case 0:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_INPUT, 51);
 | 
			
		||||
            case 1:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_MATERIAL, 52);
 | 
			
		||||
            case 2:
 | 
			
		||||
                return new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_RESULT, 50);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -65,5 +75,4 @@ public class SmithingInventoryTranslator extends BlockInventoryTranslator {
 | 
			
		|||
        }
 | 
			
		||||
        return super.javaSlotToBedrock(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,140 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.CraftResultsDeprecatedStackRequestActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.IntList;
 | 
			
		||||
import org.geysermc.connector.inventory.GeyserItemStack;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.inventory.PlayerInventory;
 | 
			
		||||
import org.geysermc.connector.inventory.StonecutterContainer;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.SlotType;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
 | 
			
		||||
 | 
			
		||||
public class StonecutterInventoryTranslator extends AbstractBlockInventoryTranslator {
 | 
			
		||||
    public StonecutterInventoryTranslator() {
 | 
			
		||||
        super(2, "minecraft:stonecutter[facing=north]", ContainerType.STONECUTTER, UIInventoryUpdater.INSTANCE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) {
 | 
			
		||||
        return action.getType() == StackRequestActionType.CRAFT_NON_IMPLEMENTED_DEPRECATED;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) {
 | 
			
		||||
        // TODO: Also surely to change in the future
 | 
			
		||||
        StackRequestActionData data = request.getActions()[1];
 | 
			
		||||
        if (!(data instanceof CraftResultsDeprecatedStackRequestActionData)) {
 | 
			
		||||
            return rejectRequest(request);
 | 
			
		||||
        }
 | 
			
		||||
        CraftResultsDeprecatedStackRequestActionData craftData = (CraftResultsDeprecatedStackRequestActionData) data;
 | 
			
		||||
        StonecutterContainer container = (StonecutterContainer) inventory;
 | 
			
		||||
        // Get the ID of the item we are cutting
 | 
			
		||||
        int id = inventory.getItem(0).getJavaId();
 | 
			
		||||
        // Look up all possible options of cutting from this ID
 | 
			
		||||
        IntList results = session.getStonecutterRecipes().get(id);
 | 
			
		||||
        if (results == null) {
 | 
			
		||||
            return rejectRequest(request);
 | 
			
		||||
        }
 | 
			
		||||
        System.out.println(id +  " " + results);
 | 
			
		||||
        ItemStack javaOutput = ItemTranslator.translateToJava(craftData.getResultItems()[0]);
 | 
			
		||||
        System.out.println(javaOutput);
 | 
			
		||||
        int button = results.indexOf(javaOutput.getId());
 | 
			
		||||
        // If we've already pressed the button with this item, no need to press it again!
 | 
			
		||||
        if (container.getStonecutterButton() != button) {
 | 
			
		||||
            // Getting the index of the item in the Java stonecutter list
 | 
			
		||||
            ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), button);
 | 
			
		||||
            System.out.println(packet.toString());
 | 
			
		||||
            session.sendDownstreamPacket(packet);
 | 
			
		||||
            container.setStonecutterButton(button);
 | 
			
		||||
            if (inventory.getItem(1).getJavaId() != javaOutput.getId()) {
 | 
			
		||||
                // We don't know there is an output here, so we tell ourselves that there is
 | 
			
		||||
                inventory.setItem(1, GeyserItemStack.from(javaOutput), session);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return translateRequest(session, inventory, request);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.STONECUTTER_INPUT) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.STONECUTTER_RESULT || slotInfoData.getContainer() == ContainerSlotType.CREATIVE_OUTPUT) {
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(slotInfoData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        if (slot == 0) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.STONECUTTER_INPUT, 3);
 | 
			
		||||
        }
 | 
			
		||||
        if (slot == 1) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.STONECUTTER_RESULT, 50);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int javaSlotToBedrock(int slot) {
 | 
			
		||||
        if (slot == 0) {
 | 
			
		||||
            return 3;
 | 
			
		||||
        }
 | 
			
		||||
        if (slot == 1) {
 | 
			
		||||
            return 50;
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrock(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public SlotType getSlotType(int javaSlot) {
 | 
			
		||||
        if (javaSlot == 1) {
 | 
			
		||||
            return SlotType.OUTPUT;
 | 
			
		||||
        }
 | 
			
		||||
        return super.getSlotType(javaSlot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) {
 | 
			
		||||
        return new StonecutterContainer(name, windowId, this.size, playerInventory);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -23,16 +23,15 @@
 | 
			
		|||
 * @link https://github.com/GeyserMC/Geyser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory;
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory.translators.chest;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.translators.BaseInventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
 | 
			
		||||
import org.geysermc.connector.utils.InventoryUtils;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public abstract class ChestInventoryTranslator extends BaseInventoryTranslator {
 | 
			
		||||
    private final InventoryUpdater updater;
 | 
			
		||||
| 
						 | 
				
			
			@ -53,17 +52,10 @@ public abstract class ChestInventoryTranslator extends BaseInventoryTranslator {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
 | 
			
		||||
        for (InventoryActionData action : actions) {
 | 
			
		||||
            if (action.getSource().getContainerId() == inventory.getId()) {
 | 
			
		||||
                if (action.getSlot() >= size) {
 | 
			
		||||
                    updateInventory(session, inventory);
 | 
			
		||||
                    InventoryUtils.updateCursor(session);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int javaSlot) {
 | 
			
		||||
        if (javaSlot < this.size) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.CONTAINER, javaSlot);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        super.translateActions(session, inventory, actions);
 | 
			
		||||
        return super.javaSlotToBedrockContainer(javaSlot);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +23,7 @@
 | 
			
		|||
 * @link https://github.com/GeyserMC/Geyser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory;
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory.translators.chest;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
 | 
			
		||||
import com.nukkitx.math.vector.Vector3i;
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +23,7 @@
 | 
			
		|||
 * @link https://github.com/GeyserMC/Geyser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory;
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory.translators.chest;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
| 
						 | 
				
			
			@ -23,18 +23,21 @@
 | 
			
		|||
 * @link https://github.com/GeyserMC/Geyser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory;
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory.translators.furnace;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.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.BedrockContainerSlot;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.SlotType;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.translators.AbstractBlockInventoryTranslator;
 | 
			
		||||
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());
 | 
			
		||||
public abstract class AbstractFurnaceInventoryTranslator extends AbstractBlockInventoryTranslator {
 | 
			
		||||
    AbstractFurnaceInventoryTranslator(String javaBlockIdentifier, ContainerType containerType) {
 | 
			
		||||
        super(3, javaBlockIdentifier, containerType, ContainerInventoryUpdater.INSTANCE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -50,9 +53,6 @@ public class FurnaceInventoryTranslator extends BlockInventoryTranslator {
 | 
			
		|||
                break;
 | 
			
		||||
            case 2:
 | 
			
		||||
                dataPacket.setProperty(ContainerSetDataPacket.FURNACE_TICK_COUNT);
 | 
			
		||||
                if (inventory.getWindowType() == WindowType.BLAST_FURNACE || inventory.getWindowType() == WindowType.SMOKER) {
 | 
			
		||||
                    value *= 2;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                return;
 | 
			
		||||
| 
						 | 
				
			
			@ -67,4 +67,15 @@ public class FurnaceInventoryTranslator extends BlockInventoryTranslator {
 | 
			
		|||
            return SlotType.FURNACE_OUTPUT;
 | 
			
		||||
        return SlotType.NORMAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        if (slot == 1) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.FURNACE_FUEL, javaSlotToBedrock(slot));
 | 
			
		||||
        }
 | 
			
		||||
        if (slot == 2) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.FURNACE_OUTPUT, javaSlotToBedrock(slot));
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators.furnace;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
 | 
			
		||||
public class BlastFurnaceInventoryTranslator extends AbstractFurnaceInventoryTranslator {
 | 
			
		||||
    public BlastFurnaceInventoryTranslator() {
 | 
			
		||||
        super("minecraft:blast_furnace[facing=north,lit=false]", ContainerType.BLAST_FURNACE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        if (slot == 0) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.BLAST_FURNACE_INGREDIENT, javaSlotToBedrock(slot));
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators.furnace;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
 | 
			
		||||
public class FurnaceInventoryTranslator extends AbstractFurnaceInventoryTranslator {
 | 
			
		||||
    public FurnaceInventoryTranslator() {
 | 
			
		||||
        super("minecraft:furnace[facing=north,lit=false]", ContainerType.FURNACE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        if (slot == 0) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.FURNACE_INGREDIENT, javaSlotToBedrock(slot));
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators.furnace;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
 | 
			
		||||
public class SmokerInventoryTranslator extends AbstractFurnaceInventoryTranslator {
 | 
			
		||||
    public SmokerInventoryTranslator() {
 | 
			
		||||
        super("minecraft:smoker[facing=north,lit=false]", ContainerType.SMOKER);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        if (slot == 0) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.SMOKER_INGREDIENT, javaSlotToBedrock(slot));
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -23,68 +23,20 @@
 | 
			
		|||
 * @link https://github.com/GeyserMC/Geyser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory;
 | 
			
		||||
package org.geysermc.connector.network.translators.inventory.translators.horse;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
 | 
			
		||||
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.translators.BaseInventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.HorseInventoryUpdater;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class MerchantInventoryTranslator extends BaseInventoryTranslator {
 | 
			
		||||
 | 
			
		||||
public abstract class AbstractHorseInventoryTranslator extends BaseInventoryTranslator {
 | 
			
		||||
    private final InventoryUpdater updater;
 | 
			
		||||
 | 
			
		||||
    public MerchantInventoryTranslator() {
 | 
			
		||||
        super(3);
 | 
			
		||||
        this.updater = new CursorInventoryUpdater();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int javaSlotToBedrock(int slot) {
 | 
			
		||||
        switch (slot) {
 | 
			
		||||
            case 0:
 | 
			
		||||
                return 4;
 | 
			
		||||
            case 1:
 | 
			
		||||
                return 5;
 | 
			
		||||
            case 2:
 | 
			
		||||
                return 50;
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrock(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(InventoryActionData action) {
 | 
			
		||||
        switch (action.getSource().getContainerId()) {
 | 
			
		||||
            case ContainerId.UI:
 | 
			
		||||
                switch (action.getSlot()) {
 | 
			
		||||
                    case 4:
 | 
			
		||||
                        return 0;
 | 
			
		||||
                    case 5:
 | 
			
		||||
                        return 1;
 | 
			
		||||
                    case 50:
 | 
			
		||||
                        return 2;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            case -28: // Trading 1?
 | 
			
		||||
                return 0;
 | 
			
		||||
            case -29: // Trading 2?
 | 
			
		||||
                return 1;
 | 
			
		||||
            case -30: // Trading Output?
 | 
			
		||||
                return 2;
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(action);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public SlotType getSlotType(int javaSlot) {
 | 
			
		||||
        if (javaSlot == 2) {
 | 
			
		||||
            return SlotType.OUTPUT;
 | 
			
		||||
        }
 | 
			
		||||
        return SlotType.NORMAL;
 | 
			
		||||
    public AbstractHorseInventoryTranslator(int size) {
 | 
			
		||||
        super(size);
 | 
			
		||||
        this.updater = HorseInventoryUpdater.INSTANCE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -99,8 +51,7 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator {
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void closeInventory(GeyserSession session, Inventory inventory) {
 | 
			
		||||
        session.setLastInteractedVillagerEid(-1);
 | 
			
		||||
        session.setVillagerTrades(null);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -112,13 +63,4 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator {
 | 
			
		|||
    public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
 | 
			
		||||
        updater.updateSlot(this, session, inventory, slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
 | 
			
		||||
        if (actions.stream().anyMatch(a -> a.getSource().getContainerId() == -31)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        super.translateActions(session, inventory, actions);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,113 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators.horse;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
 | 
			
		||||
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.inventory.BedrockContainerSlot;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
public abstract class ChestedHorseInventoryTranslator extends AbstractHorseInventoryTranslator {
 | 
			
		||||
    private final int chestSize;
 | 
			
		||||
    private final int equipSlot;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param size the total Java size of the inventory
 | 
			
		||||
     * @param equipSlot the Java equipment slot. Java always has two slots - one for armor and one for saddle. Chested horses
 | 
			
		||||
     *                  on Bedrock only acknowledge one slot.
 | 
			
		||||
     */
 | 
			
		||||
    public ChestedHorseInventoryTranslator(int size, int equipSlot) {
 | 
			
		||||
        super(size);
 | 
			
		||||
        this.chestSize = size - 2;
 | 
			
		||||
        this.equipSlot = equipSlot;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.HORSE_EQUIP) {
 | 
			
		||||
            return this.equipSlot;
 | 
			
		||||
        }
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.CONTAINER) {
 | 
			
		||||
            return slotInfoData.getSlot() + 1;
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(slotInfoData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        if (slot == this.equipSlot) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.HORSE_EQUIP, 0);
 | 
			
		||||
        }
 | 
			
		||||
        if (slot <= this.size - 1) { // Accommodate for the lack of one slot (saddle or armor)
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.CONTAINER, slot - 1);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int javaSlotToBedrock(int slot) {
 | 
			
		||||
        if (slot == 0 && this.equipSlot == 0) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (slot <= this.size - 1) {
 | 
			
		||||
            return slot - 1;
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrock(slot);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateInventory(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] = inventory.getItem(this.size + i + offset).getItemData(session);
 | 
			
		||||
        }
 | 
			
		||||
        InventoryContentPacket contentPacket = new InventoryContentPacket();
 | 
			
		||||
        contentPacket.setContainerId(ContainerId.INVENTORY);
 | 
			
		||||
        contentPacket.setContents(Arrays.asList(bedrockItems));
 | 
			
		||||
        session.sendUpstreamPacket(contentPacket);
 | 
			
		||||
 | 
			
		||||
        ItemData[] horseItems = new ItemData[chestSize + 1];
 | 
			
		||||
        // Manually specify the first slot - Java always has two slots (armor and saddle) and one is invisible.
 | 
			
		||||
        // Bedrock doesn't have this invisible slot.
 | 
			
		||||
        horseItems[0] = inventory.getItem(this.equipSlot).getItemData(session);
 | 
			
		||||
        for (int i = 1; i < horseItems.length; i++) {
 | 
			
		||||
            horseItems[i] = inventory.getItem(i + 1).getItemData(session);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        InventoryContentPacket horseContentsPacket = new InventoryContentPacket();
 | 
			
		||||
        horseContentsPacket.setContainerId(inventory.getId());
 | 
			
		||||
        horseContentsPacket.setContents(Arrays.asList(horseItems));
 | 
			
		||||
        System.out.println(horseContentsPacket);
 | 
			
		||||
        session.sendUpstreamPacket(horseContentsPacket);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators.horse;
 | 
			
		||||
 | 
			
		||||
public class DonkeyInventoryTranslator extends ChestedHorseInventoryTranslator {
 | 
			
		||||
    public DonkeyInventoryTranslator(int size) {
 | 
			
		||||
        super(size, 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,52 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators.horse;
 | 
			
		||||
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
 | 
			
		||||
 | 
			
		||||
public class HorseInventoryTranslator extends AbstractHorseInventoryTranslator {
 | 
			
		||||
    public HorseInventoryTranslator(int size) {
 | 
			
		||||
        super(size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
 | 
			
		||||
        if (slotInfoData.getContainer() == ContainerSlotType.HORSE_EQUIP) {
 | 
			
		||||
            return slotInfoData.getSlot();
 | 
			
		||||
        }
 | 
			
		||||
        return super.bedrockSlotToJava(slotInfoData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
 | 
			
		||||
        if (slot == 0 || slot == 1) {
 | 
			
		||||
            return new BedrockContainerSlot(ContainerSlotType.HORSE_EQUIP, slot);
 | 
			
		||||
        }
 | 
			
		||||
        return super.javaSlotToBedrockContainer(slot);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.translators.horse;
 | 
			
		||||
 | 
			
		||||
public class LlamaInventoryTranslator extends ChestedHorseInventoryTranslator {
 | 
			
		||||
    public LlamaInventoryTranslator(int size) {
 | 
			
		||||
        super(size, 1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +32,6 @@ import lombok.AllArgsConstructor;
 | 
			
		|||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
 | 
			
		||||
import org.geysermc.connector.utils.InventoryUtils;
 | 
			
		||||
import org.geysermc.connector.utils.LanguageUtils;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +51,7 @@ public class ChestInventoryUpdater extends InventoryUpdater {
 | 
			
		|||
        List<ItemData> bedrockItems = new ArrayList<>(paddedSize);
 | 
			
		||||
        for (int i = 0; i < paddedSize; i++) {
 | 
			
		||||
            if (i < translator.size) {
 | 
			
		||||
                bedrockItems.add(ItemTranslator.translateToBedrock(session, inventory.getItem(i)));
 | 
			
		||||
                bedrockItems.add(inventory.getItem(i).getItemData(session));
 | 
			
		||||
            } else {
 | 
			
		||||
                bedrockItems.add(UNUSUABLE_SPACE_BLOCK);
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +71,7 @@ public class ChestInventoryUpdater extends InventoryUpdater {
 | 
			
		|||
        InventorySlotPacket slotPacket = new InventorySlotPacket();
 | 
			
		||||
        slotPacket.setContainerId(inventory.getId());
 | 
			
		||||
        slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
 | 
			
		||||
        slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
 | 
			
		||||
        slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
 | 
			
		||||
        session.sendUpstreamPacket(slotPacket);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,18 +31,19 @@ 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.inventory.InventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
public class ContainerInventoryUpdater extends InventoryUpdater {
 | 
			
		||||
    public static final ContainerInventoryUpdater INSTANCE = new ContainerInventoryUpdater();
 | 
			
		||||
 | 
			
		||||
    @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)] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
 | 
			
		||||
            bedrockItems[translator.javaSlotToBedrock(i)] = inventory.getItem(i).getItemData(session);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        InventoryContentPacket contentPacket = new InventoryContentPacket();
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +60,7 @@ public class ContainerInventoryUpdater extends InventoryUpdater {
 | 
			
		|||
        InventorySlotPacket slotPacket = new InventorySlotPacket();
 | 
			
		||||
        slotPacket.setContainerId(inventory.getId());
 | 
			
		||||
        slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
 | 
			
		||||
        slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
 | 
			
		||||
        slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
 | 
			
		||||
        session.sendUpstreamPacket(slotPacket);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,69 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.inventory.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.inventory.InventoryTranslator;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
public class HorseInventoryUpdater extends InventoryUpdater {
 | 
			
		||||
    public static final HorseInventoryUpdater INSTANCE = new HorseInventoryUpdater();
 | 
			
		||||
 | 
			
		||||
    @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)] = inventory.getItem(i).getItemData(session);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        InventoryContentPacket contentPacket = new InventoryContentPacket();
 | 
			
		||||
        contentPacket.setContainerId(inventory.getId());
 | 
			
		||||
        contentPacket.setContents(Arrays.asList(bedrockItems));
 | 
			
		||||
        session.sendUpstreamPacket(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(4); // Horse GUI?
 | 
			
		||||
        slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
 | 
			
		||||
        slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
 | 
			
		||||
        session.sendUpstreamPacket(slotPacket);
 | 
			
		||||
        System.out.println(slotPacket);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +32,6 @@ 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.inventory.InventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +40,7 @@ public abstract class InventoryUpdater {
 | 
			
		|||
        ItemData[] bedrockItems = new ItemData[36];
 | 
			
		||||
        for (int i = 0; i < 36; i++) {
 | 
			
		||||
            final int offset = i < 9 ? 27 : -9;
 | 
			
		||||
            bedrockItems[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(translator.size + i + offset));
 | 
			
		||||
            bedrockItems[i] = inventory.getItem(translator.size + i + offset).getItemData(session);
 | 
			
		||||
        }
 | 
			
		||||
        InventoryContentPacket contentPacket = new InventoryContentPacket();
 | 
			
		||||
        contentPacket.setContainerId(ContainerId.INVENTORY);
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +53,7 @@ public abstract class InventoryUpdater {
 | 
			
		|||
            InventorySlotPacket slotPacket = new InventorySlotPacket();
 | 
			
		||||
            slotPacket.setContainerId(ContainerId.INVENTORY);
 | 
			
		||||
            slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
 | 
			
		||||
            slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
 | 
			
		||||
            slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
 | 
			
		||||
            session.sendUpstreamPacket(slotPacket);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,11 +30,10 @@ 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.inventory.InventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
 | 
			
		||||
 | 
			
		||||
public class CursorInventoryUpdater extends InventoryUpdater {
 | 
			
		||||
public class UIInventoryUpdater extends InventoryUpdater {
 | 
			
		||||
    public static final UIInventoryUpdater INSTANCE = new UIInventoryUpdater();
 | 
			
		||||
 | 
			
		||||
    //TODO: Consider renaming this? Since the Protocol enum updated
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
 | 
			
		||||
        super.updateInventory(translator, session, inventory);
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +45,7 @@ public class CursorInventoryUpdater extends InventoryUpdater {
 | 
			
		|||
            InventorySlotPacket slotPacket = new InventorySlotPacket();
 | 
			
		||||
            slotPacket.setContainerId(ContainerId.UI);
 | 
			
		||||
            slotPacket.setSlot(bedrockSlot);
 | 
			
		||||
            slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i)));
 | 
			
		||||
            slotPacket.setItem(inventory.getItem(i).getItemData(session));
 | 
			
		||||
            session.sendUpstreamPacket(slotPacket);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +58,7 @@ public class CursorInventoryUpdater extends InventoryUpdater {
 | 
			
		|||
        InventorySlotPacket slotPacket = new InventorySlotPacket();
 | 
			
		||||
        slotPacket.setContainerId(ContainerId.UI);
 | 
			
		||||
        slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
 | 
			
		||||
        slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
 | 
			
		||||
        slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
 | 
			
		||||
        session.sendUpstreamPacket(slotPacket);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ import lombok.ToString;
 | 
			
		|||
@ToString
 | 
			
		||||
public class ItemEntry {
 | 
			
		||||
 | 
			
		||||
    public static ItemEntry AIR = new ItemEntry("minecraft:air", "minecraft:air", 0, 0, 0, false);
 | 
			
		||||
    public static ItemEntry AIR = new ItemEntry("minecraft:air", "minecraft:air", 0, 0, 0, false, 64);
 | 
			
		||||
 | 
			
		||||
    private final String javaIdentifier;
 | 
			
		||||
    private final String bedrockIdentifier;
 | 
			
		||||
| 
						 | 
				
			
			@ -43,6 +43,7 @@ public class ItemEntry {
 | 
			
		|||
    private final int bedrockData;
 | 
			
		||||
 | 
			
		||||
    private final boolean block;
 | 
			
		||||
    private final int stackSize;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean equals(Object obj) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,7 +56,7 @@ public class ItemRegistry {
 | 
			
		|||
     * A list of all identifiers that only exist on Java. Used to prevent creative items from becoming these unintentionally.
 | 
			
		||||
     */
 | 
			
		||||
    private static final List<String> JAVA_ONLY_ITEMS = Arrays.asList("minecraft:spectral_arrow", "minecraft:debug_stick",
 | 
			
		||||
            "minecraft:knowledge_book");
 | 
			
		||||
            "minecraft:knowledge_book", "minecraft:tipped_arrow", "minecraft:furnace_minecart");
 | 
			
		||||
 | 
			
		||||
    public static final ItemData[] CREATIVE_ITEMS;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -147,6 +147,8 @@ public class ItemRegistry {
 | 
			
		|||
            if (bedrockIdentifier == null) {
 | 
			
		||||
                throw new RuntimeException("Missing Bedrock ID in mappings!: " + bedrockId);
 | 
			
		||||
            }
 | 
			
		||||
            JsonNode stackSizeNode = entry.getValue().get("stack_size");
 | 
			
		||||
            int stackSize = stackSizeNode == null ? 64 : stackSizeNode.intValue();
 | 
			
		||||
            if (entry.getValue().has("tool_type")) {
 | 
			
		||||
                if (entry.getValue().has("tool_tier")) {
 | 
			
		||||
                    ITEM_ENTRIES.put(itemIndex, new ToolItemEntry(
 | 
			
		||||
| 
						 | 
				
			
			@ -154,19 +156,22 @@ public class ItemRegistry {
 | 
			
		|||
                            entry.getValue().get("bedrock_data").intValue(),
 | 
			
		||||
                            entry.getValue().get("tool_type").textValue(),
 | 
			
		||||
                            entry.getValue().get("tool_tier").textValue(),
 | 
			
		||||
                            entry.getValue().get("is_block") != null && entry.getValue().get("is_block").booleanValue()));
 | 
			
		||||
                            entry.getValue().get("is_block").booleanValue(),
 | 
			
		||||
                            stackSize));
 | 
			
		||||
                } else {
 | 
			
		||||
                    ITEM_ENTRIES.put(itemIndex, new ToolItemEntry(
 | 
			
		||||
                            entry.getKey(), bedrockIdentifier, itemIndex, bedrockId,
 | 
			
		||||
                            entry.getValue().get("bedrock_data").intValue(),
 | 
			
		||||
                            entry.getValue().get("tool_type").textValue(),
 | 
			
		||||
                            "", entry.getValue().get("is_block").booleanValue()));
 | 
			
		||||
                            "", entry.getValue().get("is_block").booleanValue(),
 | 
			
		||||
                            stackSize));
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                ITEM_ENTRIES.put(itemIndex, new ItemEntry(
 | 
			
		||||
                        entry.getKey(), bedrockIdentifier, itemIndex, bedrockId,
 | 
			
		||||
                        entry.getValue().get("bedrock_data").intValue(),
 | 
			
		||||
                        entry.getValue().get("is_block") != null && entry.getValue().get("is_block").booleanValue()));
 | 
			
		||||
                        entry.getValue().get("is_block").booleanValue(),
 | 
			
		||||
                        stackSize));
 | 
			
		||||
            }
 | 
			
		||||
            switch (entry.getKey()) {
 | 
			
		||||
                case "minecraft:barrier":
 | 
			
		||||
| 
						 | 
				
			
			@ -209,7 +214,7 @@ public class ItemRegistry {
 | 
			
		|||
 | 
			
		||||
        // Add the loadstone compass since it doesn't exist on java but we need it for item conversion
 | 
			
		||||
        ITEM_ENTRIES.put(itemIndex, new ItemEntry("minecraft:lodestone_compass", "minecraft:lodestone_compass", itemIndex,
 | 
			
		||||
                lodestoneCompassId, 0, false));
 | 
			
		||||
                lodestoneCompassId, 0, false, 1));
 | 
			
		||||
 | 
			
		||||
        /* Load creative items */
 | 
			
		||||
        stream = FileUtils.getResource("bedrock/creative_items.json");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,13 +26,25 @@
 | 
			
		|||
package org.geysermc.connector.network.translators.item;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.databind.JsonNode;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
 | 
			
		||||
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.RecipeType;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData;
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData;
 | 
			
		||||
import com.nukkitx.nbt.NbtMap;
 | 
			
		||||
import com.nukkitx.nbt.NbtUtils;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.CraftingData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 | 
			
		||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
 | 
			
		||||
import org.geysermc.connector.GeyserConnector;
 | 
			
		||||
import org.geysermc.connector.utils.FileUtils;
 | 
			
		||||
import org.geysermc.connector.utils.LanguageUtils;
 | 
			
		||||
 | 
			
		||||
import java.io.ByteArrayInputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -47,6 +59,12 @@ public class RecipeRegistry {
 | 
			
		|||
     */
 | 
			
		||||
    public static int LAST_RECIPE_NET_ID = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A list of all the following crafting recipes, but in a format understood by Java servers.
 | 
			
		||||
     * Used for console autocrafting.
 | 
			
		||||
     */
 | 
			
		||||
    public static final Int2ObjectMap<Recipe> ALL_CRAFTING_RECIPES = new Int2ObjectOpenHashMap<>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A list of all possible leather armor dyeing recipes.
 | 
			
		||||
     * Created manually.
 | 
			
		||||
| 
						 | 
				
			
			@ -78,6 +96,12 @@ public class RecipeRegistry {
 | 
			
		|||
     */
 | 
			
		||||
    public static final List<CraftingData> TIPPED_ARROW_RECIPES = new ObjectArrayList<>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Recipe data that, when sent to the client, enables cartography features.
 | 
			
		||||
     * This does not have a Java equivalent.
 | 
			
		||||
     */
 | 
			
		||||
    public static final List<CraftingData> CARTOGRAPHY_RECIPE_DATA = new ObjectArrayList<>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Recipe data that, when sent to the client, enables book cloning
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -106,6 +130,11 @@ public class RecipeRegistry {
 | 
			
		|||
        MAP_EXTENDING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("d392b075-4ba1-40ae-8789-af868d56f6ce"), LAST_RECIPE_NET_ID++);
 | 
			
		||||
        MAP_CLONING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("85939755-ba10-4d9d-a4cc-efb7a8e943c4"), LAST_RECIPE_NET_ID++);
 | 
			
		||||
        BANNER_DUPLICATING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("b5c5d105-75a2-4076-af2b-923ea2bf4bf0"), LAST_RECIPE_NET_ID++);
 | 
			
		||||
 | 
			
		||||
        CARTOGRAPHY_RECIPE_DATA.add(CraftingData.fromMulti(UUID.fromString("8b36268c-1829-483c-a0f1-993b7156a8f2"), LAST_RECIPE_NET_ID++)); // Map extending
 | 
			
		||||
        CARTOGRAPHY_RECIPE_DATA.add(CraftingData.fromMulti(UUID.fromString("442d85ed-8272-4543-a6f1-418f90ded05d"), LAST_RECIPE_NET_ID++)); // Map cloning
 | 
			
		||||
        CARTOGRAPHY_RECIPE_DATA.add(CraftingData.fromMulti(UUID.fromString("98c84b38-1085-46bd-b1ce-dd38c159e6cc"), LAST_RECIPE_NET_ID++)); // Map upgrading
 | 
			
		||||
        CARTOGRAPHY_RECIPE_DATA.add(CraftingData.fromMulti(UUID.fromString("602234e4-cac1-4353-8bb7-b1ebff70024b"), LAST_RECIPE_NET_ID++)); // Map locking
 | 
			
		||||
        // https://github.com/pmmp/PocketMine-MP/blob/stable/src/pocketmine/inventory/MultiRecipe.php
 | 
			
		||||
 | 
			
		||||
        // Get all recipes that are not directly sent from a Java server
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +147,7 @@ public class RecipeRegistry {
 | 
			
		|||
            throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (JsonNode entry: items.get("leather_armor")) {
 | 
			
		||||
        for (JsonNode entry : items.get("leather_armor")) {
 | 
			
		||||
            // This won't be perfect, as we can't possibly send every leather input for every kind of color
 | 
			
		||||
            // But it does display the correct output from a base leather armor, and besides visuals everything works fine
 | 
			
		||||
            LEATHER_DYEING_RECIPES.add(getCraftingDataFromJsonNode(entry));
 | 
			
		||||
| 
						 | 
				
			
			@ -146,9 +175,13 @@ public class RecipeRegistry {
 | 
			
		|||
     * @return the {@link CraftingData} to send to the Bedrock client.
 | 
			
		||||
     */
 | 
			
		||||
    private static CraftingData getCraftingDataFromJsonNode(JsonNode node) {
 | 
			
		||||
        ItemData output = ItemRegistry.getBedrockItemFromJson(node.get("output").get(0));
 | 
			
		||||
        int netId = LAST_RECIPE_NET_ID++;
 | 
			
		||||
        int type = node.get("bedrockRecipeType").asInt();
 | 
			
		||||
        JsonNode outputNode = node.get("output");
 | 
			
		||||
        ItemEntry outputEntry = ItemRegistry.getItemEntry(outputNode.get("identifier").asText());
 | 
			
		||||
        ItemData output = getBedrockItemFromIdentifierJson(outputEntry, outputNode);
 | 
			
		||||
        UUID uuid = UUID.randomUUID();
 | 
			
		||||
        if (node.get("type").asInt() == 1) {
 | 
			
		||||
        if (type == 1) {
 | 
			
		||||
            // Shaped recipe
 | 
			
		||||
            List<String> shape = new ArrayList<>();
 | 
			
		||||
            // Get the shape of the recipe
 | 
			
		||||
| 
						 | 
				
			
			@ -158,10 +191,12 @@ public class RecipeRegistry {
 | 
			
		|||
 | 
			
		||||
            // In recipes.json each recipe is mapped by a letter
 | 
			
		||||
            Map<String, ItemData> letterToRecipe = new HashMap<>();
 | 
			
		||||
            Iterator<Map.Entry<String, JsonNode>> iterator = node.get("input").fields();
 | 
			
		||||
            Iterator<Map.Entry<String, JsonNode>> iterator = node.get("inputs").fields();
 | 
			
		||||
            while (iterator.hasNext()) {
 | 
			
		||||
                Map.Entry<String, JsonNode> entry = iterator.next();
 | 
			
		||||
                letterToRecipe.put(entry.getKey(), ItemRegistry.getBedrockItemFromJson(entry.getValue()));
 | 
			
		||||
                JsonNode inputNode = entry.getValue();
 | 
			
		||||
                ItemEntry inputEntry = ItemRegistry.getItemEntry(inputNode.get("identifier").asText());
 | 
			
		||||
                letterToRecipe.put(entry.getKey(), getBedrockItemFromIdentifierJson(inputEntry, inputNode));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            List<ItemData> inputs = new ArrayList<>(shape.size() * shape.get(0).length());
 | 
			
		||||
| 
						 | 
				
			
			@ -175,20 +210,69 @@ public class RecipeRegistry {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* Convert into a Java recipe class for autocrafting */
 | 
			
		||||
            List<Ingredient> ingredients = new ArrayList<>();
 | 
			
		||||
            for (ItemData input : inputs) {
 | 
			
		||||
                ingredients.add(new Ingredient(new ItemStack[]{ItemTranslator.translateToJava(input)}));
 | 
			
		||||
            }
 | 
			
		||||
            ShapedRecipeData data = new ShapedRecipeData(shape.get(0).length(), shape.size(), "crafting_table",
 | 
			
		||||
                    ingredients.toArray(new Ingredient[0]), ItemTranslator.translateToJava(output));
 | 
			
		||||
            Recipe recipe = new Recipe(RecipeType.CRAFTING_SHAPED, "", data);
 | 
			
		||||
            ALL_CRAFTING_RECIPES.put(netId, recipe);
 | 
			
		||||
            /* Convert end */
 | 
			
		||||
 | 
			
		||||
            return CraftingData.fromShaped(uuid.toString(), shape.get(0).length(), shape.size(),
 | 
			
		||||
                    inputs, Collections.singletonList(output), uuid, "crafting_table", 0, LAST_RECIPE_NET_ID++);
 | 
			
		||||
                    inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId);
 | 
			
		||||
        }
 | 
			
		||||
        List<ItemData> inputs = new ObjectArrayList<>();
 | 
			
		||||
        for (JsonNode entry : node.get("input")) {
 | 
			
		||||
            inputs.add(ItemRegistry.getBedrockItemFromJson(entry));
 | 
			
		||||
        for (JsonNode entry : node.get("inputs")) {
 | 
			
		||||
            ItemEntry inputEntry = ItemRegistry.getItemEntry(entry.get("identifier").asText());
 | 
			
		||||
            inputs.add(getBedrockItemFromIdentifierJson(inputEntry, entry));
 | 
			
		||||
        }
 | 
			
		||||
        if (node.get("type").asInt() == 5) {
 | 
			
		||||
 | 
			
		||||
        /* Convert into a Java Recipe class for autocrafting */
 | 
			
		||||
        List<Ingredient> ingredients = new ArrayList<>();
 | 
			
		||||
        for (ItemData input : inputs) {
 | 
			
		||||
            ingredients.add(new Ingredient(new ItemStack[]{ItemTranslator.translateToJava(input)}));
 | 
			
		||||
        }
 | 
			
		||||
        ShapelessRecipeData data = new ShapelessRecipeData("crafting_table",
 | 
			
		||||
                ingredients.toArray(new Ingredient[0]), ItemTranslator.translateToJava(output));
 | 
			
		||||
        Recipe recipe = new Recipe(RecipeType.CRAFTING_SHAPELESS, "", data);
 | 
			
		||||
        ALL_CRAFTING_RECIPES.put(netId, recipe);
 | 
			
		||||
        /* Convert end */
 | 
			
		||||
 | 
			
		||||
        if (type == 5) {
 | 
			
		||||
            // Shulker box
 | 
			
		||||
            return CraftingData.fromShulkerBox(uuid.toString(),
 | 
			
		||||
                    inputs, Collections.singletonList(output), uuid, "crafting_table", 0, LAST_RECIPE_NET_ID++);
 | 
			
		||||
                    inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId);
 | 
			
		||||
        }
 | 
			
		||||
        return CraftingData.fromShapeless(uuid.toString(),
 | 
			
		||||
                inputs, Collections.singletonList(output), uuid, "crafting_table", 0, LAST_RECIPE_NET_ID++);
 | 
			
		||||
                inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ItemData getBedrockItemFromIdentifierJson(ItemEntry itemEntry, JsonNode itemNode) {
 | 
			
		||||
        int count = 1;
 | 
			
		||||
        short damage = 0;
 | 
			
		||||
        NbtMap tag = null;
 | 
			
		||||
        JsonNode damageNode = itemNode.get("bedrockDamage");
 | 
			
		||||
        if (damageNode != null) {
 | 
			
		||||
            damage = damageNode.numberValue().shortValue();
 | 
			
		||||
        }
 | 
			
		||||
        JsonNode countNode = itemNode.get("count");
 | 
			
		||||
        if (countNode != null) {
 | 
			
		||||
            count = countNode.asInt();
 | 
			
		||||
        }
 | 
			
		||||
        JsonNode nbtNode = itemNode.get("bedrockNbt");
 | 
			
		||||
        if (nbtNode != null) {
 | 
			
		||||
            byte[] bytes = Base64.getDecoder().decode(nbtNode.asText());
 | 
			
		||||
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
 | 
			
		||||
            try {
 | 
			
		||||
                tag = (NbtMap) NbtUtils.createReaderLE(bais).readTag();
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                e.printStackTrace();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return ItemData.of(itemEntry.getBedrockId(), damage, count, tag);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void init() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,8 +32,8 @@ public class ToolItemEntry extends ItemEntry {
 | 
			
		|||
    private final String toolType;
 | 
			
		||||
    private final String toolTier;
 | 
			
		||||
 | 
			
		||||
    public ToolItemEntry(String javaIdentifier, String bedrockIdentifier, int javaId, int bedrockId, int bedrockData, String toolType, String toolTier, boolean isBlock) {
 | 
			
		||||
        super(javaIdentifier, bedrockIdentifier, javaId, bedrockId, bedrockData, isBlock);
 | 
			
		||||
    public ToolItemEntry(String javaIdentifier, String bedrockIdentifier, int javaId, int bedrockId, int bedrockData, String toolType, String toolTier, boolean isBlock, int stackSize) {
 | 
			
		||||
        super(javaIdentifier, bedrockIdentifier, javaId, bedrockId, bedrockData, isBlock, stackSize);
 | 
			
		||||
        this.toolType = toolType;
 | 
			
		||||
        this.toolTier = toolTier;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -195,13 +195,14 @@ public class BannerTranslator extends ItemTranslator {
 | 
			
		|||
            blockEntityTag.put(OMINOUS_BANNER_PATTERN);
 | 
			
		||||
 | 
			
		||||
            itemStack.getNbt().put(blockEntityTag);
 | 
			
		||||
        } else if (nbtTag.containsKey("Patterns", NbtType.COMPOUND)) {
 | 
			
		||||
        } else if (nbtTag.containsKey("Patterns", NbtType.LIST)) {
 | 
			
		||||
            List<NbtMap> patterns = nbtTag.getList("Patterns", NbtType.COMPOUND);
 | 
			
		||||
 | 
			
		||||
            CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
 | 
			
		||||
            blockEntityTag.put(convertBannerPattern(patterns));
 | 
			
		||||
 | 
			
		||||
            itemStack.getNbt().put(blockEntityTag);
 | 
			
		||||
            itemStack.getNbt().remove("Patterns"); // Remove the old Bedrock patterns list
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return itemStack;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,17 +25,18 @@
 | 
			
		|||
 | 
			
		||||
package org.geysermc.connector.network.translators.java;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
 | 
			
		||||
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.data.game.recipe.data.StoneCuttingRecipeData;
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerDeclareRecipesPacket;
 | 
			
		||||
import com.nukkitx.nbt.NbtMap;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.CraftingData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.IntSet;
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.*;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
| 
						 | 
				
			
			@ -46,13 +47,20 @@ import org.geysermc.connector.network.translators.item.*;
 | 
			
		|||
import java.util.*;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Used to send all valid recipes from Java to Bedrock.
 | 
			
		||||
 *
 | 
			
		||||
 * Bedrock REQUIRES a CraftingDataPacket to be sent in order to craft anything.
 | 
			
		||||
 */
 | 
			
		||||
@Translator(packet = ServerDeclareRecipesPacket.class)
 | 
			
		||||
public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclareRecipesPacket> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translate(ServerDeclareRecipesPacket packet, GeyserSession session) {
 | 
			
		||||
        // Get the last known network ID (first used for the pregenerated recipes) and increment from there.
 | 
			
		||||
        int networkId = RecipeRegistry.LAST_RECIPE_NET_ID;
 | 
			
		||||
        int netId = RecipeRegistry.LAST_RECIPE_NET_ID + 1;
 | 
			
		||||
        Int2ObjectMap<Recipe> recipeMap = new Int2ObjectOpenHashMap<>(RecipeRegistry.ALL_CRAFTING_RECIPES);
 | 
			
		||||
        Int2ObjectMap<List<StoneCuttingRecipeData>> unsortedStonecutterData = new Int2ObjectOpenHashMap<>();
 | 
			
		||||
        CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
 | 
			
		||||
        craftingDataPacket.setCleanRecipes(true);
 | 
			
		||||
        for (Recipe recipe : packet.getRecipes()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +73,8 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
 | 
			
		|||
                    for (ItemData[] inputs : inputCombinations) {
 | 
			
		||||
                        UUID uuid = UUID.randomUUID();
 | 
			
		||||
                        craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
 | 
			
		||||
                                Arrays.asList(inputs), Collections.singletonList(output), uuid, "crafting_table", 0, networkId++));
 | 
			
		||||
                                Arrays.asList(inputs), Collections.singletonList(output), uuid, "crafting_table", 0, netId));
 | 
			
		||||
                        recipeMap.put(netId++, recipe);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +87,8 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
 | 
			
		|||
                        UUID uuid = UUID.randomUUID();
 | 
			
		||||
                        craftingDataPacket.getCraftingData().add(CraftingData.fromShaped(uuid.toString(),
 | 
			
		||||
                                shapedRecipeData.getWidth(), shapedRecipeData.getHeight(), Arrays.asList(inputs),
 | 
			
		||||
                                Collections.singletonList(output), uuid, "crafting_table", 0, networkId++));
 | 
			
		||||
                                        Collections.singletonList(output), uuid, "crafting_table", 0, netId));
 | 
			
		||||
                        recipeMap.put(netId++, recipe);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -131,10 +141,55 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
 | 
			
		|||
                    craftingDataPacket.getCraftingData().addAll(RecipeRegistry.LEATHER_DYEING_RECIPES);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case STONECUTTING: {
 | 
			
		||||
                    StoneCuttingRecipeData stoneCuttingData = (StoneCuttingRecipeData) recipe.getData();
 | 
			
		||||
                    ItemStack ingredient = stoneCuttingData.getIngredient().getOptions()[0];
 | 
			
		||||
                    List<StoneCuttingRecipeData> data = unsortedStonecutterData.get(ingredient.getId());
 | 
			
		||||
                    if (data == null) {
 | 
			
		||||
                        data = new ArrayList<>();
 | 
			
		||||
                        unsortedStonecutterData.put(ingredient.getId(), data);
 | 
			
		||||
                    }
 | 
			
		||||
                    data.add(stoneCuttingData);
 | 
			
		||||
                    // Save for processing after all recipes have been received
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // Add all cartography table recipe UUIDs, so we can use the cartography table
 | 
			
		||||
        craftingDataPacket.getCraftingData().addAll(RecipeRegistry.CARTOGRAPHY_RECIPE_DATA);
 | 
			
		||||
 | 
			
		||||
        craftingDataPacket.getPotionMixData().addAll(PotionMixRegistry.POTION_MIXES);
 | 
			
		||||
 | 
			
		||||
        Int2ObjectMap<IntList> stonecutterRecipeMap = new Int2ObjectOpenHashMap<>();
 | 
			
		||||
        for (Int2ObjectMap.Entry<List<StoneCuttingRecipeData>> data : unsortedStonecutterData.int2ObjectEntrySet()) {
 | 
			
		||||
            data.getValue().sort(Comparator.comparing((stoneCuttingRecipeData ->
 | 
			
		||||
                    // Sort the list by each output item's Java identifier - this is how it's sorted on Java, and therefore
 | 
			
		||||
                    // We can get the correct order for button pressing
 | 
			
		||||
                    ItemRegistry.getItem(stoneCuttingRecipeData.getResult()).getJavaIdentifier())));
 | 
			
		||||
            // Now that it's sorted, let's translate these recipes
 | 
			
		||||
            for (StoneCuttingRecipeData stoneCuttingData : data.getValue()) {
 | 
			
		||||
                // As of 1.16.4, all stonecutter recipes have one ingredient option
 | 
			
		||||
                ItemStack ingredient = stoneCuttingData.getIngredient().getOptions()[0];
 | 
			
		||||
                ItemData input = ItemTranslator.translateToBedrock(session, ingredient);
 | 
			
		||||
                ItemData output = ItemTranslator.translateToBedrock(session, stoneCuttingData.getResult());
 | 
			
		||||
                UUID uuid = UUID.randomUUID();
 | 
			
		||||
                // We need to register stonecutting recipes so they show up on Bedrock
 | 
			
		||||
                craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
 | 
			
		||||
                        Collections.singletonList(input), Collections.singletonList(output), uuid, "stonecutter", 0, netId++));
 | 
			
		||||
                // Save the recipe list for reference when crafting
 | 
			
		||||
                IntList outputs = stonecutterRecipeMap.get(ingredient.getId());
 | 
			
		||||
                if (outputs == null) {
 | 
			
		||||
                    outputs = new IntArrayList();
 | 
			
		||||
                    // Add the ingredient as the key and all possible values as the value
 | 
			
		||||
                    stonecutterRecipeMap.put(ingredient.getId(), outputs);
 | 
			
		||||
                }
 | 
			
		||||
                outputs.add(stoneCuttingData.getResult().getId());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        session.sendUpstreamPacket(craftingDataPacket);
 | 
			
		||||
        session.setCraftingRecipes(recipeMap);
 | 
			
		||||
        session.getUnlockedRecipes().clear();
 | 
			
		||||
        session.setStonecutterRecipes(stonecutterRecipeMap);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //TODO: rewrite
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ 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);
 | 
			
		||||
        session.setOpenInventory(null);
 | 
			
		||||
 | 
			
		||||
        SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
 | 
			
		||||
        playerGameTypePacket.setGamemode(packet.getGamemode().ordinal());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.UnlockRecipesAction;
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerUnlockRecipesPacket;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.PacketTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.Translator;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Used to list recipes that we can definitely use the recipe book for (and therefore save on packet usage)
 | 
			
		||||
 */
 | 
			
		||||
@Translator(packet = ServerUnlockRecipesPacket.class)
 | 
			
		||||
public class JavaUnlockRecipesTranslator extends PacketTranslator<ServerUnlockRecipesPacket> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translate(ServerUnlockRecipesPacket packet, GeyserSession session) {
 | 
			
		||||
        if (packet.getAction() == UnlockRecipesAction.REMOVE) {
 | 
			
		||||
            session.getUnlockedRecipes().removeAll(Arrays.asList(packet.getRecipes()));
 | 
			
		||||
        } else {
 | 
			
		||||
            session.getUnlockedRecipes().addAll(Arrays.asList(packet.getRecipes()));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +32,7 @@ import com.github.steveice10.opennbt.tag.builtin.*;
 | 
			
		|||
import com.nukkitx.math.vector.Vector3f;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.LevelEventType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
 | 
			
		||||
import org.geysermc.connector.inventory.GeyserItemStack;
 | 
			
		||||
import org.geysermc.connector.inventory.PlayerInventory;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.PacketTranslator;
 | 
			
		||||
| 
						 | 
				
			
			@ -71,15 +72,9 @@ public class JavaPlayerActionAckTranslator extends PacketTranslator<ServerPlayer
 | 
			
		|||
                        packet.getPosition().getY(),
 | 
			
		||||
                        packet.getPosition().getZ()
 | 
			
		||||
                ));
 | 
			
		||||
                PlayerInventory inventory = session.getInventory();
 | 
			
		||||
                ItemStack item = inventory.getItemInHand();
 | 
			
		||||
                ItemEntry itemEntry = null;
 | 
			
		||||
                CompoundTag nbtData = new CompoundTag("");
 | 
			
		||||
                if (item != null) {
 | 
			
		||||
                    itemEntry = ItemRegistry.getItem(item);
 | 
			
		||||
                    nbtData = item.getNbt();
 | 
			
		||||
                }
 | 
			
		||||
                double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, packet.getNewState(), itemEntry, nbtData, session) * 20);
 | 
			
		||||
                PlayerInventory inventory = session.getPlayerInventory();
 | 
			
		||||
                GeyserItemStack item = inventory.getItemInHand();
 | 
			
		||||
                double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, packet.getNewState(), item.getItemEntry(), item.getNbt(), session) * 20);
 | 
			
		||||
                levelEvent.setData((int) (65535 / breakTime));
 | 
			
		||||
                session.setBreakingBlock(packet.getNewState());
 | 
			
		||||
                session.sendUpstreamPacket(levelEvent);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,12 +36,14 @@ public class JavaPlayerChangeHeldItemTranslator extends PacketTranslator<ServerP
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translate(ServerPlayerChangeHeldItemPacket packet, GeyserSession session) {
 | 
			
		||||
        PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket();
 | 
			
		||||
        hotbarPacket.setContainerId(0);
 | 
			
		||||
        hotbarPacket.setSelectedHotbarSlot(packet.getSlot());
 | 
			
		||||
        hotbarPacket.setSelectHotbarSlot(true);
 | 
			
		||||
        session.sendUpstreamPacket(hotbarPacket);
 | 
			
		||||
        session.addInventoryTask(() -> {
 | 
			
		||||
            PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket();
 | 
			
		||||
            hotbarPacket.setContainerId(0);
 | 
			
		||||
            hotbarPacket.setSelectedHotbarSlot(packet.getSlot());
 | 
			
		||||
            hotbarPacket.setSelectHotbarSlot(true);
 | 
			
		||||
            session.sendUpstreamPacket(hotbarPacket);
 | 
			
		||||
 | 
			
		||||
        session.getInventory().setHeldItemSlot(packet.getSlot());
 | 
			
		||||
            session.getPlayerInventory().setHeldItemSlot(packet.getSlot());
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,7 +36,6 @@ public class JavaCloseWindowTranslator extends PacketTranslator<ServerCloseWindo
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translate(ServerCloseWindowPacket packet, GeyserSession session) {
 | 
			
		||||
        InventoryUtils.closeWindow(session, packet.getWindowId());
 | 
			
		||||
        InventoryUtils.closeInventory(session, packet.getWindowId());
 | 
			
		||||
        session.addInventoryTask(() -> InventoryUtils.closeInventory(session, packet.getWindowId()));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ 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.Translator;
 | 
			
		||||
| 
						 | 
				
			
			@ -36,9 +37,12 @@ public class JavaConfirmTransactionTranslator extends PacketTranslator<ServerCon
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translate(ServerConfirmTransactionPacket packet, GeyserSession session) {
 | 
			
		||||
        if (!packet.isAccepted()) {
 | 
			
		||||
            ClientConfirmTransactionPacket confirmPacket = new ClientConfirmTransactionPacket(packet.getWindowId(), packet.getActionId(), true);
 | 
			
		||||
            session.sendDownstreamPacket(confirmPacket);
 | 
			
		||||
        }
 | 
			
		||||
        session.addInventoryTask(() -> {
 | 
			
		||||
            if (!packet.isAccepted()) {
 | 
			
		||||
                ClientConfirmTransactionPacket confirmPacket = new ClientConfirmTransactionPacket(packet.getWindowId(), packet.getActionId(), true);
 | 
			
		||||
                session.sendDownstreamPacket(confirmPacket);
 | 
			
		||||
                System.out.println(packet);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,139 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019-2021 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.ServerOpenHorseWindowPacket;
 | 
			
		||||
import com.nukkitx.nbt.NbtMap;
 | 
			
		||||
import com.nukkitx.nbt.NbtMapBuilder;
 | 
			
		||||
import com.nukkitx.nbt.NbtType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
 | 
			
		||||
import com.nukkitx.protocol.bedrock.packet.UpdateEquipPacket;
 | 
			
		||||
import org.geysermc.connector.entity.Entity;
 | 
			
		||||
import org.geysermc.connector.entity.living.animal.horse.ChestedHorseEntity;
 | 
			
		||||
import org.geysermc.connector.entity.living.animal.horse.LlamaEntity;
 | 
			
		||||
import org.geysermc.connector.inventory.Container;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.PacketTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.Translator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.translators.horse.DonkeyInventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.translators.horse.HorseInventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.translators.horse.LlamaInventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.utils.InventoryUtils;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@Translator(packet = ServerOpenHorseWindowPacket.class)
 | 
			
		||||
public class JavaOpenHorseWindowTranslator extends PacketTranslator<ServerOpenHorseWindowPacket> {
 | 
			
		||||
 | 
			
		||||
    private static final NbtMap ARMOR_SLOT;
 | 
			
		||||
    private static final NbtMap CARPET_SLOT;
 | 
			
		||||
    private static final NbtMap SADDLE_SLOT;
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        // Build the NBT mappings that Bedrock wants to lay out the GUI
 | 
			
		||||
        String[] acceptedHorseArmorIdentifiers = new String[] {"minecraft:horsearmorleather", "minecraft:horsearmoriron",
 | 
			
		||||
                "minecraft:horsearmorgold", "minecraft:horsearmordiamond"};
 | 
			
		||||
        NbtMapBuilder armorBuilder = NbtMap.builder();
 | 
			
		||||
        List<NbtMap> acceptedArmors = new ArrayList<>();
 | 
			
		||||
        for (String identifier : acceptedHorseArmorIdentifiers) {
 | 
			
		||||
            NbtMapBuilder acceptedItemBuilder = NbtMap.builder()
 | 
			
		||||
                    .putShort("Aux", Short.MAX_VALUE)
 | 
			
		||||
                    .putString("Name", identifier);
 | 
			
		||||
            acceptedArmors.add(NbtMap.builder().putCompound("slotItem", acceptedItemBuilder.build()).build());
 | 
			
		||||
        }
 | 
			
		||||
        armorBuilder.putList("acceptedItems", NbtType.COMPOUND, acceptedArmors);
 | 
			
		||||
        NbtMapBuilder armorItem = NbtMap.builder()
 | 
			
		||||
                .putShort("Aux", Short.MAX_VALUE)
 | 
			
		||||
                .putString("Name", "minecraft:horsearmoriron");
 | 
			
		||||
        armorBuilder.putCompound("item", armorItem.build());
 | 
			
		||||
        armorBuilder.putInt("slotNumber", 1);
 | 
			
		||||
        ARMOR_SLOT = armorBuilder.build();
 | 
			
		||||
 | 
			
		||||
        NbtMapBuilder carpetBuilder = NbtMap.builder();
 | 
			
		||||
        NbtMapBuilder carpetItem = NbtMap.builder()
 | 
			
		||||
                .putShort("Aux", Short.MAX_VALUE)
 | 
			
		||||
                .putString("Name", "minecraft:carpet");
 | 
			
		||||
        List<NbtMap> acceptedCarpet = Collections.singletonList(NbtMap.builder().putCompound("slotItem", carpetItem.build()).build());
 | 
			
		||||
        carpetBuilder.putList("acceptedItems", NbtType.COMPOUND, acceptedCarpet);
 | 
			
		||||
        carpetBuilder.putCompound("item", carpetItem.build());
 | 
			
		||||
        carpetBuilder.putInt("slotNumber", 1);
 | 
			
		||||
        CARPET_SLOT = carpetBuilder.build();
 | 
			
		||||
 | 
			
		||||
        NbtMapBuilder saddleBuilder = NbtMap.builder();
 | 
			
		||||
        NbtMapBuilder acceptedSaddle = NbtMap.builder()
 | 
			
		||||
                .putShort("Aux", Short.MAX_VALUE)
 | 
			
		||||
                .putString("Name", "minecraft:saddle");
 | 
			
		||||
        List<NbtMap> acceptedItem = Collections.singletonList(NbtMap.builder().putCompound("slotItem", acceptedSaddle.build()).build());
 | 
			
		||||
        saddleBuilder.putList("acceptedItems", NbtType.COMPOUND, acceptedItem);
 | 
			
		||||
        saddleBuilder.putCompound("item", acceptedSaddle.build());
 | 
			
		||||
        saddleBuilder.putInt("slotNumber", 0);
 | 
			
		||||
        SADDLE_SLOT = saddleBuilder.build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translate(ServerOpenHorseWindowPacket packet, GeyserSession session) {
 | 
			
		||||
        System.out.println(packet.toString());
 | 
			
		||||
        Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
 | 
			
		||||
        if (entity == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        UpdateEquipPacket updateEquipPacket = new UpdateEquipPacket();
 | 
			
		||||
        updateEquipPacket.setWindowId((short) packet.getWindowId());
 | 
			
		||||
        updateEquipPacket.setWindowType((short) ContainerType.HORSE.getId());
 | 
			
		||||
        updateEquipPacket.setUniqueEntityId(entity.getGeyserId());
 | 
			
		||||
 | 
			
		||||
        NbtMapBuilder builder = NbtMap.builder();
 | 
			
		||||
        List<NbtMap> slots = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        InventoryTranslator inventoryTranslator;
 | 
			
		||||
        if (entity instanceof LlamaEntity) {
 | 
			
		||||
            inventoryTranslator = new LlamaInventoryTranslator(packet.getNumberOfSlots());
 | 
			
		||||
            slots.add(CARPET_SLOT);
 | 
			
		||||
        } else if (entity instanceof ChestedHorseEntity) {
 | 
			
		||||
            inventoryTranslator = new DonkeyInventoryTranslator(packet.getNumberOfSlots());
 | 
			
		||||
            slots.add(SADDLE_SLOT);
 | 
			
		||||
        } else {
 | 
			
		||||
            inventoryTranslator = new HorseInventoryTranslator(packet.getNumberOfSlots());
 | 
			
		||||
            slots.add(SADDLE_SLOT);
 | 
			
		||||
            slots.add(ARMOR_SLOT);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Build the NbtMap that sets the icons for Bedrock (e.g. sets the saddle outline on the saddle slot)
 | 
			
		||||
        builder.putList("slots", NbtType.COMPOUND, slots);
 | 
			
		||||
 | 
			
		||||
        updateEquipPacket.setTag(builder.build());
 | 
			
		||||
        System.out.println(updateEquipPacket);
 | 
			
		||||
        session.sendUpstreamPacket(updateEquipPacket);
 | 
			
		||||
 | 
			
		||||
        session.setInventoryTranslator(inventoryTranslator);
 | 
			
		||||
        InventoryUtils.openInventory(session, new Container(entity.getMetadata().getString(EntityData.NAMETAG), packet.getWindowId(), packet.getNumberOfSlots(), session.getPlayerInventory()));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -41,35 +41,36 @@ public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowP
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translate(ServerOpenWindowPacket packet, GeyserSession session) {
 | 
			
		||||
        if (packet.getWindowId() == 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        InventoryTranslator newTranslator = InventoryTranslator.INVENTORY_TRANSLATORS.get(packet.getType());
 | 
			
		||||
        Inventory openInventory = session.getInventoryCache().getOpenInventory();
 | 
			
		||||
        if (newTranslator == null) {
 | 
			
		||||
        session.addInventoryTask(() -> {
 | 
			
		||||
            if (packet.getWindowId() == 0) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            InventoryTranslator newTranslator = InventoryTranslator.INVENTORY_TRANSLATORS.get(packet.getType());
 | 
			
		||||
            Inventory openInventory = session.getOpenInventory();
 | 
			
		||||
            //No translator exists for this window type. Close all windows and return.
 | 
			
		||||
            if (newTranslator == null) {
 | 
			
		||||
                if (openInventory != null) {
 | 
			
		||||
                    InventoryUtils.closeInventory(session, openInventory.getId());
 | 
			
		||||
                }
 | 
			
		||||
                ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId());
 | 
			
		||||
                session.sendDownstreamPacket(closeWindowPacket);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            String name = MessageTranslator.convertMessageLenient(packet.getName(), session.getLocale());
 | 
			
		||||
            name = LocaleUtils.getLocaleString(name, session.getLocale());
 | 
			
		||||
 | 
			
		||||
            Inventory newInventory = newTranslator.createInventory(name, packet.getWindowId(), packet.getType(), session.getPlayerInventory());
 | 
			
		||||
            if (openInventory != null) {
 | 
			
		||||
                InventoryUtils.closeWindow(session, openInventory.getId());
 | 
			
		||||
                InventoryUtils.closeInventory(session, openInventory.getId());
 | 
			
		||||
                InventoryTranslator openTranslator = session.getInventoryTranslator();
 | 
			
		||||
                if (!openTranslator.getClass().equals(newTranslator.getClass())) {
 | 
			
		||||
                    InventoryUtils.closeInventory(session, openInventory.getId());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId());
 | 
			
		||||
            session.sendDownstreamPacket(closeWindowPacket);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        String name = MessageTranslator.convertMessageLenient(packet.getName(), session.getLocale());
 | 
			
		||||
 | 
			
		||||
        name = LocaleUtils.getLocaleString(name, session.getLocale());
 | 
			
		||||
 | 
			
		||||
        Inventory newInventory = new Inventory(name, packet.getWindowId(), packet.getType(), newTranslator.size + 36);
 | 
			
		||||
        session.getInventoryCache().cacheInventory(newInventory);
 | 
			
		||||
        if (openInventory != null) {
 | 
			
		||||
            InventoryTranslator openTranslator = InventoryTranslator.INVENTORY_TRANSLATORS.get(openInventory.getWindowType());
 | 
			
		||||
            if (!openTranslator.getClass().equals(newTranslator.getClass())) {
 | 
			
		||||
                InventoryUtils.closeWindow(session, openInventory.getId());
 | 
			
		||||
                InventoryUtils.closeInventory(session, openInventory.getId());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        InventoryUtils.openInventory(session, newInventory);
 | 
			
		||||
            session.setInventoryTranslator(newTranslator);
 | 
			
		||||
            InventoryUtils.openInventory(session, newInventory);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,7 @@
 | 
			
		|||
package org.geysermc.connector.network.translators.java.window;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket;
 | 
			
		||||
import org.geysermc.connector.inventory.GeyserItemStack;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.PacketTranslator;
 | 
			
		||||
| 
						 | 
				
			
			@ -38,23 +39,26 @@ public class JavaSetSlotTranslator extends PacketTranslator<ServerSetSlotPacket>
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translate(ServerSetSlotPacket packet, GeyserSession session) {
 | 
			
		||||
        if (packet.getWindowId() == 255 && packet.getSlot() == -1) { //cursor
 | 
			
		||||
            if (session.getCraftSlot() != 0)
 | 
			
		||||
        System.out.println(packet.toString());
 | 
			
		||||
        session.addInventoryTask(() -> {
 | 
			
		||||
            if (packet.getWindowId() == 255) { //cursor
 | 
			
		||||
                GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
 | 
			
		||||
                session.getPlayerInventory().setCursor(newItem, session);
 | 
			
		||||
                InventoryUtils.updateCursor(session);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //TODO: support window id -2, should update player inventory
 | 
			
		||||
            Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
 | 
			
		||||
            if (inventory == null)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            session.getInventory().setCursor(packet.getItem());
 | 
			
		||||
            InventoryUtils.updateCursor(session);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId());
 | 
			
		||||
        if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType());
 | 
			
		||||
        if (translator != null) {
 | 
			
		||||
            inventory.setItem(packet.getSlot(), packet.getItem());
 | 
			
		||||
            translator.updateSlot(session, inventory, packet.getSlot());
 | 
			
		||||
        }
 | 
			
		||||
            InventoryTranslator translator = session.getInventoryTranslator();
 | 
			
		||||
            if (translator != null) {
 | 
			
		||||
                GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
 | 
			
		||||
                inventory.setItem(packet.getSlot(), newItem, session);
 | 
			
		||||
                translator.updateSlot(session, inventory, packet.getSlot());
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,32 +26,33 @@
 | 
			
		|||
package org.geysermc.connector.network.translators.java.window;
 | 
			
		||||
 | 
			
		||||
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket;
 | 
			
		||||
import org.geysermc.connector.inventory.GeyserItemStack;
 | 
			
		||||
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.Translator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import org.geysermc.connector.utils.InventoryUtils;
 | 
			
		||||
 | 
			
		||||
@Translator(packet = ServerWindowItemsPacket.class)
 | 
			
		||||
public class JavaWindowItemsTranslator extends PacketTranslator<ServerWindowItemsPacket> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translate(ServerWindowItemsPacket packet, GeyserSession session) {
 | 
			
		||||
        Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId());
 | 
			
		||||
        if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null))
 | 
			
		||||
            return;
 | 
			
		||||
        session.addInventoryTask(() -> {
 | 
			
		||||
            Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
 | 
			
		||||
            if (inventory == null)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
        if (packet.getItems().length < inventory.getSize()) {
 | 
			
		||||
            inventory.setItems(Arrays.copyOf(packet.getItems(), inventory.getSize()));
 | 
			
		||||
        } else {
 | 
			
		||||
            inventory.setItems(packet.getItems());
 | 
			
		||||
        }
 | 
			
		||||
            for (int i = 0; i < packet.getItems().length; i++) {
 | 
			
		||||
                GeyserItemStack newItem = GeyserItemStack.from(packet.getItems()[i]);
 | 
			
		||||
                inventory.setItem(i, newItem, session);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType());
 | 
			
		||||
        if (translator != null) {
 | 
			
		||||
            translator.updateInventory(session, inventory);
 | 
			
		||||
        }
 | 
			
		||||
            InventoryTranslator translator = session.getInventoryTranslator();
 | 
			
		||||
            if (translator != null) {
 | 
			
		||||
                translator.updateInventory(session, inventory);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,19 +31,23 @@ import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		|||
import org.geysermc.connector.network.translators.PacketTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.Translator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.utils.InventoryUtils;
 | 
			
		||||
 | 
			
		||||
@Translator(packet = ServerWindowPropertyPacket.class)
 | 
			
		||||
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;
 | 
			
		||||
        System.out.println(packet.toString());
 | 
			
		||||
        session.addInventoryTask(() -> {
 | 
			
		||||
            Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
 | 
			
		||||
            if (inventory == null)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
        InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType());
 | 
			
		||||
        if (translator != null) {
 | 
			
		||||
            translator.updateProperty(session, inventory, packet.getRawProperty(), packet.getValue());
 | 
			
		||||
        }
 | 
			
		||||
            InventoryTranslator translator = session.getInventoryTranslator();
 | 
			
		||||
            if (translator != null) {
 | 
			
		||||
                translator.updateProperty(session, inventory, packet.getRawProperty(), packet.getValue());
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,7 +42,7 @@ import org.geysermc.connector.entity.player.PlayerEntity;
 | 
			
		|||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.PacketTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.Translator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.PlayerInventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.inventory.translators.PlayerInventoryTranslator;
 | 
			
		||||
import org.geysermc.connector.utils.LocaleUtils;
 | 
			
		||||
 | 
			
		||||
@Translator(packet = ServerNotifyClientPacket.class)
 | 
			
		||||
| 
						 | 
				
			
			@ -111,7 +111,7 @@ public class JavaNotifyClientTranslator extends PacketTranslator<ServerNotifyCli
 | 
			
		|||
                session.sendAdventureSettings();
 | 
			
		||||
 | 
			
		||||
                // Update the crafting grid to add/remove barriers for creative inventory
 | 
			
		||||
                PlayerInventoryTranslator.updateCraftingGrid(session, session.getInventory());
 | 
			
		||||
                PlayerInventoryTranslator.updateCraftingGrid(session, session.getPlayerInventory());
 | 
			
		||||
                break;
 | 
			
		||||
            case ENTER_CREDITS:
 | 
			
		||||
                switch ((EnterCreditsValue) packet.getValue()) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,6 +38,7 @@ import com.nukkitx.protocol.bedrock.packet.UpdateTradePacket;
 | 
			
		|||
import org.geysermc.connector.entity.Entity;
 | 
			
		||||
import org.geysermc.connector.entity.type.EntityType;
 | 
			
		||||
import org.geysermc.connector.inventory.Inventory;
 | 
			
		||||
import org.geysermc.connector.inventory.MerchantContainer;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.PacketTranslator;
 | 
			
		||||
import org.geysermc.connector.network.translators.Translator;
 | 
			
		||||
| 
						 | 
				
			
			@ -53,8 +54,14 @@ public class JavaTradeListTranslator extends PacketTranslator<ServerTradeListPac
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void translate(ServerTradeListPacket packet, GeyserSession session) {
 | 
			
		||||
        Entity villager = session.getPlayerEntity();
 | 
			
		||||
        session.setVillagerTrades(packet.getTrades());
 | 
			
		||||
        Inventory openInventory = session.getOpenInventory();
 | 
			
		||||
        if (!(openInventory instanceof MerchantContainer && openInventory.getId() == packet.getWindowId())) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        MerchantContainer merchantInventory = (MerchantContainer) openInventory;
 | 
			
		||||
        merchantInventory.setVillagerTrades(packet.getTrades());
 | 
			
		||||
        Entity villager = merchantInventory.getVillager();
 | 
			
		||||
        villager.getMetadata().put(EntityData.TRADE_TIER, packet.getVillagerLevel() - 1);
 | 
			
		||||
        villager.getMetadata().put(EntityData.MAX_TRADE_TIER, 4);
 | 
			
		||||
        villager.getMetadata().put(EntityData.TRADE_XP, packet.getExperience());
 | 
			
		||||
| 
						 | 
				
			
			@ -64,30 +71,28 @@ public class JavaTradeListTranslator extends PacketTranslator<ServerTradeListPac
 | 
			
		|||
        updateTradePacket.setTradeTier(packet.getVillagerLevel() - 1);
 | 
			
		||||
        updateTradePacket.setContainerId((short) packet.getWindowId());
 | 
			
		||||
        updateTradePacket.setContainerType(ContainerType.TRADE);
 | 
			
		||||
        Inventory openInv = session.getInventoryCache().getOpenInventory();
 | 
			
		||||
        String displayName;
 | 
			
		||||
        if (openInv != null && openInv.getId() == packet.getWindowId()) {
 | 
			
		||||
            displayName = openInv.getTitle();
 | 
			
		||||
        //TODO: verify correct window title behavior
 | 
			
		||||
        Entity realVillager = session.getEntityCache().getEntityByGeyserId(session.getLastInteractedVillagerEid());
 | 
			
		||||
        if (realVillager != null && realVillager.getMetadata().containsKey(EntityData.NAMETAG) && realVillager.getMetadata().getString(EntityData.NAMETAG) != null) {
 | 
			
		||||
            displayName = realVillager.getMetadata().getString(EntityData.NAMETAG);
 | 
			
		||||
        } else {
 | 
			
		||||
            Entity realVillager = session.getEntityCache().getEntityByGeyserId(session.getLastInteractedVillagerEid());
 | 
			
		||||
            if (realVillager != null && realVillager.getMetadata().containsKey(EntityData.NAMETAG) && realVillager.getMetadata().getString(EntityData.NAMETAG) != null) {
 | 
			
		||||
                displayName = realVillager.getMetadata().getString(EntityData.NAMETAG);
 | 
			
		||||
            } else {
 | 
			
		||||
                displayName = realVillager != null &&
 | 
			
		||||
                        realVillager.getEntityType() == EntityType.WANDERING_TRADER ? "Wandering Trader" : "Villager";
 | 
			
		||||
            }
 | 
			
		||||
            displayName = realVillager != null &&
 | 
			
		||||
                    realVillager.getEntityType() == EntityType.WANDERING_TRADER ? "Wandering Trader" : "Villager";
 | 
			
		||||
        }
 | 
			
		||||
        updateTradePacket.setDisplayName(displayName);
 | 
			
		||||
        updateTradePacket.setSize(0);
 | 
			
		||||
        updateTradePacket.setNewTradingUi(true);
 | 
			
		||||
        updateTradePacket.setUsingEconomyTrade(true);
 | 
			
		||||
        updateTradePacket.setPlayerUniqueEntityId(session.getPlayerEntity().getGeyserId());
 | 
			
		||||
        updateTradePacket.setTraderUniqueEntityId(session.getPlayerEntity().getGeyserId());
 | 
			
		||||
        updateTradePacket.setTraderUniqueEntityId(villager.getGeyserId());
 | 
			
		||||
        NbtMapBuilder builder = NbtMap.builder();
 | 
			
		||||
        List<NbtMap> tags = new ArrayList<>();
 | 
			
		||||
        for (VillagerTrade trade : packet.getTrades()) {
 | 
			
		||||
        for (int i = 0; i < packet.getTrades().length; i++) {
 | 
			
		||||
            VillagerTrade trade = packet.getTrades()[i];
 | 
			
		||||
            NbtMapBuilder recipe = NbtMap.builder();
 | 
			
		||||
            recipe.putInt("maxUses", trade.getMaxUses());
 | 
			
		||||
            recipe.putInt("netId", i + 1);
 | 
			
		||||
            recipe.putInt("maxUses", trade.isTradeDisabled() ? 0 : trade.getMaxUses());
 | 
			
		||||
            recipe.putInt("traderExp", trade.getXp());
 | 
			
		||||
            recipe.putFloat("priceMultiplierA", trade.getPriceMultiplier());
 | 
			
		||||
            recipe.put("sell", getItemTag(session, trade.getOutput(), 0));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,10 +44,19 @@ public class JavaUnloadChunkTranslator extends PacketTranslator<ServerUnloadChun
 | 
			
		|||
        Iterator<Vector3i> iterator = session.getSkullCache().keySet().iterator();
 | 
			
		||||
        while (iterator.hasNext()) {
 | 
			
		||||
            Vector3i position = iterator.next();
 | 
			
		||||
            if (Math.floor(position.getX() / 16) == packet.getX() && Math.floor(position.getZ() / 16) == packet.getZ()) {
 | 
			
		||||
            if (Math.floor((double) position.getX() / 16) == packet.getX() && Math.floor((double) position.getZ() / 16) == packet.getZ()) {
 | 
			
		||||
                session.getSkullCache().get(position).despawnEntity(session);
 | 
			
		||||
                iterator.remove();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Do the same thing with lecterns
 | 
			
		||||
        iterator = session.getLecternCache().iterator();
 | 
			
		||||
        while (iterator.hasNext()) {
 | 
			
		||||
            Vector3i position = iterator.next();
 | 
			
		||||
            if (Math.floor((double) position.getX() / 16) == packet.getX() && Math.floor((double) position.getZ() / 16) == packet.getZ()) {
 | 
			
		||||
                iterator.remove();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ package org.geysermc.connector.network.translators.sound;
 | 
			
		|||
 | 
			
		||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
 | 
			
		||||
import com.nukkitx.math.vector.Vector3f;
 | 
			
		||||
import org.geysermc.connector.inventory.GeyserItemStack;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -60,12 +61,12 @@ public interface BlockSoundInteractionHandler extends SoundInteractionHandler<St
 | 
			
		|||
                }
 | 
			
		||||
                if (!contains) continue;
 | 
			
		||||
            }
 | 
			
		||||
            ItemStack itemInHand = session.getInventory().getItemInHand();
 | 
			
		||||
            GeyserItemStack itemInHand = session.getPlayerInventory().getItemInHand();
 | 
			
		||||
            if (interactionEntry.getKey().items().length != 0) {
 | 
			
		||||
                if (itemInHand == null || itemInHand.getId() == 0) {
 | 
			
		||||
                if (itemInHand.isEmpty()) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                String handIdentifier = ItemRegistry.getItem(session.getInventory().getItemInHand()).getJavaIdentifier();
 | 
			
		||||
                String handIdentifier = itemInHand.getItemEntry().getJavaIdentifier();
 | 
			
		||||
                boolean contains = false;
 | 
			
		||||
                for (String itemIdentifier : interactionEntry.getKey().items()) {
 | 
			
		||||
                    if (handIdentifier.contains(itemIdentifier)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -76,7 +77,7 @@ public interface BlockSoundInteractionHandler extends SoundInteractionHandler<St
 | 
			
		|||
                if (!contains) continue;
 | 
			
		||||
            }
 | 
			
		||||
            if (session.isSneaking() && !interactionEntry.getKey().ignoreSneakingWhileHolding()) {
 | 
			
		||||
                if (session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() != 0) {
 | 
			
		||||
                if (!itemInHand.isEmpty()) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,7 @@ package org.geysermc.connector.network.translators.sound;
 | 
			
		|||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
 | 
			
		||||
import com.nukkitx.math.vector.Vector3f;
 | 
			
		||||
import org.geysermc.connector.entity.Entity;
 | 
			
		||||
import org.geysermc.connector.inventory.GeyserItemStack;
 | 
			
		||||
import org.geysermc.connector.network.session.GeyserSession;
 | 
			
		||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -61,12 +62,12 @@ public interface EntitySoundInteractionHandler extends SoundInteractionHandler<E
 | 
			
		|||
                }
 | 
			
		||||
                if (!contains) continue;
 | 
			
		||||
            }
 | 
			
		||||
            ItemStack itemInHand = session.getInventory().getItemInHand();
 | 
			
		||||
            GeyserItemStack itemInHand = session.getPlayerInventory().getItemInHand();
 | 
			
		||||
            if (interactionEntry.getKey().items().length != 0) {
 | 
			
		||||
                if (itemInHand == null || itemInHand.getId() == 0) {
 | 
			
		||||
                if (itemInHand.isEmpty()) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                String handIdentifier = ItemRegistry.getItem(session.getInventory().getItemInHand()).getJavaIdentifier();
 | 
			
		||||
                String handIdentifier = itemInHand.getItemEntry().getJavaIdentifier();
 | 
			
		||||
                boolean contains = false;
 | 
			
		||||
                for (String itemIdentifier : interactionEntry.getKey().items()) {
 | 
			
		||||
                    if (handIdentifier.contains(itemIdentifier)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +78,7 @@ public interface EntitySoundInteractionHandler extends SoundInteractionHandler<E
 | 
			
		|||
                if (!contains) continue;
 | 
			
		||||
            }
 | 
			
		||||
            if (session.isSneaking() && !interactionEntry.getKey().ignoreSneakingWhileHolding()) {
 | 
			
		||||
                if (session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() != 0) {
 | 
			
		||||
                if (!itemInHand.isEmpty()) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ public class BucketSoundInteractionHandler implements BlockSoundInteractionHandl
 | 
			
		|||
    @Override
 | 
			
		||||
    public void handleInteraction(GeyserSession session, Vector3f position, String identifier) {
 | 
			
		||||
        if (session.getBucketScheduledFuture() == null) return; // No bucket was really interacted with
 | 
			
		||||
        String handItemIdentifier = ItemRegistry.getItem(session.getInventory().getItemInHand()).getJavaIdentifier();
 | 
			
		||||
        String handItemIdentifier = session.getPlayerInventory().getItemInHand().getItemEntry().getJavaIdentifier();
 | 
			
		||||
        LevelSoundEventPacket soundEventPacket = new LevelSoundEventPacket();
 | 
			
		||||
        soundEventPacket.setPosition(position);
 | 
			
		||||
        soundEventPacket.setIdentifier(":");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ public class MilkCowSoundInteractionHandler implements EntitySoundInteractionHan
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void handleInteraction(GeyserSession session, Vector3f position, Entity value) {
 | 
			
		||||
        if (!ItemRegistry.getItem(session.getInventory().getItemInHand()).getJavaIdentifier().equals("minecraft:bucket")) {
 | 
			
		||||
        if (!session.getPlayerInventory().getItemInHand().getItemEntry().getJavaIdentifier().equals("minecraft:bucket")) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue