Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-09-08 20:43:04 +02:00
Merge pull request #1982 from GeyserMC/server-inventory
The Inventory Rewrite
Dieser Commit ist enthalten in:
Commit
4af4d1f800
12
README.md
12
README.md
@ -34,20 +34,10 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set
|
|||||||
- Test Server: `test.geysermc.org` port `25565` for Java and `19132` for Bedrock
|
- Test Server: `test.geysermc.org` port `25565` for Java and `19132` for Bedrock
|
||||||
|
|
||||||
## What's Left to be Added/Fixed
|
## What's Left to be Added/Fixed
|
||||||
- Lecterns
|
|
||||||
- Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you)
|
- Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you)
|
||||||
- Resource pack conversion/CustomModelData
|
- Resource pack conversion/CustomModelData
|
||||||
- Some Entity Flags
|
- Some Entity Flags
|
||||||
- The Following Inventories
|
- Structure block UI
|
||||||
- Enchantment Table (as a proper GUI)
|
|
||||||
- Beacon
|
|
||||||
- Cartography Table
|
|
||||||
- Stonecutter
|
|
||||||
- Structure Block
|
|
||||||
- Horse Inventory
|
|
||||||
- Loom
|
|
||||||
- Smithing Table
|
|
||||||
- Grindstone
|
|
||||||
|
|
||||||
## What can't be fixed
|
## What can't be fixed
|
||||||
The following things can't be fixed because of Bedrock limitations. They might be fixable in the future, but not as of now.
|
The following things can't be fixed because of Bedrock limitations. They might be fixable in the future, but not as of now.
|
||||||
|
@ -43,6 +43,7 @@ import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
|||||||
import org.geysermc.platform.spigot.command.GeyserSpigotCommandExecutor;
|
import org.geysermc.platform.spigot.command.GeyserSpigotCommandExecutor;
|
||||||
import org.geysermc.platform.spigot.command.GeyserSpigotCommandManager;
|
import org.geysermc.platform.spigot.command.GeyserSpigotCommandManager;
|
||||||
import org.geysermc.platform.spigot.command.SpigotCommandSender;
|
import org.geysermc.platform.spigot.command.SpigotCommandSender;
|
||||||
|
import org.geysermc.platform.spigot.world.GeyserSpigot1_11CraftingListener;
|
||||||
import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener;
|
import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener;
|
||||||
import org.geysermc.platform.spigot.world.manager.*;
|
import org.geysermc.platform.spigot.world.manager.*;
|
||||||
import us.myles.ViaVersion.api.Pair;
|
import us.myles.ViaVersion.api.Pair;
|
||||||
@ -154,8 +155,9 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
geyserLogger.debug("Legacy version of Minecraft (1.15.2 or older) detected; not using 3D biomes.");
|
geyserLogger.debug("Legacy version of Minecraft (1.15.2 or older) detected; not using 3D biomes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isPre1_12 = !isCompatible(Bukkit.getServer().getVersion(), "1.12.0");
|
||||||
// Set if we need to use a different method for getting a player's locale
|
// Set if we need to use a different method for getting a player's locale
|
||||||
SpigotCommandSender.setUseLegacyLocaleMethod(!isCompatible(Bukkit.getServer().getVersion(), "1.12.0"));
|
SpigotCommandSender.setUseLegacyLocaleMethod(isPre1_12);
|
||||||
|
|
||||||
if (connector.getConfig().isUseAdapters()) {
|
if (connector.getConfig().isUseAdapters()) {
|
||||||
try {
|
try {
|
||||||
@ -165,14 +167,14 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
if (isViaVersion && isViaVersionNeeded()) {
|
if (isViaVersion && isViaVersionNeeded()) {
|
||||||
if (isLegacy) {
|
if (isLegacy) {
|
||||||
// Pre-1.13
|
// Pre-1.13
|
||||||
this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager();
|
this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager(this);
|
||||||
} else {
|
} else {
|
||||||
// Post-1.13
|
// Post-1.13
|
||||||
this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this, use3dBiomes);
|
this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this, use3dBiomes);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No ViaVersion
|
// No ViaVersion
|
||||||
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(use3dBiomes);
|
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this, use3dBiomes);
|
||||||
}
|
}
|
||||||
geyserLogger.debug("Using NMS adapter: " + this.geyserWorldManager.getClass() + ", " + nmsVersion);
|
geyserLogger.debug("Using NMS adapter: " + this.geyserWorldManager.getClass() + ", " + nmsVersion);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -188,20 +190,24 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
// No NMS adapter
|
// No NMS adapter
|
||||||
if (isLegacy && isViaVersion) {
|
if (isLegacy && isViaVersion) {
|
||||||
// Use ViaVersion for converting pre-1.13 block states
|
// Use ViaVersion for converting pre-1.13 block states
|
||||||
this.geyserWorldManager = new GeyserSpigot1_12WorldManager();
|
this.geyserWorldManager = new GeyserSpigot1_12WorldManager(this);
|
||||||
} else if (isLegacy) {
|
} else if (isLegacy) {
|
||||||
// Not sure how this happens - without ViaVersion, we don't know any block states, so just assume everything is air
|
// 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 {
|
} else {
|
||||||
// Post-1.13
|
// Post-1.13
|
||||||
this.geyserWorldManager = new GeyserSpigotWorldManager(use3dBiomes);
|
this.geyserWorldManager = new GeyserSpigotWorldManager(this, use3dBiomes);
|
||||||
}
|
}
|
||||||
geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass());
|
geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass());
|
||||||
}
|
}
|
||||||
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, this.geyserWorldManager);
|
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, this.geyserWorldManager);
|
||||||
|
|
||||||
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
|
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
|
||||||
|
|
||||||
|
if (isPre1_12) {
|
||||||
|
// Register events needed to send all recipes to the client
|
||||||
|
Bukkit.getServer().getPluginManager().registerEvents(new GeyserSpigot1_11CraftingListener(connector), this);
|
||||||
|
}
|
||||||
|
|
||||||
this.getCommand("geyser").setExecutor(new GeyserSpigotCommandExecutor(connector));
|
this.getCommand("geyser").setExecutor(new GeyserSpigotCommandExecutor(connector));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* 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.platform.spigot.world;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
||||||
|
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.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.protocol.bedrock.data.inventory.CraftingData;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
import org.bukkit.inventory.Recipe;
|
||||||
|
import org.bukkit.inventory.ShapedRecipe;
|
||||||
|
import org.bukkit.inventory.ShapelessRecipe;
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.item.RecipeRegistry;
|
||||||
|
import us.myles.ViaVersion.api.Pair;
|
||||||
|
import us.myles.ViaVersion.api.data.MappingData;
|
||||||
|
import us.myles.ViaVersion.api.protocol.Protocol;
|
||||||
|
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
|
||||||
|
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
|
||||||
|
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to send all available recipes from the server to the client, as a valid recipe book packet won't be sent by the server.
|
||||||
|
* Requires ViaVersion.
|
||||||
|
*/
|
||||||
|
public class GeyserSpigot1_11CraftingListener implements Listener {
|
||||||
|
|
||||||
|
private final GeyserConnector connector;
|
||||||
|
/**
|
||||||
|
* Specific mapping data for 1.12 to 1.13. Used to convert the 1.12 item into 1.13.
|
||||||
|
*/
|
||||||
|
private final MappingData mappingData1_12to1_13;
|
||||||
|
/**
|
||||||
|
* The list of all protocols from the client's version to 1.13.
|
||||||
|
*/
|
||||||
|
private final List<Pair<Integer, Protocol>> protocolList;
|
||||||
|
|
||||||
|
public GeyserSpigot1_11CraftingListener(GeyserConnector connector) {
|
||||||
|
this.connector = connector;
|
||||||
|
this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData();
|
||||||
|
this.protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
|
||||||
|
ProtocolVersion.v1_13.getVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
|
GeyserSession session = null;
|
||||||
|
for (GeyserSession otherSession : connector.getPlayers()) {
|
||||||
|
if (otherSession.getName().equals(event.getPlayer().getName())) {
|
||||||
|
session = otherSession;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (session == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendServerRecipes(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendServerRecipes(GeyserSession session) {
|
||||||
|
int netId = RecipeRegistry.LAST_RECIPE_NET_ID;
|
||||||
|
|
||||||
|
CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
|
||||||
|
craftingDataPacket.setCleanRecipes(true);
|
||||||
|
|
||||||
|
Iterator<Recipe> recipeIterator = Bukkit.getServer().recipeIterator();
|
||||||
|
while (recipeIterator.hasNext()) {
|
||||||
|
Recipe recipe = recipeIterator.next();
|
||||||
|
|
||||||
|
Pair<ItemStack, ItemData> outputs = translateToBedrock(session, recipe.getResult());
|
||||||
|
ItemStack javaOutput = outputs.getKey();
|
||||||
|
ItemData output = outputs.getValue();
|
||||||
|
if (output.getId() == 0) continue; // If items make air we don't want that
|
||||||
|
|
||||||
|
boolean isNotAllAir = false; // Check for all-air recipes
|
||||||
|
if (recipe instanceof ShapedRecipe) {
|
||||||
|
ShapedRecipe shapedRecipe = (ShapedRecipe) recipe;
|
||||||
|
int size = shapedRecipe.getShape().length * shapedRecipe.getShape()[0].length();
|
||||||
|
Ingredient[] ingredients = new Ingredient[size];
|
||||||
|
ItemData[] input = new ItemData[size];
|
||||||
|
for (int i = 0; i < input.length; i++) {
|
||||||
|
// Index is converting char to integer, adding i then converting back to char based on ASCII code
|
||||||
|
Pair<ItemStack, ItemData> result = translateToBedrock(session, shapedRecipe.getIngredientMap().get((char) ('a' + i)));
|
||||||
|
ingredients[i] = new Ingredient(new ItemStack[]{result.getKey()});
|
||||||
|
input[i] = result.getValue();
|
||||||
|
isNotAllAir |= input[i].getId() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNotAllAir) continue;
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
// Add recipe to our internal cache
|
||||||
|
ShapedRecipeData data = new ShapedRecipeData(shapedRecipe.getShape()[0].length(), shapedRecipe.getShape().length,
|
||||||
|
"", ingredients, javaOutput);
|
||||||
|
session.getCraftingRecipes().put(netId,
|
||||||
|
new com.github.steveice10.mc.protocol.data.game.recipe.Recipe(RecipeType.CRAFTING_SHAPED, uuid.toString(), data));
|
||||||
|
|
||||||
|
// Add recipe for Bedrock
|
||||||
|
craftingDataPacket.getCraftingData().add(CraftingData.fromShaped(uuid.toString(),
|
||||||
|
shapedRecipe.getShape()[0].length(), shapedRecipe.getShape().length, Arrays.asList(input),
|
||||||
|
Collections.singletonList(output), uuid, "crafting_table", 0, netId++));
|
||||||
|
} else if (recipe instanceof ShapelessRecipe) {
|
||||||
|
ShapelessRecipe shapelessRecipe = (ShapelessRecipe) recipe;
|
||||||
|
Ingredient[] ingredients = new Ingredient[shapelessRecipe.getIngredientList().size()];
|
||||||
|
ItemData[] input = new ItemData[shapelessRecipe.getIngredientList().size()];
|
||||||
|
|
||||||
|
for (int i = 0; i < input.length; i++) {
|
||||||
|
Pair<ItemStack, ItemData> result = translateToBedrock(session, shapelessRecipe.getIngredientList().get(i));
|
||||||
|
ingredients[i] = new Ingredient(new ItemStack[]{result.getKey()});
|
||||||
|
input[i] = result.getValue();
|
||||||
|
isNotAllAir |= input[i].getId() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNotAllAir) continue;
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
// Add recipe to our internal cache
|
||||||
|
ShapelessRecipeData data = new ShapelessRecipeData("", ingredients, javaOutput);
|
||||||
|
session.getCraftingRecipes().put(netId,
|
||||||
|
new com.github.steveice10.mc.protocol.data.game.recipe.Recipe(RecipeType.CRAFTING_SHAPELESS, uuid.toString(), data));
|
||||||
|
|
||||||
|
// Add recipe for Bedrock
|
||||||
|
craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
|
||||||
|
Arrays.asList(input), Collections.singletonList(output), uuid, "crafting_table", 0, netId++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session.sendUpstreamPacket(craftingDataPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private Pair<ItemStack, ItemData> translateToBedrock(GeyserSession session, org.bukkit.inventory.ItemStack itemStack) {
|
||||||
|
if (itemStack != null && itemStack.getData() != null) {
|
||||||
|
if (itemStack.getType().getId() == 0) {
|
||||||
|
return new Pair<>(null, ItemData.AIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
int legacyId = (itemStack.getType().getId() << 4) | (itemStack.getData().getData() & 0xFFFF);
|
||||||
|
|
||||||
|
if (itemStack.getType().getId() == 355 && itemStack.getData().getData() == (byte) 0) { // Handle bed color since the server will always be pre-1.12
|
||||||
|
legacyId = (itemStack.getType().getId() << 4) | ((byte) 14 & 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
// old version -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 and so on
|
||||||
|
int itemId;
|
||||||
|
if (mappingData1_12to1_13.getItemMappings().containsKey(legacyId)) {
|
||||||
|
itemId = mappingData1_12to1_13.getNewItemId(legacyId);
|
||||||
|
} else if (mappingData1_12to1_13.getItemMappings().containsKey((itemStack.getType().getId() << 4) | (0))) {
|
||||||
|
itemId = mappingData1_12to1_13.getNewItemId((itemStack.getType().getId() << 4) | (0));
|
||||||
|
} else {
|
||||||
|
// No ID found, just send back air
|
||||||
|
return new Pair<>(null, ItemData.AIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = protocolList.size() - 1; i >= 0; i--) {
|
||||||
|
MappingData mappingData = protocolList.get(i).getValue().getMappingData();
|
||||||
|
if (mappingData != null) {
|
||||||
|
itemId = mappingData.getNewItemId(itemId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStack mcItemStack = new ItemStack(itemId, itemStack.getAmount());
|
||||||
|
ItemData finalData = ItemTranslator.translateToBedrock(session, mcItemStack);
|
||||||
|
return new Pair<>(mcItemStack, finalData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty slot, most likely
|
||||||
|
return new Pair<>(null, ItemData.AIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -27,6 +27,7 @@ package org.geysermc.platform.spigot.world.manager;
|
|||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
||||||
@ -40,7 +41,8 @@ import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
|
|||||||
public class GeyserSpigot1_12NativeWorldManager extends GeyserSpigot1_12WorldManager {
|
public class GeyserSpigot1_12NativeWorldManager extends GeyserSpigot1_12WorldManager {
|
||||||
private final SpigotWorldAdapter adapter;
|
private final SpigotWorldAdapter adapter;
|
||||||
|
|
||||||
public GeyserSpigot1_12NativeWorldManager() {
|
public GeyserSpigot1_12NativeWorldManager(Plugin plugin) {
|
||||||
|
super(plugin);
|
||||||
this.adapter = SpigotAdapters.getWorldAdapter();
|
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
|
// 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.World;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
import us.myles.ViaVersion.api.Pair;
|
import us.myles.ViaVersion.api.Pair;
|
||||||
@ -61,8 +62,8 @@ public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager {
|
|||||||
*/
|
*/
|
||||||
private final List<Pair<Integer, Protocol>> protocolList;
|
private final List<Pair<Integer, Protocol>> protocolList;
|
||||||
|
|
||||||
public GeyserSpigot1_12WorldManager() {
|
public GeyserSpigot1_12WorldManager(Plugin plugin) {
|
||||||
super(false);
|
super(plugin, false);
|
||||||
this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData();
|
this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData();
|
||||||
this.protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION,
|
this.protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION,
|
||||||
ProtocolVersion.v1_13.getVersion());
|
ProtocolVersion.v1_13.getVersion());
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
package org.geysermc.platform.spigot.world.manager;
|
package org.geysermc.platform.spigot.world.manager;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
|
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.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
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!!
|
* If this occurs to you somehow, please let us know!!
|
||||||
*/
|
*/
|
||||||
public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager {
|
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.
|
// Since this is pre-1.13 (and thus pre-1.15), there will never be 3D biomes.
|
||||||
super(false);
|
super(plugin, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -47,7 +47,7 @@ public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorl
|
|||||||
private final Int2IntMap oldToNewBlockId;
|
private final Int2IntMap oldToNewBlockId;
|
||||||
|
|
||||||
public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin, boolean use3dBiomes) {
|
public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin, boolean use3dBiomes) {
|
||||||
super(use3dBiomes);
|
super(plugin, use3dBiomes);
|
||||||
IntList allBlockStates = adapter.getAllBlockStates();
|
IntList allBlockStates = adapter.getAllBlockStates();
|
||||||
oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size());
|
oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size());
|
||||||
ProtocolVersion serverVersion = plugin.getServerProtocolVersion();
|
ProtocolVersion serverVersion = plugin.getServerProtocolVersion();
|
||||||
|
@ -27,6 +27,7 @@ package org.geysermc.platform.spigot.world.manager;
|
|||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
||||||
@ -35,8 +36,8 @@ import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter;
|
|||||||
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
|
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
|
||||||
protected final SpigotWorldAdapter adapter;
|
protected final SpigotWorldAdapter adapter;
|
||||||
|
|
||||||
public GeyserSpigotNativeWorldManager(boolean use3dBiomes) {
|
public GeyserSpigotNativeWorldManager(Plugin plugin, boolean use3dBiomes) {
|
||||||
super(use3dBiomes);
|
super(plugin, use3dBiomes);
|
||||||
adapter = SpigotAdapters.getWorldAdapter();
|
adapter = SpigotAdapters.getWorldAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,23 +28,35 @@ package org.geysermc.platform.spigot.world.manager;
|
|||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
||||||
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
|
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.Int2IntMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.Lectern;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.entity.Player;
|
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.GeyserConnector;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
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.GeyserWorldManager;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
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.FileUtils;
|
||||||
import org.geysermc.connector.utils.GameRule;
|
import org.geysermc.connector.utils.GameRule;
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
import org.geysermc.connector.utils.LanguageUtils;
|
||||||
|
|
||||||
import java.io.InputStream;
|
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
|
* 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);
|
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.use3dBiomes = use3dBiomes;
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
// Load the values into the biome-to-ID map
|
// Load the values into the biome-to-ID map
|
||||||
InputStream biomeStream = FileUtils.getResource("biomes.json");
|
InputStream biomeStream = FileUtils.getResource("biomes.json");
|
||||||
@ -132,9 +147,6 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
|||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public int[] getBiomeDataAt(GeyserSession session, int x, int z) {
|
public int[] getBiomeDataAt(GeyserSession session, int x, int z) {
|
||||||
if (session.getPlayerEntity() == null) {
|
|
||||||
return new int[1024];
|
|
||||||
}
|
|
||||||
int[] biomeData = new int[1024];
|
int[] biomeData = new int[1024];
|
||||||
World world = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld();
|
World world = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld();
|
||||||
int chunkX = x << 4;
|
int chunkX = x << 4;
|
||||||
@ -167,6 +179,77 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
|||||||
return biomeData;
|
return biomeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) {
|
||||||
|
// Run as a task to prevent async issues
|
||||||
|
Runnable lecternInfoGet = () -> {
|
||||||
|
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();
|
||||||
|
// On the count: allow the book to show/open even there are no pages. We know there is a book here, after all, and this matches Java behavior
|
||||||
|
boolean hasBookPages = bookMeta.getPageCount() > 0;
|
||||||
|
NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, hasBookPages ? bookMeta.getPageCount() : 1);
|
||||||
|
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<>(bookMeta.getPageCount());
|
||||||
|
if (hasBookPages) {
|
||||||
|
for (String page : bookMeta.getPages()) {
|
||||||
|
NbtMapBuilder pageBuilder = NbtMap.builder()
|
||||||
|
.putString("photoname", "")
|
||||||
|
.putString("text", page);
|
||||||
|
pages.add(pageBuilder.build());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Empty page
|
||||||
|
NbtMapBuilder pageBuilder = NbtMap.builder()
|
||||||
|
.putString("photoname", "")
|
||||||
|
.putString("text", "");
|
||||||
|
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));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isChunkLoad) {
|
||||||
|
// Delay to ensure the chunk is sent first, and then the lectern data
|
||||||
|
Bukkit.getScheduler().runTaskLater(this.plugin, lecternInfoGet, 5);
|
||||||
|
} else {
|
||||||
|
Bukkit.getScheduler().runTask(this.plugin, lecternInfoGet);
|
||||||
|
}
|
||||||
|
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) {
|
public Boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
|
||||||
return Boolean.parseBoolean(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID()));
|
return Boolean.parseBoolean(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID()));
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ import org.geysermc.connector.entity.attribute.AttributeType;
|
|||||||
import org.geysermc.connector.entity.living.ArmorStandEntity;
|
import org.geysermc.connector.entity.living.ArmorStandEntity;
|
||||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
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.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||||
import org.geysermc.connector.utils.AttributeUtils;
|
import org.geysermc.connector.utils.AttributeUtils;
|
||||||
@ -280,11 +281,12 @@ public class Entity {
|
|||||||
|
|
||||||
// Shield code
|
// Shield code
|
||||||
if (session.getPlayerEntity().getEntityId() == entityId && metadata.getFlags().getFlag(EntityFlag.SNEAKING)) {
|
if (session.getPlayerEntity().getEntityId() == entityId && metadata.getFlags().getFlag(EntityFlag.SNEAKING)) {
|
||||||
if ((session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() == ItemRegistry.SHIELD.getJavaId()) ||
|
PlayerInventory playerInv = session.getPlayerInventory();
|
||||||
(session.getInventoryCache().getPlayerInventory().getItem(45) != null && session.getInventoryCache().getPlayerInventory().getItem(45).getId() == ItemRegistry.SHIELD.getJavaId())) {
|
if ((playerInv.getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) ||
|
||||||
|
(playerInv.getOffhand().getJavaId() == ItemRegistry.SHIELD.getJavaId())) {
|
||||||
ClientPlayerUseItemPacket useItemPacket;
|
ClientPlayerUseItemPacket useItemPacket;
|
||||||
metadata.getFlags().setFlag(EntityFlag.BLOCKING, true);
|
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);
|
useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
||||||
}
|
}
|
||||||
// Else we just assume it's the offhand, to simplify logic and to assure the packet gets sent
|
// 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.EntityData;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
||||||
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
||||||
import org.geysermc.connector.entity.living.animal.AnimalEntity;
|
import org.geysermc.connector.entity.living.animal.AnimalEntity;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
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) {
|
public AbstractHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, 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
|
@Override
|
||||||
@ -75,6 +79,9 @@ public class AbstractHorseEntity extends AnimalEntity {
|
|||||||
entityEventPacket.setData(ItemRegistry.WHEAT.getBedrockId() << 16);
|
entityEventPacket.setData(ItemRegistry.WHEAT.getBedrockId() << 16);
|
||||||
session.sendUpstreamPacket(entityEventPacket);
|
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
|
// 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.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
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) {
|
public ChestedHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
|
|
||||||
|
metadata.put(EntityData.CONTAINER_BASE_SIZE, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -38,6 +38,8 @@ public class LlamaEntity extends ChestedHorseEntity {
|
|||||||
|
|
||||||
public LlamaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
public LlamaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
|
|
||||||
|
metadata.put(EntityData.CONTAINER_STRENGTH_MODIFIER, 3); // Presumably 3 slots for every 1 strength
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -56,7 +58,7 @@ public class LlamaEntity extends ChestedHorseEntity {
|
|||||||
// The damage value is the dye color that Java sends us
|
// The damage value is the dye color that Java sends us
|
||||||
// Always going to be a carpet so we can hardcode 171 in BlockTranslator
|
// 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
|
// 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 {
|
} else {
|
||||||
equipmentPacket.setChestplate(ItemData.AIR);
|
equipmentPacket.setChestplate(ItemData.AIR);
|
||||||
}
|
}
|
||||||
|
@ -23,16 +23,15 @@
|
|||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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.WindowType;
|
||||||
import com.github.steveice10.mc.protocol.data.game.window.WindowActionParam;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
/**
|
||||||
enum Click {
|
* Used to determine if rename packets should be sent.
|
||||||
LEFT(ClickItemParam.LEFT_CLICK),
|
*/
|
||||||
RIGHT(ClickItemParam.RIGHT_CLICK);
|
public class AnvilContainer extends Container {
|
||||||
|
public AnvilContainer(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
public final WindowActionParam actionParam;
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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.WindowType;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class BeaconContainer extends Container {
|
||||||
|
private int primaryId;
|
||||||
|
private int secondaryId;
|
||||||
|
|
||||||
|
public BeaconContainer(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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.WindowType;
|
||||||
|
|
||||||
|
public class CartographyContainer extends Container {
|
||||||
|
public CartographyContainer(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* 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.WindowType;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether we are using a real block when opening this inventory.
|
||||||
|
*/
|
||||||
|
private boolean isUsingRealBlock = false;
|
||||||
|
|
||||||
|
public Container(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
super(title, id, size, windowType);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will be overwritten for droppers.
|
||||||
|
*
|
||||||
|
* @param usingRealBlock whether this container is using a real container or not
|
||||||
|
* @param javaBlockId the Java block string of the block, if real
|
||||||
|
*/
|
||||||
|
public void setUsingRealBlock(boolean usingRealBlock, String javaBlockId) {
|
||||||
|
isUsingRealBlock = usingRealBlock;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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.WindowType;
|
||||||
|
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, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
|
||||||
|
enchantOptions = new EnchantOptionData[3];
|
||||||
|
geyserEnchantOptions = new GeyserEnchantOption[3];
|
||||||
|
for (int i = 0; i < geyserEnchantOptions.length; i++) {
|
||||||
|
geyserEnchantOptions[i] = new GeyserEnchantOption(i);
|
||||||
|
// Options cannot be null, so we build initial options
|
||||||
|
// GeyserSession can be safely null here because it's only needed for net IDs
|
||||||
|
enchantOptions[i] = geyserEnchantOptions[i].build(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
|
||||||
|
public class Generic3X3Container extends Container {
|
||||||
|
/**
|
||||||
|
* Whether we need to set the container type as {@link com.nukkitx.protocol.bedrock.data.inventory.ContainerType#DROPPER}.
|
||||||
|
*
|
||||||
|
* Used at {@link org.geysermc.connector.network.translators.inventory.translators.Generic3X3InventoryTranslator#openInventory(GeyserSession, Inventory)}
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
private boolean isDropper = false;
|
||||||
|
|
||||||
|
public Generic3X3Container(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsingRealBlock(boolean usingRealBlock, String javaBlockId) {
|
||||||
|
super.setUsingRealBlock(usingRealBlock, javaBlockId);
|
||||||
|
if (usingRealBlock) {
|
||||||
|
isDropper = javaBlockId.startsWith("minecraft:dropper");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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) {
|
||||||
|
return new EnchantOptionData(xpCost, javaIndex + 16, EMPTY,
|
||||||
|
enchantLevel == -1 ? EMPTY : Collections.singletonList(new EnchantData(bedrockEnchantIndex, enchantLevel)), EMPTY,
|
||||||
|
javaEnchantIndex == -1 ? "unknown" : ENCHANT_NAMES.get(javaEnchantIndex), enchantLevel == -1 ? 0 : 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 static GeyserItemStack from(ItemStack itemStack) {
|
||||||
|
return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getNbt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getJavaId() {
|
||||||
|
return isEmpty() ? 0 : javaId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAmount() {
|
||||||
|
return isEmpty() ? 0 : amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompoundTag getNbt() {
|
||||||
|
return isEmpty() ? null : nbt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNetId() {
|
||||||
|
return isEmpty() ? 0 : netId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(int add) {
|
||||||
|
amount += add;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sub(int sub) {
|
||||||
|
amount -= sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack getItemStack() {
|
||||||
|
return getItemStack(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack getItemStack(int newAmount) {
|
||||||
|
return isEmpty() ? null : new ItemStack(javaId, newAmount, 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,36 +25,39 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.inventory;
|
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.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.NonNull;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class Inventory {
|
public class Inventory {
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
protected int id;
|
protected final int id;
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
protected boolean open;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
protected WindowType windowType;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
protected final int size;
|
protected final int size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for smooth transitions between two windows of the same type.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
protected final WindowType windowType;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
protected String title;
|
protected String title;
|
||||||
|
|
||||||
@Setter
|
protected GeyserItemStack[] items;
|
||||||
protected ItemStack[] items;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The location of the inventory block. Will either be a fake block above the player's head, or the actual block location
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
protected Vector3i holderPosition = Vector3i.ZERO;
|
protected Vector3i holderPosition = Vector3i.ZERO;
|
||||||
@ -64,27 +67,67 @@ public class Inventory {
|
|||||||
protected long holderId = -1;
|
protected long holderId = -1;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
protected AtomicInteger transactionId = new AtomicInteger(1);
|
protected short transactionId = 0;
|
||||||
|
|
||||||
public Inventory(int id, WindowType windowType, int size) {
|
@Getter
|
||||||
this("Inventory", id, windowType, size);
|
@Setter
|
||||||
|
private boolean pending = false;
|
||||||
|
|
||||||
|
protected Inventory(int id, int size, WindowType windowType) {
|
||||||
|
this("Inventory", id, size, windowType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Inventory(String title, int id, WindowType windowType, int size) {
|
protected Inventory(String title, int id, int size, WindowType windowType) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.windowType = windowType;
|
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.items = new ItemStack[size];
|
this.windowType = windowType;
|
||||||
|
this.items = new GeyserItemStack[size];
|
||||||
|
Arrays.fill(items, GeyserItemStack.EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemStack getItem(int slot) {
|
public GeyserItemStack getItem(int slot) {
|
||||||
|
if (slot > this.size) {
|
||||||
|
GeyserConnector.getInstance().getLogger().debug("Tried to get an item out of bounds! " + this.toString());
|
||||||
|
return GeyserItemStack.EMPTY;
|
||||||
|
}
|
||||||
return items[slot];
|
return items[slot];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setItem(int slot, ItemStack item) {
|
public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) {
|
||||||
if (item != null && (item.getId() == 0 || item.getAmount() < 1))
|
if (slot > this.size) {
|
||||||
item = null;
|
session.getConnector().getLogger().debug("Tried to set an item out of bounds! " + this.toString());
|
||||||
items[slot] = item;
|
return;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Inventory{" +
|
||||||
|
"id=" + id +
|
||||||
|
", size=" + size +
|
||||||
|
", title='" + title + '\'' +
|
||||||
|
", items=" + Arrays.toString(items) +
|
||||||
|
", holderPosition=" + holderPosition +
|
||||||
|
", holderId=" + holderId +
|
||||||
|
", transactionId=" + transactionId +
|
||||||
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.inventory;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
|
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, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
|
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, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
}
|
@ -25,9 +25,11 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.inventory;
|
package org.geysermc.connector.inventory;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.NonNull;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
|
||||||
public class PlayerInventory extends Inventory {
|
public class PlayerInventory extends Inventory {
|
||||||
|
|
||||||
@ -40,20 +42,36 @@ public class PlayerInventory extends Inventory {
|
|||||||
private int heldItemSlot;
|
private int heldItemSlot;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private ItemStack cursor;
|
@NonNull
|
||||||
|
private GeyserItemStack cursor = GeyserItemStack.EMPTY;
|
||||||
|
|
||||||
public PlayerInventory() {
|
public PlayerInventory() {
|
||||||
super(0, null, 46);
|
super(0, 46, null);
|
||||||
heldItemSlot = 0;
|
heldItemSlot = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCursor(ItemStack stack) {
|
public void setCursor(@NonNull GeyserItemStack newCursor, GeyserSession session) {
|
||||||
if (stack != null && (stack.getId() == 0 || stack.getAmount() < 1))
|
updateItemNetId(cursor, newCursor, session);
|
||||||
stack = null;
|
cursor = newCursor;
|
||||||
cursor = stack;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemStack getItemInHand() {
|
public GeyserItemStack getItemInHand() {
|
||||||
|
if (36 + heldItemSlot > this.size) {
|
||||||
|
GeyserConnector.getInstance().getLogger().debug("Held item slot was larger than expected!");
|
||||||
|
return GeyserItemStack.EMPTY;
|
||||||
|
}
|
||||||
return items[36 + heldItemSlot];
|
return items[36 + heldItemSlot];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setItemInHand(@NonNull GeyserItemStack item) {
|
||||||
|
if (36 + heldItemSlot > this.size) {
|
||||||
|
GeyserConnector.getInstance().getLogger().debug("Held item slot was larger than expected!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
items[36 + heldItemSlot] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeyserItemStack getOffhand() {
|
||||||
|
return items[45];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,39 +23,32 @@
|
|||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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 com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.NonNull;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
|
||||||
public class InventoryCache {
|
public class StonecutterContainer extends Container {
|
||||||
|
/**
|
||||||
private GeyserSession session;
|
* The button that has currently been pressed Java-side
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private Inventory openInventory;
|
private int stonecutterButton = -1;
|
||||||
|
|
||||||
@Getter
|
public StonecutterContainer(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
private Int2ObjectMap<Inventory> inventories = new Int2ObjectOpenHashMap<>();
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
|
||||||
public InventoryCache(GeyserSession session) {
|
|
||||||
this.session = session;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Inventory getPlayerInventory() {
|
@Override
|
||||||
return inventories.get(0);
|
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
|
||||||
public void cacheInventory(Inventory inventory) {
|
this.stonecutterButton = -1;
|
||||||
inventories.put(inventory.getId(), inventory);
|
}
|
||||||
}
|
super.setItem(slot, newItem, session);
|
||||||
|
|
||||||
public void uncacheInventory(int id) {
|
|
||||||
inventories.remove(id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -36,8 +36,8 @@ import com.github.steveice10.mc.protocol.MinecraftConstants;
|
|||||||
import com.github.steveice10.mc.protocol.MinecraftProtocol;
|
import com.github.steveice10.mc.protocol.MinecraftProtocol;
|
||||||
import com.github.steveice10.mc.protocol.data.SubProtocol;
|
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.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.statistic.Statistic;
|
||||||
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
|
|
||||||
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
|
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket;
|
||||||
@ -58,12 +58,15 @@ import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
|||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
import com.nukkitx.protocol.bedrock.packet.*;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
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.Long2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||||
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@ -76,6 +79,7 @@ import org.geysermc.connector.entity.Entity;
|
|||||||
import org.geysermc.connector.entity.Tickable;
|
import org.geysermc.connector.entity.Tickable;
|
||||||
import org.geysermc.connector.entity.player.SessionPlayerEntity;
|
import org.geysermc.connector.entity.player.SessionPlayerEntity;
|
||||||
import org.geysermc.connector.entity.player.SkullPlayerEntity;
|
import org.geysermc.connector.entity.player.SkullPlayerEntity;
|
||||||
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
import org.geysermc.connector.inventory.PlayerInventory;
|
import org.geysermc.connector.inventory.PlayerInventory;
|
||||||
import org.geysermc.connector.network.remote.RemoteServer;
|
import org.geysermc.connector.network.remote.RemoteServer;
|
||||||
import org.geysermc.connector.network.session.auth.AuthData;
|
import org.geysermc.connector.network.session.auth.AuthData;
|
||||||
@ -86,7 +90,7 @@ import org.geysermc.connector.network.translators.EntityIdentifierRegistry;
|
|||||||
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
||||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||||
import org.geysermc.connector.network.translators.collision.CollisionManager;
|
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.network.translators.item.ItemRegistry;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
import org.geysermc.connector.skin.SkinManager;
|
import org.geysermc.connector.skin.SkinManager;
|
||||||
@ -101,9 +105,8 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.spec.InvalidKeySpecException;
|
import java.security.spec.InvalidKeySpecException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class GeyserSession implements CommandSender {
|
public class GeyserSession implements CommandSender {
|
||||||
@ -122,18 +125,39 @@ public class GeyserSession implements CommandSender {
|
|||||||
private boolean microsoftAccount;
|
private boolean microsoftAccount;
|
||||||
|
|
||||||
private final SessionPlayerEntity playerEntity;
|
private final SessionPlayerEntity playerEntity;
|
||||||
private PlayerInventory inventory;
|
|
||||||
|
|
||||||
private AdvancementsCache advancementsCache;
|
private AdvancementsCache advancementsCache;
|
||||||
private BookEditCache bookEditCache;
|
private BookEditCache bookEditCache;
|
||||||
private ChunkCache chunkCache;
|
private ChunkCache chunkCache;
|
||||||
private EntityCache entityCache;
|
private EntityCache entityCache;
|
||||||
private EntityEffectCache effectCache;
|
private EntityEffectCache effectCache;
|
||||||
private InventoryCache inventoryCache;
|
|
||||||
private WorldCache worldCache;
|
private WorldCache worldCache;
|
||||||
private WindowCache windowCache;
|
private WindowCache windowCache;
|
||||||
private final Int2ObjectMap<TeleportCache> teleportMap = new Int2ObjectOpenHashMap<>();
|
private final Int2ObjectMap<TeleportCache> teleportMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
|
private final PlayerInventory playerInventory;
|
||||||
|
@Setter
|
||||||
|
private Inventory openInventory;
|
||||||
|
@Setter
|
||||||
|
private boolean closingInventory;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private InventoryTranslator inventoryTranslator = InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use {@link #getNextItemNetId()} instead for consistency
|
||||||
|
*/
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
private final AtomicInteger itemNetId = new AtomicInteger(2);
|
||||||
|
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
private final Object inventoryLock = new Object();
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
private CompletableFuture<Void> inventoryFuture;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private ScheduledFuture<?> craftingGridFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores session collision
|
* Stores session collision
|
||||||
*/
|
*/
|
||||||
@ -154,6 +178,16 @@ public class GeyserSession implements CommandSender {
|
|||||||
*/
|
*/
|
||||||
private final Object2LongMap<Vector3i> itemFrameCache = new Object2LongOpenHashMap<>();
|
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 Set<Vector3i> lecternCache = new ObjectOpenHashSet<>();
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private boolean droppingLecternBook;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private Vector2i lastChunkPosition = null;
|
private Vector2i lastChunkPosition = null;
|
||||||
private int renderDistance;
|
private int renderDistance;
|
||||||
@ -210,26 +244,30 @@ public class GeyserSession implements CommandSender {
|
|||||||
* Initialized as (0, 0, 0) so it is always not-null.
|
* Initialized as (0, 0, 0) so it is always not-null.
|
||||||
*/
|
*/
|
||||||
@Setter
|
@Setter
|
||||||
private Vector3i lastInteractionPosition = Vector3i.ZERO;
|
private Vector3i lastInteractionBlockPosition = Vector3i.ZERO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the position of the player the last time they interacted.
|
||||||
|
* Used to verify that the player did not move since their last interaction. <br>
|
||||||
|
* Initialized as (0, 0, 0) so it is always not-null.
|
||||||
|
*/
|
||||||
|
@Setter
|
||||||
|
private Vector3f lastInteractionPlayerPosition = Vector3f.ZERO;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private Entity ridingVehicleEntity;
|
private Entity ridingVehicleEntity;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private int craftSlot = 0;
|
private Int2ObjectMap<Recipe> craftingRecipes;
|
||||||
|
private final Set<String> unlockedRecipes;
|
||||||
@Setter
|
private final AtomicInteger lastRecipeNetId;
|
||||||
private long lastWindowCloseTime = 0;
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
private VillagerTrade[] villagerTrades;
|
|
||||||
@Setter
|
|
||||||
private long lastInteractedVillagerEid;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
* The current attack speed of the player. Used for sending proper cooldown timings.
|
||||||
@ -368,20 +406,25 @@ public class GeyserSession implements CommandSender {
|
|||||||
this.chunkCache = new ChunkCache(this);
|
this.chunkCache = new ChunkCache(this);
|
||||||
this.entityCache = new EntityCache(this);
|
this.entityCache = new EntityCache(this);
|
||||||
this.effectCache = new EntityEffectCache();
|
this.effectCache = new EntityEffectCache();
|
||||||
this.inventoryCache = new InventoryCache(this);
|
|
||||||
this.worldCache = new WorldCache(this);
|
this.worldCache = new WorldCache(this);
|
||||||
this.windowCache = new WindowCache(this);
|
this.windowCache = new WindowCache(this);
|
||||||
|
|
||||||
this.collisionManager = new CollisionManager(this);
|
this.collisionManager = new CollisionManager(this);
|
||||||
|
|
||||||
this.playerEntity = new SessionPlayerEntity(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.lastRecipeNetId = new AtomicInteger(1);
|
||||||
|
|
||||||
this.spawned = false;
|
this.spawned = false;
|
||||||
this.loggedIn = false;
|
this.loggedIn = false;
|
||||||
|
|
||||||
this.inventoryCache.getInventories().put(0, inventory);
|
|
||||||
|
|
||||||
// Make a copy to prevent ConcurrentModificationException
|
// Make a copy to prevent ConcurrentModificationException
|
||||||
final List<GeyserSession> tmpPlayers = new ArrayList<>(connector.getPlayers());
|
final List<GeyserSession> tmpPlayers = new ArrayList<>(connector.getPlayers());
|
||||||
tmpPlayers.forEach(player -> this.emotes.addAll(player.getEmotes()));
|
tmpPlayers.forEach(player -> this.emotes.addAll(player.getEmotes()));
|
||||||
@ -709,7 +752,6 @@ public class GeyserSession implements CommandSender {
|
|||||||
this.entityCache = null;
|
this.entityCache = null;
|
||||||
this.effectCache = null;
|
this.effectCache = null;
|
||||||
this.worldCache = null;
|
this.worldCache = null;
|
||||||
this.inventoryCache = null;
|
|
||||||
this.windowCache = null;
|
this.windowCache = null;
|
||||||
|
|
||||||
closed = true;
|
closed = true;
|
||||||
@ -849,6 +891,7 @@ public class GeyserSession implements CommandSender {
|
|||||||
startGamePacket.setMultiplayerCorrelationId("");
|
startGamePacket.setMultiplayerCorrelationId("");
|
||||||
startGamePacket.setItemEntries(ItemRegistry.ITEMS);
|
startGamePacket.setItemEntries(ItemRegistry.ITEMS);
|
||||||
startGamePacket.setVanillaVersion("*");
|
startGamePacket.setVanillaVersion("*");
|
||||||
|
startGamePacket.setInventoriesServerAuthoritative(true);
|
||||||
startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT); // can be removed once 1.16.200 support is dropped
|
startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT); // can be removed once 1.16.200 support is dropped
|
||||||
|
|
||||||
SyncedPlayerMovementSettings settings = new SyncedPlayerMovementSettings();
|
SyncedPlayerMovementSettings settings = new SyncedPlayerMovementSettings();
|
||||||
@ -860,6 +903,46 @@ public class GeyserSession implements CommandSender {
|
|||||||
upstream.sendPacket(startGamePacket);
|
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) {
|
||||||
|
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) {
|
||||||
|
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) {
|
public void addTeleport(TeleportCache teleportCache) {
|
||||||
teleportMap.put(teleportCache.getTeleportConfirmId(), teleportCache);
|
teleportMap.put(teleportCache.getTeleportConfirmId(), teleportCache);
|
||||||
|
|
||||||
|
@ -25,9 +25,9 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.network.session.cache;
|
package org.geysermc.connector.network.session.cache;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientEditBookPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientEditBookPacket;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.geysermc.connector.inventory.GeyserItemStack;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||||
|
|
||||||
@ -63,8 +63,8 @@ public class BookEditCache {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Don't send the update if the player isn't not holding a book, shouldn't happen if we catch all interactions
|
// Don't send the update if the player isn't not holding a book, shouldn't happen if we catch all interactions
|
||||||
ItemStack itemStack = session.getInventory().getItemInHand();
|
GeyserItemStack itemStack = session.getPlayerInventory().getItemInHand();
|
||||||
if (itemStack == null || itemStack.getId() != ItemRegistry.WRITABLE_BOOK.getJavaId()) {
|
if (itemStack == null || itemStack.getJavaId() != ItemRegistry.WRITABLE_BOOK.getJavaId()) {
|
||||||
packet = null;
|
packet = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -26,17 +26,16 @@
|
|||||||
package org.geysermc.connector.network.translators.bedrock;
|
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.ItemStack;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientEditBookPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientEditBookPacket;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||||
import com.nukkitx.protocol.bedrock.packet.BookEditPacket;
|
import com.nukkitx.protocol.bedrock.packet.BookEditPacket;
|
||||||
|
import org.geysermc.connector.inventory.GeyserItemStack;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@ -47,58 +46,55 @@ public class BedrockBookEditTranslator extends PacketTranslator<BookEditPacket>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(BookEditPacket packet, GeyserSession session) {
|
public void translate(BookEditPacket packet, GeyserSession session) {
|
||||||
ItemStack itemStack = session.getInventory().getItemInHand();
|
GeyserItemStack itemStack = session.getPlayerInventory().getItemInHand();
|
||||||
if (itemStack != null) {
|
if (itemStack != null) {
|
||||||
CompoundTag tag = itemStack.getNbt() != null ? itemStack.getNbt() : new CompoundTag("");
|
CompoundTag tag = itemStack.getNbt() != null ? itemStack.getNbt() : new CompoundTag("");
|
||||||
ItemStack bookItem = new ItemStack(itemStack.getId(), itemStack.getAmount(), tag);
|
ItemStack bookItem = new ItemStack(itemStack.getJavaId(), itemStack.getAmount(), tag);
|
||||||
List<Tag> pages = tag.contains("pages") ? new LinkedList<>(((ListTag) tag.get("pages")).getValue()) : new LinkedList<>();
|
List<Tag> pages = tag.contains("pages") ? new LinkedList<>(((ListTag) tag.get("pages")).getValue()) : new LinkedList<>();
|
||||||
|
|
||||||
int page = packet.getPageNumber();
|
int page = packet.getPageNumber();
|
||||||
// Creative edits the NBT for us
|
switch (packet.getAction()) {
|
||||||
if (session.getGameMode() != GameMode.CREATIVE) {
|
case ADD_PAGE: {
|
||||||
switch (packet.getAction()) {
|
// Add empty pages in between
|
||||||
case ADD_PAGE: {
|
for (int i = pages.size(); i < page; i++) {
|
||||||
|
pages.add(i, new StringTag("", ""));
|
||||||
|
}
|
||||||
|
pages.add(page, new StringTag("", packet.getText()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Called whenever a page is modified
|
||||||
|
case REPLACE_PAGE: {
|
||||||
|
if (page < pages.size()) {
|
||||||
|
pages.set(page, new StringTag("", packet.getText()));
|
||||||
|
} else {
|
||||||
// Add empty pages in between
|
// Add empty pages in between
|
||||||
for (int i = pages.size(); i < page; i++) {
|
for (int i = pages.size(); i < page; i++) {
|
||||||
pages.add(i, new StringTag("", ""));
|
pages.add(i, new StringTag("", ""));
|
||||||
}
|
}
|
||||||
pages.add(page, new StringTag("", packet.getText()));
|
pages.add(page, new StringTag("", packet.getText()));
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
// Called whenever a page is modified
|
break;
|
||||||
case REPLACE_PAGE: {
|
|
||||||
if (page < pages.size()) {
|
|
||||||
pages.set(page, new StringTag("", packet.getText()));
|
|
||||||
} else {
|
|
||||||
// Add empty pages in between
|
|
||||||
for (int i = pages.size(); i < page; i++) {
|
|
||||||
pages.add(i, new StringTag("", ""));
|
|
||||||
}
|
|
||||||
pages.add(page, new StringTag("", packet.getText()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DELETE_PAGE: {
|
|
||||||
if (page < pages.size()) {
|
|
||||||
pages.remove(page);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SWAP_PAGES: {
|
|
||||||
int page2 = packet.getSecondaryPageNumber();
|
|
||||||
if (page < pages.size() && page2 < pages.size()) {
|
|
||||||
Collections.swap(pages, page, page2);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGN_BOOK: {
|
|
||||||
tag.put(new StringTag("author", packet.getAuthor()));
|
|
||||||
tag.put(new StringTag("title", packet.getTitle()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
case DELETE_PAGE: {
|
||||||
|
if (page < pages.size()) {
|
||||||
|
pages.remove(page);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SWAP_PAGES: {
|
||||||
|
int page2 = packet.getSecondaryPageNumber();
|
||||||
|
if (page < pages.size() && page2 < pages.size()) {
|
||||||
|
Collections.swap(pages, page, page2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SIGN_BOOK: {
|
||||||
|
tag.put(new StringTag("author", packet.getAuthor()));
|
||||||
|
tag.put(new StringTag("title", packet.getTitle()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// Remove empty pages at the end
|
// Remove empty pages at the end
|
||||||
while (pages.size() > 0) {
|
while (pages.size() > 0) {
|
||||||
@ -110,10 +106,10 @@ public class BedrockBookEditTranslator extends PacketTranslator<BookEditPacket>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tag.put(new ListTag("pages", pages));
|
tag.put(new ListTag("pages", pages));
|
||||||
session.getInventory().setItem(36 + session.getInventory().getHeldItemSlot(), bookItem);
|
session.getPlayerInventory().setItem(36 + session.getPlayerInventory().getHeldItemSlot(), GeyserItemStack.from(bookItem), session);
|
||||||
InventoryTranslator.INVENTORY_TRANSLATORS.get(null).updateInventory(session, session.getInventory());
|
session.getInventoryTranslator().updateInventory(session, session.getPlayerInventory());
|
||||||
|
|
||||||
session.getBookEditCache().setPacket(new ClientEditBookPacket(bookItem, packet.getAction() == BookEditPacket.Action.SIGN_BOOK, session.getInventory().getHeldItemSlot()));
|
session.getBookEditCache().setPacket(new ClientEditBookPacket(bookItem, packet.getAction() == BookEditPacket.Action.SIGN_BOOK, session.getPlayerInventory().getHeldItemSlot()));
|
||||||
// There won't be any more book updates after this, so we can try sending the edit packet immediately
|
// There won't be any more book updates after this, so we can try sending the edit packet immediately
|
||||||
if (packet.getAction() == BookEditPacket.Action.SIGN_BOOK) {
|
if (packet.getAction() == BookEditPacket.Action.SIGN_BOOK) {
|
||||||
session.getBookEditCache().checkForSend();
|
session.getBookEditCache().checkForSend();
|
||||||
|
@ -28,6 +28,7 @@ package org.geysermc.connector.network.translators.bedrock;
|
|||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
|
import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
|
import org.geysermc.connector.inventory.MerchantContainer;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
@ -38,24 +39,29 @@ public class BedrockContainerCloseTranslator extends PacketTranslator<ContainerC
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ContainerClosePacket packet, GeyserSession session) {
|
public void translate(ContainerClosePacket packet, GeyserSession session) {
|
||||||
session.setLastWindowCloseTime(0);
|
session.addInventoryTask(() -> {
|
||||||
byte windowId = packet.getId();
|
byte windowId = packet.getId();
|
||||||
Inventory openInventory = session.getInventoryCache().getOpenInventory();
|
|
||||||
if (windowId == -1) { //player inventory or crafting table
|
//Client wants close confirmation
|
||||||
if (openInventory != null) {
|
session.sendUpstreamPacket(packet);
|
||||||
windowId = (byte) openInventory.getId();
|
session.setClosingInventory(false);
|
||||||
} else {
|
|
||||||
windowId = 0;
|
if (windowId == -1 && session.getOpenInventory() instanceof MerchantContainer) {
|
||||||
|
// 1.16.200 - window ID is always -1 sent from Bedrock
|
||||||
|
windowId = (byte) session.getOpenInventory().getId();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (windowId == 0 || (openInventory != null && openInventory.getId() == windowId)) {
|
Inventory openInventory = session.getOpenInventory();
|
||||||
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
|
if (openInventory != null) {
|
||||||
session.getDownstream().getSession().send(closeWindowPacket);
|
if (windowId == openInventory.getId()) {
|
||||||
InventoryUtils.closeInventory(session, windowId);
|
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
|
||||||
}
|
session.sendDownstreamPacket(closeWindowPacket);
|
||||||
|
InventoryUtils.closeInventory(session, windowId, false);
|
||||||
//Client wants close confirmation
|
} else if (openInventory.isPending()) {
|
||||||
session.sendUpstreamPacket(packet);
|
InventoryUtils.displayInventory(session, openInventory);
|
||||||
|
openInventory.setPending(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,10 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.network.translators.bedrock;
|
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 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.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
@ -39,7 +42,17 @@ public class BedrockFilterTextTranslator extends PacketTranslator<FilterTextPack
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(FilterTextPacket packet, GeyserSession session) {
|
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);
|
packet.setFromServer(true);
|
||||||
session.sendUpstreamPacket(packet);
|
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());
|
||||||
|
session.sendDownstreamPacket(renameItemPacket);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.network.translators.bedrock;
|
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.metadata.Position;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||||
@ -43,23 +42,22 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
|||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlags;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityFlags;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
|
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
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 com.nukkitx.protocol.bedrock.packet.*;
|
import com.nukkitx.protocol.bedrock.packet.*;
|
||||||
import org.geysermc.connector.entity.CommandBlockMinecartEntity;
|
import org.geysermc.connector.entity.CommandBlockMinecartEntity;
|
||||||
import org.geysermc.connector.entity.Entity;
|
import org.geysermc.connector.entity.Entity;
|
||||||
import org.geysermc.connector.entity.ItemFrameEntity;
|
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.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.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
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.ItemEntry;
|
||||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||||
import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler;
|
import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
import org.geysermc.connector.utils.BlockUtils;
|
import org.geysermc.connector.utils.BlockUtils;
|
||||||
import org.geysermc.connector.utils.InventoryUtils;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@ -82,15 +80,35 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
|
|
||||||
switch (packet.getTransactionType()) {
|
switch (packet.getTransactionType()) {
|
||||||
case NORMAL:
|
case NORMAL:
|
||||||
Inventory inventory = session.getInventoryCache().getOpenInventory();
|
if (packet.getActions().size() == 2) {
|
||||||
if (inventory == null) inventory = session.getInventory();
|
InventoryActionData worldAction = packet.getActions().get(0);
|
||||||
InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()).translateActions(session, inventory, packet.getActions());
|
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() ||
|
||||||
|
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;
|
break;
|
||||||
case INVENTORY_MISMATCH:
|
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;
|
break;
|
||||||
case ITEM_USE:
|
case ITEM_USE:
|
||||||
switch (packet.getActionType()) {
|
switch (packet.getActionType()) {
|
||||||
@ -98,8 +116,9 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
// Check to make sure the client isn't spamming interaction
|
// Check to make sure the client isn't spamming interaction
|
||||||
// Based on Nukkit 1.0, with changes to ensure holding down still works
|
// Based on Nukkit 1.0, with changes to ensure holding down still works
|
||||||
boolean hasAlreadyClicked = System.currentTimeMillis() - session.getLastInteractionTime() < 110.0 &&
|
boolean hasAlreadyClicked = System.currentTimeMillis() - session.getLastInteractionTime() < 110.0 &&
|
||||||
packet.getBlockPosition().distanceSquared(session.getLastInteractionPosition()) < 0.00001;
|
packet.getBlockPosition().distanceSquared(session.getLastInteractionBlockPosition()) < 0.00001;
|
||||||
session.setLastInteractionPosition(packet.getBlockPosition());
|
session.setLastInteractionBlockPosition(packet.getBlockPosition());
|
||||||
|
session.setLastInteractionPlayerPosition(session.getPlayerEntity().getPosition());
|
||||||
if (hasAlreadyClicked) {
|
if (hasAlreadyClicked) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@ -215,9 +234,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
session.setInteracting(true);
|
session.setInteracting(true);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
ItemStack shieldSlot = session.getInventory().getItem(session.getInventory().getHeldItemSlot() + 36);
|
|
||||||
// Handled in Entity.java
|
// Handled in Entity.java
|
||||||
if (shieldSlot != null && shieldSlot.getId() == ItemRegistry.SHIELD.getJavaId()) {
|
if (session.getPlayerInventory().getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,10 +326,6 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
session.sendDownstreamPacket(interactAtPacket);
|
session.sendDownstreamPacket(interactAtPacket);
|
||||||
|
|
||||||
EntitySoundInteractionHandler.handleEntityInteraction(session, vector, entity);
|
EntitySoundInteractionHandler.handleEntityInteraction(session, vector, entity);
|
||||||
|
|
||||||
if (entity instanceof AbstractMerchantEntity) {
|
|
||||||
session.setLastInteractedVillagerEid(packet.getRuntimeEntityId());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 1: //Attack
|
case 1: //Attack
|
||||||
if (entity.getEntityType() == EntityType.ENDER_DRAGON) {
|
if (entity.getEntityType() == EntityType.ENDER_DRAGON) {
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The packet sent for server-authoritative-style inventory transactions.
|
||||||
|
*/
|
||||||
|
@Translator(packet = ItemStackRequestPacket.class)
|
||||||
|
public class BedrockItemStackRequestTranslator extends PacketTranslator<ItemStackRequestPacket> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translate(ItemStackRequestPacket packet, GeyserSession session) {
|
||||||
|
Inventory inventory = session.getOpenInventory();
|
||||||
|
if (inventory == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
InventoryTranslator translator = session.getInventoryTranslator();
|
||||||
|
session.addInventoryTask(() -> translator.translateRequests(session, inventory, packet.getRequests()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* 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.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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to translate moving pages, or closing the inventory
|
||||||
|
*/
|
||||||
|
@Translator(packet = LecternUpdatePacket.class)
|
||||||
|
public class BedrockLecternUpdateTranslator extends PacketTranslator<LecternUpdatePacket> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translate(LecternUpdatePacket packet, GeyserSession session) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
0, 0, 0, // Java doesn't care about these when dealing with a lectern
|
||||||
|
false);
|
||||||
|
session.sendDownstreamPacket(blockPacket);
|
||||||
|
} else {
|
||||||
|
// Bedrock wants to either move a page or exit
|
||||||
|
if (!(session.getOpenInventory() instanceof LecternContainer)) {
|
||||||
|
session.getConnector().getLogger().debug("Expected lectern but it wasn't open!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(), false);
|
||||||
|
} 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 when transmitted over the network 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);
|
||||||
|
session.sendDownstreamPacket(clickButtonPacket);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = currentJavaPage; i > newJavaPage; i--) {
|
||||||
|
ClientClickWindowButtonPacket clickButtonPacket = new ClientClickWindowButtonPacket(session.getOpenInventory().getId(), 1);
|
||||||
|
session.sendDownstreamPacket(clickButtonPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,7 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
|
|||||||
@Override
|
@Override
|
||||||
public void translate(MobEquipmentPacket packet, GeyserSession session) {
|
public void translate(MobEquipmentPacket packet, GeyserSession session) {
|
||||||
if (!session.isSpawned() || packet.getHotbarSlot() > 8 ||
|
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
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
|
|||||||
// Send book update before switching hotbar slot
|
// Send book update before switching hotbar slot
|
||||||
session.getBookEditCache().checkForSend();
|
session.getBookEditCache().checkForSend();
|
||||||
|
|
||||||
session.getInventory().setHeldItemSlot(packet.getHotbarSlot());
|
session.getPlayerInventory().setHeldItemSlot(packet.getHotbarSlot());
|
||||||
|
|
||||||
ClientPlayerChangeHeldItemPacket changeHeldItemPacket = new ClientPlayerChangeHeldItemPacket(packet.getHotbarSlot());
|
ClientPlayerChangeHeldItemPacket changeHeldItemPacket = new ClientPlayerChangeHeldItemPacket(packet.getHotbarSlot());
|
||||||
session.sendDownstreamPacket(changeHeldItemPacket);
|
session.sendDownstreamPacket(changeHeldItemPacket);
|
||||||
|
@ -26,12 +26,13 @@
|
|||||||
package org.geysermc.connector.network.translators.bedrock.entity;
|
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.VillagerTrade;
|
||||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientSelectTradePacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientSelectTradePacket;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
||||||
import org.geysermc.connector.entity.Entity;
|
import org.geysermc.connector.entity.Entity;
|
||||||
|
import org.geysermc.connector.inventory.GeyserItemStack;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
|
import org.geysermc.connector.inventory.MerchantContainer;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
@ -47,20 +48,25 @@ public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPa
|
|||||||
session.sendUpstreamPacket(packet);
|
session.sendUpstreamPacket(packet);
|
||||||
return;
|
return;
|
||||||
case COMPLETE_TRADE:
|
case COMPLETE_TRADE:
|
||||||
ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
|
session.addInventoryTask(() -> {
|
||||||
session.sendDownstreamPacket(selectTradePacket);
|
ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
|
||||||
|
session.sendDownstreamPacket(selectTradePacket);
|
||||||
|
});
|
||||||
|
|
||||||
Entity villager = session.getPlayerEntity();
|
session.addInventoryTask(() -> {
|
||||||
Inventory openInventory = session.getInventoryCache().getOpenInventory();
|
Entity villager = session.getPlayerEntity();
|
||||||
if (openInventory != null && openInventory.getWindowType() == WindowType.MERCHANT) {
|
Inventory openInventory = session.getOpenInventory();
|
||||||
VillagerTrade[] trades = session.getVillagerTrades();
|
if (openInventory instanceof MerchantContainer) {
|
||||||
if (trades != null && packet.getData() >= 0 && packet.getData() < trades.length) {
|
MerchantContainer merchantInventory = (MerchantContainer) openInventory;
|
||||||
VillagerTrade trade = session.getVillagerTrades()[packet.getData()];
|
VillagerTrade[] trades = merchantInventory.getVillagerTrades();
|
||||||
openInventory.setItem(2, trade.getOutput());
|
if (trades != null && packet.getData() >= 0 && packet.getData() < trades.length) {
|
||||||
villager.getMetadata().put(EntityData.TRADE_XP, trade.getXp() + villager.getMetadata().getInt(EntityData.TRADE_XP));
|
VillagerTrade trade = merchantInventory.getVillagerTrades()[packet.getData()];
|
||||||
villager.updateBedrockMetadata(session);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString());
|
session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString());
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.network.translators.bedrock.entity.player;
|
package org.geysermc.connector.network.translators.bedrock.entity.player;
|
||||||
|
|
||||||
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.metadata.Position;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.*;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.*;
|
||||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
|
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
|
||||||
@ -44,12 +43,12 @@ import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket;
|
|||||||
import com.nukkitx.protocol.bedrock.packet.PlayerActionPacket;
|
import com.nukkitx.protocol.bedrock.packet.PlayerActionPacket;
|
||||||
import org.geysermc.connector.entity.Entity;
|
import org.geysermc.connector.entity.Entity;
|
||||||
import org.geysermc.connector.entity.ItemFrameEntity;
|
import org.geysermc.connector.entity.ItemFrameEntity;
|
||||||
|
import org.geysermc.connector.inventory.GeyserItemStack;
|
||||||
import org.geysermc.connector.inventory.PlayerInventory;
|
import org.geysermc.connector.inventory.PlayerInventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
import org.geysermc.connector.utils.BlockUtils;
|
import org.geysermc.connector.utils.BlockUtils;
|
||||||
|
|
||||||
@ -144,12 +143,12 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
|||||||
LevelEventPacket startBreak = new LevelEventPacket();
|
LevelEventPacket startBreak = new LevelEventPacket();
|
||||||
startBreak.setType(LevelEventType.BLOCK_START_BREAK);
|
startBreak.setType(LevelEventType.BLOCK_START_BREAK);
|
||||||
startBreak.setPosition(vector.toFloat());
|
startBreak.setPosition(vector.toFloat());
|
||||||
PlayerInventory inventory = session.getInventory();
|
PlayerInventory inventory = session.getPlayerInventory();
|
||||||
ItemStack item = inventory.getItemInHand();
|
GeyserItemStack item = inventory.getItemInHand();
|
||||||
ItemEntry itemEntry = null;
|
ItemEntry itemEntry = null;
|
||||||
CompoundTag nbtData = new CompoundTag("");
|
CompoundTag nbtData = new CompoundTag("");
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
itemEntry = ItemRegistry.getItem(item);
|
itemEntry = item.getItemEntry();
|
||||||
nbtData = item.getNbt();
|
nbtData = item.getNbt();
|
||||||
}
|
}
|
||||||
double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, blockState, itemEntry, nbtData, session) * 20);
|
double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, blockState, itemEntry, nbtData, session) * 20);
|
||||||
|
@ -38,6 +38,8 @@ import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
|
|||||||
import com.nukkitx.protocol.bedrock.packet.InteractPacket;
|
import com.nukkitx.protocol.bedrock.packet.InteractPacket;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.geysermc.connector.entity.Entity;
|
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.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
@ -98,7 +100,7 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
|
|||||||
|
|
||||||
switch (packet.getAction()) {
|
switch (packet.getAction()) {
|
||||||
case INTERACT:
|
case INTERACT:
|
||||||
if (session.getInventory().getItem(session.getInventory().getHeldItemSlot() + 36).getId() == ItemRegistry.SHIELD.getJavaId()) {
|
if (session.getPlayerInventory().getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
|
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
|
||||||
@ -122,7 +124,7 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
|
|||||||
if (interactEntity == null)
|
if (interactEntity == null)
|
||||||
return;
|
return;
|
||||||
EntityDataMap entityMetadata = interactEntity.getMetadata();
|
EntityDataMap entityMetadata = interactEntity.getMetadata();
|
||||||
ItemEntry itemEntry = session.getInventory().getItemInHand() == null ? ItemEntry.AIR : ItemRegistry.getItem(session.getInventory().getItemInHand());
|
ItemEntry itemEntry = session.getPlayerInventory().getItemInHand().getItemEntry();
|
||||||
String javaIdentifierStripped = itemEntry.getJavaIdentifier().replace("minecraft:", "");
|
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
|
// 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 +138,8 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
|
|||||||
interactEntity.getEntityType() == EntityType.PIG || interactEntity.getEntityType() == EntityType.STRIDER)) {
|
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)
|
// Entity can be saddled and the conditions meet (entity can be saddled and, if needed, is tamed)
|
||||||
interactiveTag = InteractiveTag.SADDLE;
|
interactiveTag = InteractiveTag.SADDLE;
|
||||||
} else if (javaIdentifierStripped.equals("name_tag") && session.getInventory().getItemInHand().getNbt() != null &&
|
} else if (javaIdentifierStripped.equals("name_tag") && session.getPlayerInventory().getItemInHand().getNbt() != null &&
|
||||||
session.getInventory().getItemInHand().getNbt().contains("display")) {
|
session.getPlayerInventory().getItemInHand().getNbt().contains("display")) {
|
||||||
// Holding a named name tag
|
// Holding a named name tag
|
||||||
interactiveTag = InteractiveTag.NAME;
|
interactiveTag = InteractiveTag.NAME;
|
||||||
} else if (javaIdentifierStripped.equals("lead") && LEASHABLE_MOB_TYPES.contains(interactEntity.getEntityType()) &&
|
} else if (javaIdentifierStripped.equals("lead") && LEASHABLE_MOB_TYPES.contains(interactEntity.getEntityType()) &&
|
||||||
@ -210,6 +212,11 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
|
|||||||
case SKELETON_HORSE:
|
case SKELETON_HORSE:
|
||||||
case TRADER_LLAMA:
|
case TRADER_LLAMA:
|
||||||
case ZOMBIE_HORSE:
|
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
|
// have another switch statement as, while these share mount attributes they don't share food
|
||||||
switch (interactEntity.getEntityType()) {
|
switch (interactEntity.getEntityType()) {
|
||||||
case LLAMA:
|
case LLAMA:
|
||||||
@ -228,9 +235,9 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
|
|||||||
}
|
}
|
||||||
if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY)) {
|
if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY)) {
|
||||||
// Can't ride a baby
|
// Can't ride a baby
|
||||||
if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) {
|
if (tamed) {
|
||||||
interactiveTag = InteractiveTag.RIDE_HORSE;
|
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
|
// Can't hide an untamed entity without having your hand empty
|
||||||
interactiveTag = InteractiveTag.MOUNT;
|
interactiveTag = InteractiveTag.MOUNT;
|
||||||
}
|
}
|
||||||
@ -349,20 +356,30 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
|
|||||||
} else {
|
} else {
|
||||||
if (!session.getPlayerEntity().getMetadata().getString(EntityData.INTERACTIVE_TAG).isEmpty()) {
|
if (!session.getPlayerEntity().getMetadata().getString(EntityData.INTERACTIVE_TAG).isEmpty()) {
|
||||||
// No interactive tag should be sent
|
// No interactive tag should be sent
|
||||||
session.getPlayerEntity().getMetadata().remove(EntityData.INTERACTIVE_TAG);
|
session.getPlayerEntity().getMetadata().put(EntityData.INTERACTIVE_TAG, "");
|
||||||
session.getPlayerEntity().updateBedrockMetadata(session);
|
session.getPlayerEntity().updateBedrockMetadata(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OPEN_INVENTORY:
|
case OPEN_INVENTORY:
|
||||||
if (!session.getInventory().isOpen()) {
|
if (session.getOpenInventory() == null) {
|
||||||
ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
|
Entity ridingEntity = session.getRidingVehicleEntity();
|
||||||
containerOpenPacket.setId((byte) 0);
|
if (ridingEntity instanceof AbstractHorseEntity) {
|
||||||
containerOpenPacket.setType(ContainerType.INVENTORY);
|
if (ridingEntity.getMetadata().getFlags().getFlag(EntityFlag.TAMED)) {
|
||||||
containerOpenPacket.setUniqueEntityId(-1);
|
// We should request to open the horse inventory instead
|
||||||
containerOpenPacket.setBlockPosition(entity.getPosition().toInt());
|
ClientPlayerStatePacket openHorseWindowPacket = new ClientPlayerStatePacket((int) session.getPlayerEntity().getEntityId(), PlayerState.OPEN_HORSE_INVENTORY);
|
||||||
session.sendUpstreamPacket(containerOpenPacket);
|
session.sendDownstreamPacket(openHorseWindowPacket);
|
||||||
session.getInventory().setOpen(true);
|
}
|
||||||
|
} 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;
|
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,81 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.network.translators.inventory;
|
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.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.window.WindowType;
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
|
import com.nukkitx.protocol.bedrock.data.inventory.ItemStackRequest;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.*;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket;
|
||||||
|
import it.unimi.dsi.fastutil.ints.*;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
import org.geysermc.connector.inventory.*;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
|
import org.geysermc.connector.network.translators.inventory.click.Click;
|
||||||
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
|
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.utils.InventoryUtils;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public abstract class InventoryTranslator {
|
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>() {
|
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_9X1, new SingleChestInventoryTranslator(9));
|
||||||
put(WindowType.GENERIC_9X2, new SingleChestInventoryTranslator(18));
|
put(WindowType.GENERIC_9X2, new SingleChestInventoryTranslator(18));
|
||||||
put(WindowType.GENERIC_9X3, new SingleChestInventoryTranslator(27));
|
put(WindowType.GENERIC_9X3, new SingleChestInventoryTranslator(27));
|
||||||
put(WindowType.GENERIC_9X4, new DoubleChestInventoryTranslator(36));
|
put(WindowType.GENERIC_9X4, new DoubleChestInventoryTranslator(36));
|
||||||
put(WindowType.GENERIC_9X5, new DoubleChestInventoryTranslator(45));
|
put(WindowType.GENERIC_9X5, new DoubleChestInventoryTranslator(45));
|
||||||
put(WindowType.GENERIC_9X6, new DoubleChestInventoryTranslator(54));
|
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.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.CRAFTING, new CraftingInventoryTranslator());
|
||||||
//put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); //FIXME
|
put(WindowType.ENCHANTMENT, new EnchantingInventoryTranslator());
|
||||||
|
put(WindowType.HOPPER, new HopperInventoryTranslator());
|
||||||
|
put(WindowType.GENERIC_3X3, new Generic3X3InventoryTranslator());
|
||||||
|
put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator());
|
||||||
|
put(WindowType.LOOM, new LoomInventoryTranslator());
|
||||||
put(WindowType.MERCHANT, new MerchantInventoryTranslator());
|
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();
|
/* Lectern */
|
||||||
put(WindowType.FURNACE, furnace);
|
put(WindowType.LECTERN, new LecternInventoryTranslator());
|
||||||
put(WindowType.BLAST_FURNACE, furnace);
|
|
||||||
put(WindowType.SMOKER, furnace);
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static final int PLAYER_INVENTORY_SIZE = 36;
|
||||||
|
public static final int PLAYER_INVENTORY_OFFSET = 9;
|
||||||
|
|
||||||
public final int size;
|
public final int size;
|
||||||
|
|
||||||
public abstract void prepareInventory(GeyserSession session, Inventory inventory);
|
public abstract void prepareInventory(GeyserSession session, Inventory inventory);
|
||||||
@ -79,8 +108,769 @@ public abstract class InventoryTranslator {
|
|||||||
public abstract void updateProperty(GeyserSession session, Inventory inventory, int key, int value);
|
public abstract void updateProperty(GeyserSession session, Inventory inventory, int key, int value);
|
||||||
public abstract void updateInventory(GeyserSession session, Inventory inventory);
|
public abstract void updateInventory(GeyserSession session, Inventory inventory);
|
||||||
public abstract void updateSlot(GeyserSession session, Inventory inventory, int slot);
|
public abstract void updateSlot(GeyserSession session, Inventory inventory, int slot);
|
||||||
public abstract int bedrockSlotToJava(InventoryActionData action);
|
public abstract int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData);
|
||||||
public abstract int javaSlotToBedrock(int slot);
|
public abstract int javaSlotToBedrock(int javaSlot);
|
||||||
|
public abstract BedrockContainerSlot javaSlotToBedrockContainer(int javaSlot);
|
||||||
public abstract SlotType getSlotType(int javaSlot);
|
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, ContainerSlotType bedrockSourceContainer,
|
||||||
|
int javaSourceSlot, ContainerSlotType bedrockDestinationContainer, 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, ItemStackRequest request) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void translateRequests(GeyserSession session, Inventory inventory, List<ItemStackRequest> requests) {
|
||||||
|
boolean refresh = false;
|
||||||
|
ItemStackResponsePacket responsePacket = new ItemStackResponsePacket();
|
||||||
|
for (ItemStackRequest request : requests) {
|
||||||
|
ItemStackResponsePacket.Response response;
|
||||||
|
if (request.getActions().length > 0) {
|
||||||
|
StackRequestActionData firstAction = request.getActions()[0];
|
||||||
|
if (shouldHandleRequestFirst(firstAction, inventory)) {
|
||||||
|
// Some special request that shouldn't be processed normally
|
||||||
|
response = translateSpecialRequest(session, inventory, request);
|
||||||
|
} else {
|
||||||
|
switch (firstAction.getType()) {
|
||||||
|
case CRAFT_RECIPE:
|
||||||
|
response = translateCraftingRequest(session, inventory, request);
|
||||||
|
break;
|
||||||
|
case CRAFT_RECIPE_AUTO:
|
||||||
|
response = translateAutoCraftingRequest(session, inventory, request);
|
||||||
|
break;
|
||||||
|
case CRAFT_CREATIVE:
|
||||||
|
// This is also used for pulling items out of creative
|
||||||
|
response = translateCreativeRequest(session, inventory, request);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
response = translateRequest(session, inventory, request);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
response = rejectRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.getResult() != ItemStackResponsePacket.ResponseStatus.OK) {
|
||||||
|
// Sync our copy of the inventory with Bedrock's to prevent desyncs
|
||||||
|
refresh = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
responsePacket.getEntries().add(response);
|
||||||
|
}
|
||||||
|
session.sendUpstreamPacket(responsePacket);
|
||||||
|
|
||||||
|
if (refresh) {
|
||||||
|
InventoryUtils.updateCursor(session);
|
||||||
|
updateInventory(session, inventory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStackResponsePacket.Response translateRequest(GeyserSession session, Inventory inventory, ItemStackRequest 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);
|
||||||
|
}
|
||||||
|
if (session.getConnector().getConfig().isDebugMode()) {
|
||||||
|
session.getConnector().getLogger().error("DEBUG: About to reject TAKE/PLACE request made by " + session.getName());
|
||||||
|
dumpStackRequestDetails(session, inventory, transferAction.getSource(), transferAction.getDestination());
|
||||||
|
}
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sourceSlot = bedrockSlotToJava(transferAction.getSource());
|
||||||
|
int destSlot = bedrockSlotToJava(transferAction.getDestination());
|
||||||
|
|
||||||
|
if (shouldRejectItemPlace(session, inventory, transferAction.getSource().getContainer(),
|
||||||
|
isCursor(transferAction.getSource()) ? -1 : sourceSlot,
|
||||||
|
transferAction.getDestination().getContainer(), 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 (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 (!plan.getCursor().isEmpty()) {
|
||||||
|
tempSlot = findTempSlot(inventory, cursor, false, sourceSlot, destSlot);
|
||||||
|
if (tempSlot == -1) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
plan.add(Click.LEFT, tempSlot); //place cursor into temp slot
|
||||||
|
}
|
||||||
|
|
||||||
|
transferSlot(plan, sourceSlot, destSlot, transferAction.getCount());
|
||||||
|
|
||||||
|
if (tempSlot != -1) {
|
||||||
|
plan.add(Click.LEFT, tempSlot); //retrieve original cursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SWAP: {
|
||||||
|
SwapStackRequestActionData swapAction = (SwapStackRequestActionData) action;
|
||||||
|
if (!(checkNetId(session, inventory, swapAction.getSource()) && checkNetId(session, inventory, swapAction.getDestination()))) {
|
||||||
|
if (session.getConnector().getConfig().isDebugMode()) {
|
||||||
|
session.getConnector().getLogger().error("DEBUG: About to reject SWAP request made by " + session.getName());
|
||||||
|
dumpStackRequestDetails(session, inventory, swapAction.getSource(), swapAction.getDestination());
|
||||||
|
}
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sourceSlot = bedrockSlotToJava(swapAction.getSource());
|
||||||
|
int destSlot = bedrockSlotToJava(swapAction.getDestination());
|
||||||
|
boolean isSourceCursor = isCursor(swapAction.getSource());
|
||||||
|
boolean isDestCursor = isCursor(swapAction.getDestination());
|
||||||
|
|
||||||
|
if (shouldRejectItemPlace(session, inventory, swapAction.getSource().getContainer(),
|
||||||
|
isSourceCursor ? -1 : sourceSlot,
|
||||||
|
swapAction.getDestination().getContainer(), isDestCursor ? -1 : destSlot)) {
|
||||||
|
// This item would not be here in Java
|
||||||
|
return rejectRequest(request, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSourceCursor && isDestCursor) { //???
|
||||||
|
return rejectRequest(request);
|
||||||
|
} else if (isSourceCursor) { //swap cursor
|
||||||
|
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 (isDestCursor) { //swap cursor
|
||||||
|
if (InventoryUtils.canStack(cursor, plan.getItem(sourceSlot))) { //TODO
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
plan.add(Click.LEFT, sourceSlot);
|
||||||
|
} else {
|
||||||
|
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
|
||||||
|
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 CONSUME: { // Tends to be called for UI inventories
|
||||||
|
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()) || (sourceSlot == 1 && inventory.getItem(0).isEmpty())) {
|
||||||
|
// Java doesn't allow an item to be renamed; this is why one of the slots could remain empty for Bedrock
|
||||||
|
// We check this now since setting the inventory slots here messes up shouldRejectItemPlace
|
||||||
|
return rejectRequest(request, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sourceSlot == 1) {
|
||||||
|
// Decrease the item count, but only after both slots are checked.
|
||||||
|
// Otherwise, the slot 1 check will fail
|
||||||
|
GeyserItemStack item = inventory.getItem(sourceSlot);
|
||||||
|
item.setAmount(item.getAmount() - consumeData.getCount());
|
||||||
|
if (item.isEmpty()) {
|
||||||
|
inventory.setItem(sourceSlot, GeyserItemStack.EMPTY, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
GeyserItemStack itemZero = inventory.getItem(0);
|
||||||
|
itemZero.setAmount(itemZero.getAmount() - consumeData.getCount());
|
||||||
|
if (itemZero.isEmpty()) {
|
||||||
|
inventory.setItem(0, GeyserItemStack.EMPTY, session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
affectedSlots.add(sourceSlot);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CRAFT_RECIPE_AUTO: // Called by villagers
|
||||||
|
case CRAFT_NON_IMPLEMENTED_DEPRECATED: // Tends to be called for UI inventories
|
||||||
|
case CRAFT_RESULTS_DEPRECATED: // Tends to be called for UI inventories
|
||||||
|
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, ItemStackRequest request) {
|
||||||
|
int resultSize = 0;
|
||||||
|
int timesCrafted;
|
||||||
|
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: {
|
||||||
|
if (craftState != CraftState.START) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
craftState = CraftState.RECIPE_ID;
|
||||||
|
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: {
|
||||||
|
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 (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);
|
||||||
|
return acceptRequest(request, makeContainerEntries(session, inventory, plan.getAffectedSlots()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStackResponsePacket.Response translateAutoCraftingRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) {
|
||||||
|
int gridSize;
|
||||||
|
int gridDimensions;
|
||||||
|
if (this instanceof PlayerInventoryTranslator) {
|
||||||
|
gridSize = 4;
|
||||||
|
gridDimensions = 2;
|
||||||
|
} else if (this instanceof CraftingInventoryTranslator) {
|
||||||
|
gridSize = 9;
|
||||||
|
gridDimensions = 3;
|
||||||
|
} else {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
Recipe recipe;
|
||||||
|
Ingredient[] ingredients = new Ingredient[0];
|
||||||
|
ItemStack output = null;
|
||||||
|
int recipeWidth = 0;
|
||||||
|
int ingRemaining = 0;
|
||||||
|
int ingredientIndex = -1;
|
||||||
|
|
||||||
|
Int2IntMap consumedSlots = new Int2IntOpenHashMap();
|
||||||
|
int prioritySlot = -1;
|
||||||
|
int tempSlot;
|
||||||
|
|
||||||
|
int resultSize;
|
||||||
|
int timesCrafted = 0;
|
||||||
|
Int2ObjectMap<Int2IntMap> ingredientMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
CraftState craftState = CraftState.START;
|
||||||
|
|
||||||
|
ClickPlan plan = new ClickPlan(session, this, inventory);
|
||||||
|
requestLoop:
|
||||||
|
for (StackRequestActionData action : request.getActions()) {
|
||||||
|
switch (action.getType()) {
|
||||||
|
case CRAFT_RECIPE_AUTO: {
|
||||||
|
AutoCraftRecipeStackRequestActionData autoCraftAction = (AutoCraftRecipeStackRequestActionData) action;
|
||||||
|
if (craftState != CraftState.START) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
craftState = CraftState.RECIPE_ID;
|
||||||
|
|
||||||
|
int recipeId = autoCraftAction.getRecipeNetworkId();
|
||||||
|
recipe = session.getCraftingRecipes().get(recipeId);
|
||||||
|
if (recipe == null) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
if (!plan.getCursor().isEmpty()) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
//reject if crafting grid is not clear
|
||||||
|
for (int i = 1; i <= gridSize; i++) {
|
||||||
|
if (!inventory.getItem(i).isEmpty()) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (recipe.getType()) {
|
||||||
|
case CRAFTING_SHAPED:
|
||||||
|
ShapedRecipeData shapedData = (ShapedRecipeData) recipe.getData();
|
||||||
|
ingredients = shapedData.getIngredients();
|
||||||
|
recipeWidth = shapedData.getWidth();
|
||||||
|
output = shapedData.getResult();
|
||||||
|
if (shapedData.getWidth() > gridDimensions || shapedData.getHeight() > gridDimensions) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CRAFTING_SHAPELESS:
|
||||||
|
ShapelessRecipeData shapelessData = (ShapelessRecipeData) recipe.getData();
|
||||||
|
ingredients = shapelessData.getIngredients();
|
||||||
|
recipeWidth = gridDimensions;
|
||||||
|
output = shapelessData.getResult();
|
||||||
|
if (ingredients.length > gridSize) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (ingRemaining == 0) {
|
||||||
|
while (++ingredientIndex < ingredients.length) {
|
||||||
|
if (ingredients[ingredientIndex].getOptions().length != 0) {
|
||||||
|
ingRemaining = timesCrafted;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ingRemaining -= consumeAction.getCount();
|
||||||
|
if (ingRemaining < 0)
|
||||||
|
return rejectRequest(request);
|
||||||
|
|
||||||
|
int javaSlot = bedrockSlotToJava(consumeAction.getSource());
|
||||||
|
consumedSlots.merge(javaSlot, consumeAction.getCount(), Integer::sum);
|
||||||
|
|
||||||
|
int gridSlot = 1 + ingredientIndex + ((ingredientIndex / recipeWidth) * (gridDimensions - recipeWidth));
|
||||||
|
Int2IntMap sources = ingredientMap.computeIfAbsent(gridSlot, k -> new Int2IntOpenHashMap());
|
||||||
|
sources.put(javaSlot, consumeAction.getCount());
|
||||||
|
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 javaSlot = bedrockSlotToJava(transferAction.getDestination());
|
||||||
|
if (isCursor(transferAction.getDestination())) { //TODO
|
||||||
|
if (timesCrafted > 1) {
|
||||||
|
tempSlot = findTempSlot(inventory, GeyserItemStack.from(output), true);
|
||||||
|
if (tempSlot == -1) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break requestLoop;
|
||||||
|
} else if (inventory.getItem(javaSlot).getAmount() == consumedSlots.get(javaSlot)) {
|
||||||
|
prioritySlot = bedrockSlotToJava(transferAction.getDestination());
|
||||||
|
break requestLoop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final int maxLoops = Math.min(64, timesCrafted);
|
||||||
|
for (int loops = 0; loops < maxLoops; loops++) {
|
||||||
|
boolean done = true;
|
||||||
|
for (Int2ObjectMap.Entry<Int2IntMap> entry : ingredientMap.int2ObjectEntrySet()) {
|
||||||
|
Int2IntMap sources = entry.getValue();
|
||||||
|
if (sources.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
done = false;
|
||||||
|
int gridSlot = entry.getIntKey();
|
||||||
|
if (!plan.getItem(gridSlot).isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int sourceSlot;
|
||||||
|
if (loops == 0 && sources.containsKey(prioritySlot)) {
|
||||||
|
sourceSlot = prioritySlot;
|
||||||
|
} else {
|
||||||
|
sourceSlot = sources.keySet().iterator().nextInt();
|
||||||
|
}
|
||||||
|
int transferAmount = sources.remove(sourceSlot);
|
||||||
|
transferSlot(plan, sourceSlot, gridSlot, transferAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!done) {
|
||||||
|
//TODO: sometimes the server does not agree on this slot?
|
||||||
|
plan.add(Click.LEFT_SHIFT, 0, true);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inventory.setItem(0, GeyserItemStack.from(output), session);
|
||||||
|
plan.execute(true);
|
||||||
|
return acceptRequest(request, makeContainerEntries(session, inventory, plan.getAffectedSlots()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handled in {@link PlayerInventoryTranslator}
|
||||||
|
*/
|
||||||
|
public ItemStackResponsePacket.Response translateCreativeRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void transferSlot(ClickPlan plan, int sourceSlot, int destSlot, int transferAmount) {
|
||||||
|
boolean tempSwap = !plan.getCursor().isEmpty();
|
||||||
|
int sourceAmount = plan.getItem(sourceSlot).getAmount();
|
||||||
|
if (transferAmount == 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 (!tempSwap && transferAmount <= 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 (!tempSwap && transferAmount > holding / 2) { //faster to release extra items onto source or dest slot?
|
||||||
|
for (int i = 0; i < holding - transferAmount; i++) {
|
||||||
|
plan.add(Click.RIGHT, sourceSlot); //prepare cursor
|
||||||
|
}
|
||||||
|
plan.add(Click.LEFT, destSlot); //release cursor onto dest slot
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < transferAmount; i++) {
|
||||||
|
plan.add(Click.RIGHT, destSlot); //right click until transfer goal is met
|
||||||
|
}
|
||||||
|
plan.add(Click.LEFT, sourceSlot); //return extra items to source slot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStackResponsePacket.Response acceptRequest(ItemStackRequest request, List<ItemStackResponsePacket.ContainerEntry> containerEntries) {
|
||||||
|
return new ItemStackResponsePacket.Response(ItemStackResponsePacket.ResponseStatus.OK, request.getRequestId(), containerEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reject an incorrect ItemStackRequest.
|
||||||
|
*/
|
||||||
|
public static ItemStackResponsePacket.Response rejectRequest(ItemStackRequest request) {
|
||||||
|
return rejectRequest(request, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reject an incorrect ItemStackRequest.
|
||||||
|
*
|
||||||
|
* @param throwError whether this request was truly erroneous (true), or known as an outcome and should not be treated
|
||||||
|
* as bad (false).
|
||||||
|
*/
|
||||||
|
public static ItemStackResponsePacket.Response rejectRequest(ItemStackRequest request, boolean throwError) {
|
||||||
|
if (throwError && GeyserConnector.getInstance().getConfig().isDebugMode()) {
|
||||||
|
new Throwable("DEBUGGING: ItemStackRequest rejected " + request.toString()).printStackTrace();
|
||||||
|
}
|
||||||
|
return new ItemStackResponsePacket.Response(ItemStackResponsePacket.ResponseStatus.ERROR, request.getRequestId(), Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print out the contents of an ItemStackRequest, should the net ID check fail.
|
||||||
|
*/
|
||||||
|
protected void dumpStackRequestDetails(GeyserSession session, Inventory inventory, StackRequestSlotInfoData source, StackRequestSlotInfoData destination) {
|
||||||
|
session.getConnector().getLogger().error("Source: " + source.toString() + " Result: " + checkNetId(session, inventory, source));
|
||||||
|
session.getConnector().getLogger().error("Destination: " + destination.toString() + " Result: " + checkNetId(session, inventory, destination));
|
||||||
|
session.getConnector().getLogger().error("Geyser's record of source slot: " + inventory.getItem(bedrockSlotToJava(source)));
|
||||||
|
session.getConnector().getLogger().error("Geyser's record of destination slot: " + inventory.getItem(bedrockSlotToJava(destination)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkNetId(GeyserSession session, Inventory inventory, StackRequestSlotInfoData slotInfoData) {
|
||||||
|
int netId = slotInfoData.getStackNetworkId();
|
||||||
|
// "In my testing, sometimes the client thinks the netId of an item in the crafting grid is 1, even though we never said it was.
|
||||||
|
// I think it only happens when we manually set the grid but that was my quick fix"
|
||||||
|
if (netId < 0 || netId == 1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
GeyserItemStack currentItem = isCursor(slotInfoData) ? session.getPlayerInventory().getCursor() : inventory.getItem(bedrockSlotToJava(slotInfoData));
|
||||||
|
return currentItem.getNetId() == netId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
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(), "", 0);
|
||||||
|
} else {
|
||||||
|
itemEntry = new ItemStackResponsePacket.ItemEntry((byte) bedrockSlot, (byte) bedrockSlot, (byte) 0, 0, "", 0);
|
||||||
|
}
|
||||||
|
return itemEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean isCursor(StackRequestSlotInfoData slotInfoData) {
|
||||||
|
return slotInfoData.getContainer() == ContainerSlotType.CURSOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected enum CraftState {
|
||||||
|
START,
|
||||||
|
RECIPE_ID,
|
||||||
|
DEPRECATED,
|
||||||
|
INGREDIENTS,
|
||||||
|
TRANSFER,
|
||||||
|
DONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,248 +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.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.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.action.InventoryActionDataTranslator;
|
|
||||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
|
||||||
import org.geysermc.connector.utils.InventoryUtils;
|
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class PlayerInventoryTranslator extends InventoryTranslator {
|
|
||||||
private static final ItemData UNUSUABLE_CRAFTING_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock(LanguageUtils.getLocaleStringLog("geyser.inventory.unusable_item.creative"));
|
|
||||||
|
|
||||||
public PlayerInventoryTranslator() {
|
|
||||||
super(46);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateInventory(GeyserSession session, Inventory inventory) {
|
|
||||||
updateCraftingGrid(session, inventory);
|
|
||||||
|
|
||||||
InventoryContentPacket inventoryContentPacket = new InventoryContentPacket();
|
|
||||||
inventoryContentPacket.setContainerId(ContainerId.INVENTORY);
|
|
||||||
ItemData[] contents = new ItemData[36];
|
|
||||||
// Inventory
|
|
||||||
for (int i = 9; i < 36; i++) {
|
|
||||||
contents[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
|
|
||||||
}
|
|
||||||
// Hotbar
|
|
||||||
for (int i = 36; i < 45; i++) {
|
|
||||||
contents[i - 36] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
|
|
||||||
}
|
|
||||||
inventoryContentPacket.setContents(Arrays.asList(contents));
|
|
||||||
session.sendUpstreamPacket(inventoryContentPacket);
|
|
||||||
|
|
||||||
// Armor
|
|
||||||
InventoryContentPacket armorContentPacket = new InventoryContentPacket();
|
|
||||||
armorContentPacket.setContainerId(ContainerId.ARMOR);
|
|
||||||
contents = new ItemData[4];
|
|
||||||
for (int i = 5; i < 9; i++) {
|
|
||||||
contents[i - 5] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
|
|
||||||
}
|
|
||||||
armorContentPacket.setContents(Arrays.asList(contents));
|
|
||||||
session.sendUpstreamPacket(armorContentPacket);
|
|
||||||
|
|
||||||
// Offhand
|
|
||||||
InventoryContentPacket offhandPacket = new InventoryContentPacket();
|
|
||||||
offhandPacket.setContainerId(ContainerId.OFFHAND);
|
|
||||||
offhandPacket.setContents(Collections.singletonList(ItemTranslator.translateToBedrock(session, inventory.getItem(45))));
|
|
||||||
session.sendUpstreamPacket(offhandPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the crafting grid for the player to hide/show the barriers in the creative inventory
|
|
||||||
* @param session Session of the player
|
|
||||||
* @param inventory Inventory of the player
|
|
||||||
*/
|
|
||||||
public static void updateCraftingGrid(GeyserSession session, Inventory inventory) {
|
|
||||||
// Crafting grid
|
|
||||||
for (int i = 1; i < 5; i++) {
|
|
||||||
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
|
||||||
slotPacket.setContainerId(ContainerId.UI);
|
|
||||||
slotPacket.setSlot(i + 27);
|
|
||||||
|
|
||||||
if (session.getGameMode() == GameMode.CREATIVE) {
|
|
||||||
slotPacket.setItem(UNUSUABLE_CRAFTING_SPACE_BLOCK);
|
|
||||||
}else{
|
|
||||||
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i)));
|
|
||||||
}
|
|
||||||
|
|
||||||
session.sendUpstreamPacket(slotPacket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
|
|
||||||
if (slot >= 1 && slot <= 44) {
|
|
||||||
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
|
||||||
if (slot >= 9) {
|
|
||||||
slotPacket.setContainerId(ContainerId.INVENTORY);
|
|
||||||
if (slot >= 36) {
|
|
||||||
slotPacket.setSlot(slot - 36);
|
|
||||||
} else {
|
|
||||||
slotPacket.setSlot(slot);
|
|
||||||
}
|
|
||||||
} else if (slot >= 5) {
|
|
||||||
slotPacket.setContainerId(ContainerId.ARMOR);
|
|
||||||
slotPacket.setSlot(slot - 5);
|
|
||||||
} else {
|
|
||||||
slotPacket.setContainerId(ContainerId.UI);
|
|
||||||
slotPacket.setSlot(slot + 27);
|
|
||||||
}
|
|
||||||
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(slot)));
|
|
||||||
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))));
|
|
||||||
session.sendUpstreamPacket(offhandPacket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int bedrockSlotToJava(InventoryActionData action) {
|
|
||||||
int slotnum = action.getSlot();
|
|
||||||
switch (action.getSource().getContainerId()) {
|
|
||||||
case ContainerId.INVENTORY:
|
|
||||||
// Inventory
|
|
||||||
if (slotnum >= 9 && slotnum <= 35) {
|
|
||||||
return slotnum;
|
|
||||||
}
|
|
||||||
// Hotbar
|
|
||||||
if (slotnum >= 0 && slotnum <= 8) {
|
|
||||||
return slotnum + 36;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ContainerId.ARMOR:
|
|
||||||
if (slotnum >= 0 && slotnum <= 3) {
|
|
||||||
return slotnum + 5;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ContainerId.OFFHAND:
|
|
||||||
return 45;
|
|
||||||
case ContainerId.UI:
|
|
||||||
if (slotnum >= 28 && 31 >= slotnum) {
|
|
||||||
return slotnum - 27;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ContainerId.CRAFTING_RESULT:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return slotnum;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int javaSlotToBedrock(int slot) {
|
|
||||||
return slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SlotType getSlotType(int javaSlot) {
|
|
||||||
if (javaSlot == 0)
|
|
||||||
return SlotType.OUTPUT;
|
|
||||||
return SlotType.NORMAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
|
|
||||||
if (session.getGameMode() == GameMode.CREATIVE) {
|
|
||||||
//crafting grid is not visible in creative mode in java edition
|
|
||||||
for (InventoryActionData action : actions) {
|
|
||||||
if (action.getSource().getContainerId() == ContainerId.UI && (action.getSlot() >= 28 && 31 >= action.getSlot())) {
|
|
||||||
updateInventory(session, inventory);
|
|
||||||
InventoryUtils.updateCursor(session);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemStack javaItem;
|
|
||||||
for (InventoryActionData action : actions) {
|
|
||||||
switch (action.getSource().getContainerId()) {
|
|
||||||
case ContainerId.INVENTORY:
|
|
||||||
case ContainerId.ARMOR:
|
|
||||||
case ContainerId.OFFHAND:
|
|
||||||
int javaSlot = bedrockSlotToJava(action);
|
|
||||||
if (action.getToItem().getId() == 0) {
|
|
||||||
javaItem = new ItemStack(-1, 0, null);
|
|
||||||
} else {
|
|
||||||
javaItem = 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 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) {
|
|
||||||
}
|
|
||||||
}
|
|
@ -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,294 @@
|
|||||||
|
/*
|
||||||
|
* 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.network.translators.inventory.translators.CraftingInventoryTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.translators.PlayerInventoryTranslator;
|
||||||
|
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;
|
||||||
|
private final int gridSize;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (translator instanceof PlayerInventoryTranslator) {
|
||||||
|
gridSize = 4;
|
||||||
|
} else if (translator instanceof CraftingInventoryTranslator) {
|
||||||
|
gridSize = 9;
|
||||||
|
} else {
|
||||||
|
gridSize = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetSimulation() {
|
||||||
|
this.simulatedItems.clear();
|
||||||
|
this.simulatedCursor = session.getPlayerInventory().getCursor().copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Click click, int slot) {
|
||||||
|
add(click, slot, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Click click, int slot, boolean force) {
|
||||||
|
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, force);
|
||||||
|
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 || action.force) {
|
||||||
|
session.sendDownstreamPacket(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 getItem(slot, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeyserItemStack getItem(int slot, boolean generate) {
|
||||||
|
if (generate) {
|
||||||
|
return simulatedItems.computeIfAbsent(slot, k -> inventory.getItem(slot).copy());
|
||||||
|
} else {
|
||||||
|
return simulatedItems.getOrDefault(slot, inventory.getItem(slot));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
reduceCraftingGrid(false);
|
||||||
|
break;
|
||||||
|
case LEFT_SHIFT:
|
||||||
|
reduceCraftingGrid(true);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
private void reduceCraftingGrid(boolean makeAll) {
|
||||||
|
if (gridSize == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int crafted;
|
||||||
|
if (!makeAll) {
|
||||||
|
crafted = 1;
|
||||||
|
} else {
|
||||||
|
crafted = 0;
|
||||||
|
for (int i = 0; i < gridSize; i++) {
|
||||||
|
GeyserItemStack item = getItem(i + 1);
|
||||||
|
if (!item.isEmpty()) {
|
||||||
|
if (crafted == 0) {
|
||||||
|
crafted = item.getAmount();
|
||||||
|
}
|
||||||
|
crafted = Math.min(crafted, item.getAmount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < gridSize; i++) {
|
||||||
|
GeyserItemStack item = getItem(i + 1);
|
||||||
|
if (!item.isEmpty())
|
||||||
|
item.sub(crafted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
boolean force;
|
||||||
|
}
|
||||||
|
}
|
@ -26,34 +26,99 @@
|
|||||||
package org.geysermc.connector.network.translators.inventory.holder;
|
package org.geysermc.connector.network.translators.inventory.holder;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import com.nukkitx.nbt.NbtMap;
|
import com.nukkitx.nbt.NbtMap;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
||||||
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
|
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
|
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
||||||
import lombok.AllArgsConstructor;
|
import org.geysermc.connector.inventory.Container;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
|
|
||||||
@AllArgsConstructor
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the fake block we implement for each inventory, should we need to.
|
||||||
|
* This class will attempt to use a real block first, if possible.
|
||||||
|
*/
|
||||||
public class BlockInventoryHolder extends InventoryHolder {
|
public class BlockInventoryHolder extends InventoryHolder {
|
||||||
private final int javaBlockState;
|
/**
|
||||||
|
* The default Java block ID to translate as a fake block
|
||||||
|
*/
|
||||||
|
private final int defaultJavaBlockState;
|
||||||
private final ContainerType containerType;
|
private final ContainerType containerType;
|
||||||
|
private final Set<String> validBlocks;
|
||||||
|
|
||||||
|
public BlockInventoryHolder(String javaBlockIdentifier, ContainerType containerType, String... validBlocks) {
|
||||||
|
this.defaultJavaBlockState = BlockTranslator.getJavaBlockState(javaBlockIdentifier);
|
||||||
|
this.containerType = containerType;
|
||||||
|
if (validBlocks != null) {
|
||||||
|
Set<String> validBlocksTemp = new HashSet<>(validBlocks.length + 1);
|
||||||
|
Collections.addAll(validBlocksTemp, validBlocks);
|
||||||
|
validBlocksTemp.add(javaBlockIdentifier.split("\\[")[0]);
|
||||||
|
this.validBlocks = ImmutableSet.copyOf(validBlocksTemp);
|
||||||
|
} else {
|
||||||
|
this.validBlocks = Collections.singleton(javaBlockIdentifier.split("\\[")[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
public void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
||||||
|
// Check to see if there is an existing block we can use that the player just selected.
|
||||||
|
// First, verify that the player's position has not changed, so we don't try to select a block wildly out of range.
|
||||||
|
// (This could be a virtual inventory that the player is opening)
|
||||||
|
if (checkInteractionPosition(session)) {
|
||||||
|
// Then, check to see if the interacted block is valid for this inventory by ensuring the block state identifier is valid
|
||||||
|
int javaBlockId = session.getConnector().getWorldManager().getBlockAt(session, session.getLastInteractionBlockPosition());
|
||||||
|
String[] javaBlockString = BlockTranslator.getJavaIdBlockMap().inverse().getOrDefault(javaBlockId, "minecraft:air").split("\\[");
|
||||||
|
if (isValidBlock(javaBlockString)) {
|
||||||
|
// We can safely use this block
|
||||||
|
inventory.setHolderPosition(session.getLastInteractionBlockPosition());
|
||||||
|
((Container) inventory).setUsingRealBlock(true, javaBlockString[0]);
|
||||||
|
setCustomName(session, session.getLastInteractionBlockPosition(), inventory, javaBlockId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, time to conjure up a fake block!
|
||||||
Vector3i position = session.getPlayerEntity().getPosition().toInt();
|
Vector3i position = session.getPlayerEntity().getPosition().toInt();
|
||||||
position = position.add(Vector3i.UP);
|
position = position.add(Vector3i.UP);
|
||||||
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
|
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
|
||||||
blockPacket.setDataLayer(0);
|
blockPacket.setDataLayer(0);
|
||||||
blockPacket.setBlockPosition(position);
|
blockPacket.setBlockPosition(position);
|
||||||
blockPacket.setRuntimeId(session.getBlockTranslator().getBedrockBlockId(javaBlockState));
|
blockPacket.setRuntimeId(session.getBlockTranslator().getBedrockBlockId(defaultJavaBlockState));
|
||||||
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
|
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
|
||||||
session.sendUpstreamPacket(blockPacket);
|
session.sendUpstreamPacket(blockPacket);
|
||||||
inventory.setHolderPosition(position);
|
inventory.setHolderPosition(position);
|
||||||
|
|
||||||
|
setCustomName(session, position, inventory, defaultJavaBlockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will be overwritten in the beacon inventory translator to remove the check, since virtual inventories can't exist.
|
||||||
|
*
|
||||||
|
* @return if the player's last interaction position and current position match. Used to ensure that we don't select
|
||||||
|
* a block to hold the inventory that's wildly out of range.
|
||||||
|
*/
|
||||||
|
protected boolean checkInteractionPosition(GeyserSession session) {
|
||||||
|
return session.getLastInteractionPlayerPosition().equals(session.getPlayerEntity().getPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if this Java block ID can be used for player inventory.
|
||||||
|
*/
|
||||||
|
protected boolean isValidBlock(String[] javaBlockString) {
|
||||||
|
return this.validBlocks.contains(javaBlockString[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setCustomName(GeyserSession session, Vector3i position, Inventory inventory, int javaBlockState) {
|
||||||
NbtMap tag = NbtMap.builder()
|
NbtMap tag = NbtMap.builder()
|
||||||
.putInt("x", position.getX())
|
.putInt("x", position.getX())
|
||||||
.putInt("y", position.getY())
|
.putInt("y", position.getY())
|
||||||
@ -77,6 +142,16 @@ public class BlockInventoryHolder extends InventoryHolder {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
public void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
||||||
|
if (((Container) inventory).isUsingRealBlock()) {
|
||||||
|
// No need to reset a block since we didn't change any blocks
|
||||||
|
// But send a container close packet because we aren't destroying the original.
|
||||||
|
ContainerClosePacket packet = new ContainerClosePacket();
|
||||||
|
packet.setId((byte) inventory.getId());
|
||||||
|
packet.setUnknownBool0(true); //TODO needs to be changed in Protocol to "server-side" or something
|
||||||
|
session.sendUpstreamPacket(packet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Vector3i holderPos = inventory.getHolderPosition();
|
Vector3i holderPos = inventory.getHolderPosition();
|
||||||
Position pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
|
Position pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
|
||||||
int realBlock = session.getConnector().getWorldManager().getBlockAt(session, pos.getX(), pos.getY(), pos.getZ());
|
int realBlock = session.getConnector().getWorldManager().getBlockAt(session, pos.getX(), pos.getY(), pos.getZ());
|
||||||
@ -84,6 +159,7 @@ public class BlockInventoryHolder extends InventoryHolder {
|
|||||||
blockPacket.setDataLayer(0);
|
blockPacket.setDataLayer(0);
|
||||||
blockPacket.setBlockPosition(holderPos);
|
blockPacket.setBlockPosition(holderPos);
|
||||||
blockPacket.setRuntimeId(session.getBlockTranslator().getBedrockBlockId(realBlock));
|
blockPacket.setRuntimeId(session.getBlockTranslator().getBedrockBlockId(realBlock));
|
||||||
|
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
|
||||||
session.sendUpstreamPacket(blockPacket);
|
session.sendUpstreamPacket(blockPacket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,84 +23,60 @@
|
|||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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.ContainerType;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
|
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
|
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.inventory.updater.InventoryUpdater;
|
||||||
|
|
||||||
import java.util.List;
|
/**
|
||||||
|
* Provided as a base for any inventory that requires a block for opening it
|
||||||
public class MerchantInventoryTranslator extends BaseInventoryTranslator {
|
*/
|
||||||
|
public abstract class AbstractBlockInventoryTranslator extends BaseInventoryTranslator {
|
||||||
|
private final InventoryHolder holder;
|
||||||
private final InventoryUpdater updater;
|
private final InventoryUpdater updater;
|
||||||
|
|
||||||
public MerchantInventoryTranslator() {
|
/**
|
||||||
super(3);
|
* @param size the amount of slots that the inventory adds alongside the base inventory slots
|
||||||
this.updater = new CursorInventoryUpdater();
|
* @param javaBlockIdentifier a Java block identifier that is used as a temporary block
|
||||||
|
* @param containerType the container type of this inventory
|
||||||
|
* @param updater updater
|
||||||
|
* @param additionalValidBlocks any other block identifiers that can safely use this inventory without a fake block
|
||||||
|
*/
|
||||||
|
public AbstractBlockInventoryTranslator(int size, String javaBlockIdentifier, ContainerType containerType, InventoryUpdater updater,
|
||||||
|
String... additionalValidBlocks) {
|
||||||
|
super(size);
|
||||||
|
this.holder = new BlockInventoryHolder(javaBlockIdentifier, containerType, additionalValidBlocks);
|
||||||
|
this.updater = updater;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public int javaSlotToBedrock(int slot) {
|
* @param size the amount of slots that the inventory adds alongside the base inventory slots
|
||||||
switch (slot) {
|
* @param holder the custom block holder
|
||||||
case 0:
|
* @param updater updater
|
||||||
return 4;
|
*/
|
||||||
case 1:
|
public AbstractBlockInventoryTranslator(int size, InventoryHolder holder, InventoryUpdater updater) {
|
||||||
return 5;
|
super(size);
|
||||||
case 2:
|
this.holder = holder;
|
||||||
return 50;
|
this.updater = updater;
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareInventory(GeyserSession session, Inventory inventory) {
|
public void prepareInventory(GeyserSession session, Inventory inventory) {
|
||||||
|
holder.prepareInventory(this, session, inventory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openInventory(GeyserSession session, Inventory inventory) {
|
public void openInventory(GeyserSession session, Inventory inventory) {
|
||||||
|
holder.openInventory(this, session, inventory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeInventory(GeyserSession session, Inventory inventory) {
|
public void closeInventory(GeyserSession session, Inventory inventory) {
|
||||||
session.setLastInteractedVillagerEid(-1);
|
holder.closeInventory(this, session, inventory);
|
||||||
session.setVillagerTrades(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -112,13 +88,4 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator {
|
|||||||
public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
|
public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
|
||||||
updater.updateSlot(this, session, inventory, 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,144 @@
|
|||||||
|
/*
|
||||||
|
* 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.ClientRenameItemPacket;
|
||||||
|
import com.nukkitx.nbt.NbtMap;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.*;
|
||||||
|
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.ItemStackResponsePacket;
|
||||||
|
import org.geysermc.connector.inventory.AnvilContainer;
|
||||||
|
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;
|
||||||
|
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||||
|
|
||||||
|
public class AnvilInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||||
|
public AnvilInventoryTranslator() {
|
||||||
|
super(3, "minecraft:anvil[facing=north]", ContainerType.ANVIL, UIInventoryUpdater.INSTANCE,
|
||||||
|
"minecraft:chipped_anvil", "minecraft:damaged_anvil");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.16.100 support start */
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) {
|
||||||
|
return action.getType() == StackRequestActionType.CRAFT_NON_IMPLEMENTED_DEPRECATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) {
|
||||||
|
if (!(request.getActions()[1] instanceof CraftResultsDeprecatedStackRequestActionData)) {
|
||||||
|
// Just silently log an error
|
||||||
|
session.getConnector().getLogger().debug("Something isn't quite right with taking an item out of an anvil.");
|
||||||
|
return translateRequest(session, inventory, request);
|
||||||
|
}
|
||||||
|
CraftResultsDeprecatedStackRequestActionData actionData = (CraftResultsDeprecatedStackRequestActionData) request.getActions()[1];
|
||||||
|
ItemData resultItem = actionData.getResultItems()[0];
|
||||||
|
if (resultItem.getTag() != null) {
|
||||||
|
NbtMap displayTag = resultItem.getTag().getCompound("display");
|
||||||
|
if (displayTag != null && displayTag.containsKey("Name")) {
|
||||||
|
ItemData sourceSlot = inventory.getItem(0).getItemData(session);
|
||||||
|
|
||||||
|
if (sourceSlot.getTag() != null) {
|
||||||
|
NbtMap oldDisplayTag = sourceSlot.getTag().getCompound("display");
|
||||||
|
if (oldDisplayTag != null && oldDisplayTag.containsKey("Name")) {
|
||||||
|
if (!displayTag.getString("Name").equals(oldDisplayTag.getString("Name"))) {
|
||||||
|
// Name has changed
|
||||||
|
sendRenamePacket(session, inventory, resultItem, displayTag.getString("Name"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No display tag on the old item
|
||||||
|
sendRenamePacket(session, inventory, resultItem, displayTag.getString("Name"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// New NBT tag
|
||||||
|
sendRenamePacket(session, inventory, resultItem, displayTag.getString("Name"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return translateRequest(session, inventory, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendRenamePacket(GeyserSession session, Inventory inventory, ItemData outputItem, String name) {
|
||||||
|
session.sendDownstreamPacket(new ClientRenameItemPacket(name));
|
||||||
|
inventory.setItem(2, GeyserItemStack.from(ItemTranslator.translateToJava(outputItem)), session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.16.100 support end */
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
|
||||||
|
switch (slotInfoData.getContainer()) {
|
||||||
|
case ANVIL_INPUT:
|
||||||
|
return 0;
|
||||||
|
case ANVIL_MATERIAL:
|
||||||
|
return 1;
|
||||||
|
case ANVIL_RESULT:
|
||||||
|
case 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, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
}
|
@ -23,18 +23,21 @@
|
|||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
|
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.Inventory;
|
||||||
|
import org.geysermc.connector.inventory.PlayerInventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.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 {
|
||||||
|
public BaseInventoryTranslator(int size) {
|
||||||
public abstract class BaseInventoryTranslator extends InventoryTranslator{
|
|
||||||
BaseInventoryTranslator(int size) {
|
|
||||||
super(size);
|
super(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,15 +47,18 @@ public abstract class BaseInventoryTranslator extends InventoryTranslator{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int bedrockSlotToJava(InventoryActionData action) {
|
public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
|
||||||
int slotnum = action.getSlot();
|
int slotnum = slotInfoData.getSlot();
|
||||||
if (action.getSource().getContainerId() == ContainerId.INVENTORY) {
|
switch (slotInfoData.getContainer()) {
|
||||||
//hotbar
|
case HOTBAR_AND_INVENTORY:
|
||||||
if (slotnum >= 9) {
|
case HOTBAR:
|
||||||
return slotnum + this.size - 9;
|
case INVENTORY:
|
||||||
} else {
|
//hotbar
|
||||||
return slotnum + this.size + 27;
|
if (slotnum >= 9) {
|
||||||
}
|
return slotnum + this.size - 9;
|
||||||
|
} else {
|
||||||
|
return slotnum + this.size + 27;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return slotnum;
|
return slotnum;
|
||||||
}
|
}
|
||||||
@ -70,13 +76,26 @@ public abstract class BaseInventoryTranslator extends InventoryTranslator{
|
|||||||
return slot;
|
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
|
@Override
|
||||||
public SlotType getSlotType(int javaSlot) {
|
public SlotType getSlotType(int javaSlot) {
|
||||||
return SlotType.NORMAL;
|
return SlotType.NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
|
public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
InventoryActionDataTranslator.translate(this, session, inventory, actions);
|
return new Container(name, windowId, this.size, windowType, playerInventory);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* 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.ItemStackRequest;
|
||||||
|
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.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.InventoryTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.holder.BlockInventoryHolder;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater;
|
||||||
|
import org.geysermc.connector.utils.InventoryUtils;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||||
|
public BeaconInventoryTranslator() {
|
||||||
|
super(1, new BlockInventoryHolder("minecraft:beacon", ContainerType.BEACON) {
|
||||||
|
@Override
|
||||||
|
public void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
||||||
|
if (!session.getConnector().getConfig().isCacheChunks()) {
|
||||||
|
// Beacons cannot work without knowing their physical location
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.prepareInventory(translator, session, inventory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean checkInteractionPosition(GeyserSession session) {
|
||||||
|
// Since we can't fall back to a virtual inventory, let's make opening one easier
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
||||||
|
if (!session.getConnector().getConfig().isCacheChunks() || !((BeaconContainer) inventory).isUsingRealBlock()) {
|
||||||
|
InventoryUtils.closeInventory(session, inventory.getId(), false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.openInventory(translator, session, inventory);
|
||||||
|
}
|
||||||
|
}, 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());
|
||||||
|
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, ItemStackRequest request) {
|
||||||
|
// Input a beacon payment
|
||||||
|
BeaconPaymentStackRequestActionData beaconPayment = (BeaconPaymentStackRequestActionData) request.getActions()[0];
|
||||||
|
ClientSetBeaconEffectPacket packet = new ClientSetBeaconEffectPacket(beaconPayment.getPrimaryEffect(), beaconPayment.getSecondaryEffect());
|
||||||
|
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, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
}
|
@ -23,18 +23,20 @@
|
|||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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.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 com.nukkitx.protocol.bedrock.packet.ContainerSetDataPacket;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
|
||||||
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
|
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
|
||||||
|
|
||||||
public class BrewingInventoryTranslator extends BlockInventoryTranslator {
|
public class BrewingInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||||
public BrewingInventoryTranslator() {
|
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
|
@Override
|
||||||
@ -66,20 +68,16 @@ public class BrewingInventoryTranslator extends BlockInventoryTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int bedrockSlotToJava(InventoryActionData action) {
|
public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
|
||||||
final int slot = super.bedrockSlotToJava(action);
|
if (slotInfoData.getContainer() == ContainerSlotType.BREWING_INPUT) {
|
||||||
switch (slot) {
|
// Ingredient
|
||||||
case 0:
|
return 3;
|
||||||
return 3;
|
|
||||||
case 1:
|
|
||||||
return 0;
|
|
||||||
case 2:
|
|
||||||
return 1;
|
|
||||||
case 3:
|
|
||||||
return 2;
|
|
||||||
default:
|
|
||||||
return slot;
|
|
||||||
}
|
}
|
||||||
|
if (slotInfoData.getContainer() == ContainerSlotType.BREWING_RESULT) {
|
||||||
|
// Potions
|
||||||
|
return slotInfoData.getSlot() - 1;
|
||||||
|
}
|
||||||
|
return super.bedrockSlotToJava(slotInfoData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -96,4 +94,19 @@ public class BrewingInventoryTranslator extends BlockInventoryTranslator {
|
|||||||
}
|
}
|
||||||
return super.javaSlotToBedrock(slot);
|
return super.javaSlotToBedrock(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
|
||||||
|
switch (slot) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
return new BedrockContainerSlot(ContainerSlotType.BREWING_RESULT, javaSlotToBedrock(slot));
|
||||||
|
case 3:
|
||||||
|
return new BedrockContainerSlot(ContainerSlotType.BREWING_INPUT, 0);
|
||||||
|
case 4:
|
||||||
|
return new BedrockContainerSlot(ContainerSlotType.BREWING_INPUT, 0);
|
||||||
|
}
|
||||||
|
return super.javaSlotToBedrockContainer(slot);
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* 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, ContainerSlotType bedrockSourceContainer,
|
||||||
|
int javaSourceSlot, ContainerSlotType bedrockDestinationContainer, int javaDestinationSlot) {
|
||||||
|
if (javaDestinationSlot == 0) {
|
||||||
|
// Bedrock Edition can use paper or an empty map in slot 0
|
||||||
|
GeyserItemStack itemStack = javaSourceSlot == -1 ? session.getPlayerInventory().getCursor() : inventory.getItem(javaSourceSlot);
|
||||||
|
return itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:paper") || itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:map");
|
||||||
|
} else if (javaDestinationSlot == 1) {
|
||||||
|
// Bedrock Edition can use a compass to create locator maps, or use a filled map, in the ADDITIONAL slot
|
||||||
|
GeyserItemStack itemStack = javaSourceSlot == -1 ? session.getPlayerInventory().getCursor() : inventory.getItem(javaSourceSlot);
|
||||||
|
return itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:compass") || itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:filled_map");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
|
||||||
|
switch (slotInfoData.getContainer()) {
|
||||||
|
case CARTOGRAPHY_INPUT:
|
||||||
|
return 0;
|
||||||
|
case CARTOGRAPHY_ADDITIONAL:
|
||||||
|
return 1;
|
||||||
|
case CARTOGRAPHY_RESULT:
|
||||||
|
case 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, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
}
|
@ -23,39 +23,50 @@
|
|||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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.ContainerSlotType;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
|
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
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.data.inventory.InventorySource;
|
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.network.translators.inventory.SlotType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater;
|
||||||
import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
|
|
||||||
import org.geysermc.connector.utils.InventoryUtils;
|
|
||||||
|
|
||||||
import java.util.List;
|
public class CraftingInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||||
|
|
||||||
public class CraftingInventoryTranslator extends BlockInventoryTranslator {
|
|
||||||
public CraftingInventoryTranslator() {
|
public CraftingInventoryTranslator() {
|
||||||
super(10, "minecraft:crafting_table", ContainerType.WORKBENCH, new CursorInventoryUpdater());
|
super(10, "minecraft:crafting_table", ContainerType.WORKBENCH, UIInventoryUpdater.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int bedrockSlotToJava(InventoryActionData action) {
|
public SlotType getSlotType(int javaSlot) {
|
||||||
if (action.getSlot() == 50) {
|
if (javaSlot == 0) {
|
||||||
// Slot 50 is used for crafting with a controller.
|
return SlotType.OUTPUT;
|
||||||
|
}
|
||||||
|
return SlotType.NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
|
||||||
|
if (slot >= 1 && slot <= 9) {
|
||||||
|
return new BedrockContainerSlot(ContainerSlotType.CRAFTING_INPUT, slot + 31);
|
||||||
|
}
|
||||||
|
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 0;
|
||||||
}
|
}
|
||||||
|
return super.bedrockSlotToJava(slotInfoData);
|
||||||
if (action.getSource().getContainerId() == ContainerId.UI) {
|
|
||||||
int slotnum = action.getSlot();
|
|
||||||
if (slotnum >= 32 && 42 >= slotnum) {
|
|
||||||
return slotnum - 31;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.bedrockSlotToJava(action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -65,25 +76,4 @@ public class CraftingInventoryTranslator extends BlockInventoryTranslator {
|
|||||||
}
|
}
|
||||||
return super.javaSlotToBedrock(slot);
|
return super.javaSlotToBedrock(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public SlotType getSlotType(int javaSlot) {
|
|
||||||
if (javaSlot == 0)
|
|
||||||
return SlotType.OUTPUT;
|
|
||||||
return SlotType.NORMAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
|
|
||||||
if (session.getGameMode() == GameMode.CREATIVE) {
|
|
||||||
for (InventoryActionData action : actions) {
|
|
||||||
if (action.getSource().getType() == InventorySource.Type.CREATIVE) {
|
|
||||||
updateInventory(session, inventory);
|
|
||||||
InventoryUtils.updateCursor(session);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.translateActions(session, inventory, actions);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
* 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.*;
|
||||||
|
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.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()));
|
||||||
|
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, ItemStackRequest 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);
|
||||||
|
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, windowType, 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,71 @@
|
|||||||
|
/*
|
||||||
|
* 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.packet.ContainerOpenPacket;
|
||||||
|
import org.geysermc.connector.inventory.Generic3X3Container;
|
||||||
|
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.ContainerInventoryUpdater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Droppers and dispensers
|
||||||
|
*/
|
||||||
|
public class Generic3X3InventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||||
|
public Generic3X3InventoryTranslator() {
|
||||||
|
super(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER, ContainerInventoryUpdater.INSTANCE,
|
||||||
|
"minecraft:dropper");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
return new Generic3X3Container(name, windowId, this.size, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void openInventory(GeyserSession session, Inventory inventory) {
|
||||||
|
ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
|
||||||
|
containerOpenPacket.setId((byte) inventory.getId());
|
||||||
|
// Required for opening the real block - otherwise, if the container type is incorrect, it refuses to open
|
||||||
|
containerOpenPacket.setType(((Generic3X3Container) inventory).isDropper() ? ContainerType.DROPPER : ContainerType.DISPENSER);
|
||||||
|
containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
|
||||||
|
containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
|
||||||
|
session.sendUpstreamPacket(containerOpenPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
* @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.ContainerType;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
|
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
|
||||||
import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
|
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater;
|
||||||
public class GrindstoneInventoryTranslator extends BlockInventoryTranslator {
|
|
||||||
|
|
||||||
|
public class GrindstoneInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||||
public GrindstoneInventoryTranslator() {
|
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
|
@Override
|
||||||
public int bedrockSlotToJava(InventoryActionData action) {
|
public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
|
||||||
final int slot = super.bedrockSlotToJava(action);
|
switch (slotInfoData.getContainer()) {
|
||||||
if (action.getSource().getContainerId() == ContainerId.UI) {
|
case GRINDSTONE_INPUT:
|
||||||
switch (slot) {
|
return 0;
|
||||||
case 16:
|
case GRINDSTONE_ADDITIONAL:
|
||||||
return 0;
|
return 1;
|
||||||
case 17:
|
case GRINDSTONE_RESULT:
|
||||||
return 1;
|
case CREATIVE_OUTPUT:
|
||||||
case 50:
|
return 2;
|
||||||
return 2;
|
}
|
||||||
default:
|
return super.bedrockSlotToJava(slotInfoData);
|
||||||
return slot;
|
}
|
||||||
}
|
|
||||||
} return slot;
|
@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
|
@Override
|
||||||
@ -65,5 +75,4 @@ public class GrindstoneInventoryTranslator extends BlockInventoryTranslator {
|
|||||||
}
|
}
|
||||||
return super.javaSlotToBedrock(slot);
|
return super.javaSlotToBedrock(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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 HopperInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||||
|
public HopperInventoryTranslator() {
|
||||||
|
super(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, ContainerInventoryUpdater.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BedrockContainerSlot javaSlotToBedrockContainer(int javaSlot) {
|
||||||
|
if (javaSlot < this.size) {
|
||||||
|
return new BedrockContainerSlot(ContainerSlotType.CONTAINER, javaSlot);
|
||||||
|
}
|
||||||
|
return super.javaSlotToBedrockContainer(javaSlot);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* 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.nbt.NbtType;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
public class LecternInventoryTranslator extends BaseInventoryTranslator {
|
||||||
|
private final InventoryUpdater updater;
|
||||||
|
|
||||||
|
public LecternInventoryTranslator() {
|
||||||
|
super(1);
|
||||||
|
this.updater = new InventoryUpdater();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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(), false);
|
||||||
|
} 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();
|
||||||
|
// Position has to be the last interacted position... right?
|
||||||
|
Vector3i position = session.getLastInteractionBlockPosition();
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
NbtMap blockEntityTag;
|
||||||
|
if (tag != null) {
|
||||||
|
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());
|
||||||
|
blockEntityTag = lecternTag.build();
|
||||||
|
} else {
|
||||||
|
// There is *a* book here, but... no NBT.
|
||||||
|
NbtMapBuilder lecternTag = getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 1);
|
||||||
|
NbtMapBuilder bookTag = NbtMap.builder()
|
||||||
|
.putByte("Count", (byte) 1)
|
||||||
|
.putShort("Damage", (short) 0)
|
||||||
|
.putString("Name", "minecraft:writable_book")
|
||||||
|
.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, Collections.singletonList(
|
||||||
|
NbtMap.builder()
|
||||||
|
.putString("photoname", "")
|
||||||
|
.putString("text", "")
|
||||||
|
.build()
|
||||||
|
)).build());
|
||||||
|
|
||||||
|
blockEntityTag = lecternTag.putCompound("book", bookTag.build()).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(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
return new LecternContainer(name, windowId, this.size, windowType, 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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,231 @@
|
|||||||
|
/*
|
||||||
|
* 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.ItemStackRequest;
|
||||||
|
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.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.SlotType;
|
||||||
|
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, ContainerSlotType bedrockSourceContainer,
|
||||||
|
int javaSourceSlot, ContainerSlotType bedrockDestinationContainer, 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, ItemStackRequest 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);
|
||||||
|
session.sendDownstreamPacket(packet);
|
||||||
|
|
||||||
|
GeyserItemStack inputCopy = inventory.getItem(0).copy(1);
|
||||||
|
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) {
|
||||||
|
switch (slotInfoData.getContainer()) {
|
||||||
|
case LOOM_INPUT:
|
||||||
|
return 0;
|
||||||
|
case LOOM_DYE:
|
||||||
|
return 1;
|
||||||
|
case LOOM_MATERIAL:
|
||||||
|
return 2;
|
||||||
|
case LOOM_RESULT:
|
||||||
|
case 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SlotType getSlotType(int javaSlot) {
|
||||||
|
if (javaSlot == 3) {
|
||||||
|
return SlotType.OUTPUT;
|
||||||
|
}
|
||||||
|
return super.getSlotType(javaSlot);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* 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.ContainerSlotType;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.ItemStackRequest;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket;
|
||||||
|
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
|
||||||
|
//TODO: send a blank inventory here in case the villager doesn't send a TradeList packet
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closeInventory(GeyserSession session, Inventory inventory) {
|
||||||
|
MerchantContainer merchantInventory = (MerchantContainer) inventory;
|
||||||
|
if (merchantInventory.getVillager() != null) {
|
||||||
|
merchantInventory.getVillager().despawnEntity(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStackResponsePacket.Response translateAutoCraftingRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) {
|
||||||
|
// We're not crafting here
|
||||||
|
// Called at least by consoles when pressing a trade option button
|
||||||
|
return translateRequest(session, inventory, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,490 @@
|
|||||||
|
/*
|
||||||
|
* 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.entity.player.GameMode;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
|
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.*;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.*;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
|
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.InventoryTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.SlotType;
|
||||||
|
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.LanguageUtils;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
public class PlayerInventoryTranslator extends InventoryTranslator {
|
||||||
|
private static final ItemData UNUSUABLE_CRAFTING_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock(LanguageUtils.getLocaleStringLog("geyser.inventory.unusable_item.creative"));
|
||||||
|
|
||||||
|
public PlayerInventoryTranslator() {
|
||||||
|
super(46);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateInventory(GeyserSession session, Inventory inventory) {
|
||||||
|
updateCraftingGrid(session, inventory);
|
||||||
|
|
||||||
|
InventoryContentPacket inventoryContentPacket = new InventoryContentPacket();
|
||||||
|
inventoryContentPacket.setContainerId(ContainerId.INVENTORY);
|
||||||
|
ItemData[] contents = new ItemData[36];
|
||||||
|
// Inventory
|
||||||
|
for (int i = 9; i < 36; i++) {
|
||||||
|
contents[i] = inventory.getItem(i).getItemData(session);
|
||||||
|
}
|
||||||
|
// Hotbar
|
||||||
|
for (int i = 36; i < 45; i++) {
|
||||||
|
contents[i - 36] = inventory.getItem(i).getItemData(session);
|
||||||
|
}
|
||||||
|
inventoryContentPacket.setContents(Arrays.asList(contents));
|
||||||
|
session.sendUpstreamPacket(inventoryContentPacket);
|
||||||
|
|
||||||
|
// Armor
|
||||||
|
InventoryContentPacket armorContentPacket = new InventoryContentPacket();
|
||||||
|
armorContentPacket.setContainerId(ContainerId.ARMOR);
|
||||||
|
contents = new ItemData[4];
|
||||||
|
for (int i = 5; i < 9; i++) {
|
||||||
|
contents[i - 5] = inventory.getItem(i).getItemData(session);
|
||||||
|
}
|
||||||
|
armorContentPacket.setContents(Arrays.asList(contents));
|
||||||
|
session.sendUpstreamPacket(armorContentPacket);
|
||||||
|
|
||||||
|
// Offhand
|
||||||
|
InventoryContentPacket offhandPacket = new InventoryContentPacket();
|
||||||
|
offhandPacket.setContainerId(ContainerId.OFFHAND);
|
||||||
|
offhandPacket.setContents(Collections.singletonList(inventory.getItem(45).getItemData(session)));
|
||||||
|
session.sendUpstreamPacket(offhandPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the crafting grid for the player to hide/show the barriers in the creative inventory
|
||||||
|
* @param session Session of the player
|
||||||
|
* @param inventory Inventory of the player
|
||||||
|
*/
|
||||||
|
public static void updateCraftingGrid(GeyserSession session, Inventory inventory) {
|
||||||
|
// Crafting grid
|
||||||
|
for (int i = 1; i < 5; i++) {
|
||||||
|
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||||
|
slotPacket.setContainerId(ContainerId.UI);
|
||||||
|
slotPacket.setSlot(i + 27);
|
||||||
|
|
||||||
|
if (session.getGameMode() == GameMode.CREATIVE) {
|
||||||
|
slotPacket.setItem(UNUSUABLE_CRAFTING_SPACE_BLOCK);
|
||||||
|
} else {
|
||||||
|
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i).getItemStack()));
|
||||||
|
}
|
||||||
|
|
||||||
|
session.sendUpstreamPacket(slotPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
|
||||||
|
if (slot >= 1 && slot <= 44) {
|
||||||
|
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||||
|
if (slot >= 9) {
|
||||||
|
slotPacket.setContainerId(ContainerId.INVENTORY);
|
||||||
|
if (slot >= 36) {
|
||||||
|
slotPacket.setSlot(slot - 36);
|
||||||
|
} else {
|
||||||
|
slotPacket.setSlot(slot);
|
||||||
|
}
|
||||||
|
} else if (slot >= 5) {
|
||||||
|
slotPacket.setContainerId(ContainerId.ARMOR);
|
||||||
|
slotPacket.setSlot(slot - 5);
|
||||||
|
} else {
|
||||||
|
slotPacket.setContainerId(ContainerId.UI);
|
||||||
|
slotPacket.setSlot(slot + 27);
|
||||||
|
}
|
||||||
|
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(inventory.getItem(slot).getItemData(session)));
|
||||||
|
session.sendUpstreamPacket(offhandPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
// Hotbar
|
||||||
|
if (slotnum >= 0 && slotnum <= 8) {
|
||||||
|
return slotnum + 36;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ARMOR:
|
||||||
|
if (slotnum >= 0 && slotnum <= 3) {
|
||||||
|
return slotnum + 5;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OFFHAND:
|
||||||
|
return 45;
|
||||||
|
case CRAFTING_INPUT:
|
||||||
|
if (slotnum >= 28 && 31 >= slotnum) {
|
||||||
|
return slotnum - 27;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CREATIVE_OUTPUT:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return slotnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int javaSlotToBedrock(int 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
|
||||||
|
public SlotType getSlotType(int javaSlot) {
|
||||||
|
if (javaSlot == 0)
|
||||||
|
return SlotType.OUTPUT;
|
||||||
|
return SlotType.NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStackResponsePacket.Response translateRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) {
|
||||||
|
if (session.getGameMode() != GameMode.CREATIVE) {
|
||||||
|
return super.translateRequest(session, inventory, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerInventory playerInv = session.getPlayerInventory();
|
||||||
|
IntSet affectedSlots = new IntOpenHashSet();
|
||||||
|
for (StackRequestActionData action : request.getActions()) {
|
||||||
|
switch (action.getType()) {
|
||||||
|
case TAKE:
|
||||||
|
case PLACE: {
|
||||||
|
TransferStackRequestActionData transferAction = (TransferStackRequestActionData) action;
|
||||||
|
if (!(checkNetId(session, inventory, transferAction.getSource()) && checkNetId(session, inventory, transferAction.getDestination()))) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
if (isCraftingGrid(transferAction.getSource()) || isCraftingGrid(transferAction.getDestination())) {
|
||||||
|
return rejectRequest(request, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int transferAmount = transferAction.getCount();
|
||||||
|
if (isCursor(transferAction.getDestination())) {
|
||||||
|
int sourceSlot = bedrockSlotToJava(transferAction.getSource());
|
||||||
|
GeyserItemStack sourceItem = inventory.getItem(sourceSlot);
|
||||||
|
if (playerInv.getCursor().isEmpty()) {
|
||||||
|
playerInv.setCursor(sourceItem.copy(0), session);
|
||||||
|
}
|
||||||
|
|
||||||
|
playerInv.getCursor().add(transferAmount);
|
||||||
|
sourceItem.sub(transferAmount);
|
||||||
|
|
||||||
|
affectedSlots.add(sourceSlot);
|
||||||
|
} else if (isCursor(transferAction.getSource())) {
|
||||||
|
int destSlot = bedrockSlotToJava(transferAction.getDestination());
|
||||||
|
GeyserItemStack sourceItem = playerInv.getCursor();
|
||||||
|
if (inventory.getItem(destSlot).isEmpty()) {
|
||||||
|
inventory.setItem(destSlot, sourceItem.copy(0), session);
|
||||||
|
}
|
||||||
|
|
||||||
|
inventory.getItem(destSlot).add(transferAmount);
|
||||||
|
sourceItem.sub(transferAmount);
|
||||||
|
|
||||||
|
affectedSlots.add(destSlot);
|
||||||
|
} else {
|
||||||
|
int sourceSlot = bedrockSlotToJava(transferAction.getSource());
|
||||||
|
int destSlot = bedrockSlotToJava(transferAction.getDestination());
|
||||||
|
GeyserItemStack sourceItem = inventory.getItem(sourceSlot);
|
||||||
|
if (inventory.getItem(destSlot).isEmpty()) {
|
||||||
|
inventory.setItem(destSlot, sourceItem.copy(0), session);
|
||||||
|
}
|
||||||
|
|
||||||
|
inventory.getItem(destSlot).add(transferAmount);
|
||||||
|
sourceItem.sub(transferAmount);
|
||||||
|
|
||||||
|
affectedSlots.add(sourceSlot);
|
||||||
|
affectedSlots.add(destSlot);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SWAP: {
|
||||||
|
SwapStackRequestActionData swapAction = (SwapStackRequestActionData) action;
|
||||||
|
if (!(checkNetId(session, inventory, swapAction.getSource()) && checkNetId(session, inventory, swapAction.getDestination()))) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
if (isCraftingGrid(swapAction.getSource()) || isCraftingGrid(swapAction.getDestination())) {
|
||||||
|
return rejectRequest(request, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCursor(swapAction.getDestination())) {
|
||||||
|
int sourceSlot = bedrockSlotToJava(swapAction.getSource());
|
||||||
|
GeyserItemStack sourceItem = inventory.getItem(sourceSlot);
|
||||||
|
GeyserItemStack destItem = playerInv.getCursor();
|
||||||
|
|
||||||
|
playerInv.setCursor(sourceItem, session);
|
||||||
|
inventory.setItem(sourceSlot, destItem, session);
|
||||||
|
|
||||||
|
affectedSlots.add(sourceSlot);
|
||||||
|
} else if (isCursor(swapAction.getSource())) {
|
||||||
|
int destSlot = bedrockSlotToJava(swapAction.getDestination());
|
||||||
|
GeyserItemStack sourceItem = playerInv.getCursor();
|
||||||
|
GeyserItemStack destItem = inventory.getItem(destSlot);
|
||||||
|
|
||||||
|
inventory.setItem(destSlot, sourceItem, session);
|
||||||
|
playerInv.setCursor(destItem, session);
|
||||||
|
|
||||||
|
affectedSlots.add(destSlot);
|
||||||
|
} else {
|
||||||
|
int sourceSlot = bedrockSlotToJava(swapAction.getSource());
|
||||||
|
int destSlot = bedrockSlotToJava(swapAction.getDestination());
|
||||||
|
GeyserItemStack sourceItem = inventory.getItem(sourceSlot);
|
||||||
|
GeyserItemStack destItem = inventory.getItem(destSlot);
|
||||||
|
|
||||||
|
inventory.setItem(destSlot, sourceItem, session);
|
||||||
|
inventory.setItem(sourceSlot, destItem, session);
|
||||||
|
|
||||||
|
affectedSlots.add(sourceSlot);
|
||||||
|
affectedSlots.add(destSlot);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DROP: {
|
||||||
|
DropStackRequestActionData dropAction = (DropStackRequestActionData) action;
|
||||||
|
if (!checkNetId(session, inventory, dropAction.getSource())) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
if (isCraftingGrid(dropAction.getSource())) {
|
||||||
|
return rejectRequest(request, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
GeyserItemStack sourceItem;
|
||||||
|
if (isCursor(dropAction.getSource())) {
|
||||||
|
sourceItem = playerInv.getCursor();
|
||||||
|
} else {
|
||||||
|
int sourceSlot = bedrockSlotToJava(dropAction.getSource());
|
||||||
|
sourceItem = inventory.getItem(sourceSlot);
|
||||||
|
affectedSlots.add(sourceSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sourceItem.isEmpty()) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientCreativeInventoryActionPacket creativeDropPacket = new ClientCreativeInventoryActionPacket(-1, sourceItem.getItemStack(dropAction.getCount()));
|
||||||
|
session.sendDownstreamPacket(creativeDropPacket);
|
||||||
|
|
||||||
|
sourceItem.sub(dropAction.getCount());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DESTROY: {
|
||||||
|
// Only called when a creative client wants to destroy an item... I think - Camotoy
|
||||||
|
DestroyStackRequestActionData destroyAction = (DestroyStackRequestActionData) action;
|
||||||
|
if (!checkNetId(session, inventory, destroyAction.getSource())) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
if (isCraftingGrid(destroyAction.getSource())) {
|
||||||
|
return rejectRequest(request, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isCursor(destroyAction.getSource())) {
|
||||||
|
// Item exists; let's remove it from the inventory
|
||||||
|
int javaSlot = bedrockSlotToJava(destroyAction.getSource());
|
||||||
|
GeyserItemStack existingItem = inventory.getItem(javaSlot);
|
||||||
|
existingItem.sub(destroyAction.getCount());
|
||||||
|
affectedSlots.add(javaSlot);
|
||||||
|
} else {
|
||||||
|
// Just sync up the item on our end, since the server doesn't care what's in our cursor
|
||||||
|
playerInv.getCursor().sub(destroyAction.getCount());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
session.getConnector().getLogger().error("Unknown crafting state induced by " + session.getName());
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int slot : affectedSlots) {
|
||||||
|
sendCreativeAction(session, inventory, slot);
|
||||||
|
}
|
||||||
|
return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStackResponsePacket.Response translateCreativeRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) {
|
||||||
|
ItemStack javaCreativeItem = null;
|
||||||
|
IntSet affectedSlots = new IntOpenHashSet();
|
||||||
|
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;
|
||||||
|
|
||||||
|
int creativeId = creativeAction.getCreativeItemNetworkId() - 1;
|
||||||
|
if (creativeId < 0 || creativeId >= ItemRegistry.CREATIVE_ITEMS.length) {
|
||||||
|
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];
|
||||||
|
javaCreativeItem = ItemTranslator.translateToJava(creativeItem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CRAFT_RESULTS_DEPRECATED: {
|
||||||
|
CraftResultsDeprecatedStackRequestActionData deprecatedCraftAction = (CraftResultsDeprecatedStackRequestActionData) action;
|
||||||
|
if (craftState != CraftState.RECIPE_ID) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
craftState = CraftState.DEPRECATED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DESTROY: {
|
||||||
|
DestroyStackRequestActionData destroyAction = (DestroyStackRequestActionData) action;
|
||||||
|
if (craftState != CraftState.DEPRECATED) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sourceSlot = bedrockSlotToJava(destroyAction.getSource());
|
||||||
|
inventory.setItem(sourceSlot, GeyserItemStack.EMPTY, session); //assume all creative destroy requests will empty the slot
|
||||||
|
affectedSlots.add(sourceSlot);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TAKE:
|
||||||
|
case PLACE: {
|
||||||
|
TransferStackRequestActionData transferAction = (TransferStackRequestActionData) action;
|
||||||
|
if (!(craftState == CraftState.DEPRECATED || craftState == CraftState.TRANSFER)) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
craftState = CraftState.TRANSFER;
|
||||||
|
|
||||||
|
if (transferAction.getSource().getContainer() != ContainerSlotType.CREATIVE_OUTPUT) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCursor(transferAction.getDestination())) {
|
||||||
|
if (session.getPlayerInventory().getCursor().isEmpty()) {
|
||||||
|
GeyserItemStack newItemStack = GeyserItemStack.from(javaCreativeItem);
|
||||||
|
newItemStack.setAmount(transferAction.getCount());
|
||||||
|
session.getPlayerInventory().setCursor(newItemStack, session);
|
||||||
|
} else {
|
||||||
|
session.getPlayerInventory().getCursor().add(transferAction.getCount());
|
||||||
|
}
|
||||||
|
//cursor is always included in response
|
||||||
|
} else {
|
||||||
|
int destSlot = bedrockSlotToJava(transferAction.getDestination());
|
||||||
|
if (inventory.getItem(destSlot).isEmpty()) {
|
||||||
|
GeyserItemStack newItemStack = GeyserItemStack.from(javaCreativeItem);
|
||||||
|
newItemStack.setAmount(transferAction.getCount());
|
||||||
|
inventory.setItem(destSlot, newItemStack, session);
|
||||||
|
} else {
|
||||||
|
inventory.getItem(destSlot).add(transferAction.getCount());
|
||||||
|
}
|
||||||
|
affectedSlots.add(destSlot);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int slot : affectedSlots) {
|
||||||
|
sendCreativeAction(session, inventory, slot);
|
||||||
|
}
|
||||||
|
return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sendCreativeAction(GeyserSession session, Inventory inventory, int slot) {
|
||||||
|
GeyserItemStack item = inventory.getItem(slot);
|
||||||
|
ItemStack itemStack = item.isEmpty() ? new ItemStack(-1, 0, null) : item.getItemStack();
|
||||||
|
|
||||||
|
ClientCreativeInventoryActionPacket creativePacket = new ClientCreativeInventoryActionPacket(slot, itemStack);
|
||||||
|
session.sendDownstreamPacket(creativePacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isCraftingGrid(StackRequestSlotInfoData slotInfoData) {
|
||||||
|
return slotInfoData.getContainer() == ContainerSlotType.CRAFTING_INPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* 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.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.packet.BlockEntityDataPacket;
|
||||||
|
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.holder.BlockInventoryHolder;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
|
||||||
|
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
|
||||||
|
|
||||||
|
public class ShulkerInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||||
|
public ShulkerInventoryTranslator() {
|
||||||
|
super(27, new BlockInventoryHolder("minecraft:shulker_box[facing=north]", ContainerType.CONTAINER) {
|
||||||
|
private final BlockEntityTranslator shulkerBoxTranslator = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATORS.get("ShulkerBox");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isValidBlock(String[] javaBlockString) {
|
||||||
|
return javaBlockString[0].contains("shulker_box");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setCustomName(GeyserSession session, Vector3i position, Inventory inventory, int javaBlockState) {
|
||||||
|
NbtMapBuilder tag = NbtMap.builder()
|
||||||
|
.putInt("x", position.getX())
|
||||||
|
.putInt("y", position.getY())
|
||||||
|
.putInt("z", position.getZ())
|
||||||
|
.putString("CustomName", inventory.getTitle());
|
||||||
|
// Don't reset facing property
|
||||||
|
shulkerBoxTranslator.translateTag(tag, null, javaBlockState);
|
||||||
|
|
||||||
|
BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
|
||||||
|
dataPacket.setData(tag.build());
|
||||||
|
dataPacket.setBlockPosition(position);
|
||||||
|
session.sendUpstreamPacket(dataPacket);
|
||||||
|
}
|
||||||
|
}, 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
|
* @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.ContainerType;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
|
import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData;
|
||||||
import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
|
import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater;
|
||||||
public class SmithingInventoryTranslator extends BlockInventoryTranslator {
|
|
||||||
|
|
||||||
|
public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||||
public SmithingInventoryTranslator() {
|
public SmithingInventoryTranslator() {
|
||||||
super(3, "minecraft:smithing_table", ContainerType.SMITHING_TABLE, new CursorInventoryUpdater());
|
super(3, "minecraft:smithing_table", ContainerType.SMITHING_TABLE, UIInventoryUpdater.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int bedrockSlotToJava(InventoryActionData action) {
|
public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) {
|
||||||
final int slot = super.bedrockSlotToJava(action);
|
switch (slotInfoData.getContainer()) {
|
||||||
if (action.getSource().getContainerId() == ContainerId.UI) {
|
case SMITHING_TABLE_INPUT:
|
||||||
switch (slot) {
|
return 0;
|
||||||
case 51:
|
case SMITHING_TABLE_MATERIAL:
|
||||||
return 0;
|
return 1;
|
||||||
case 52:
|
case SMITHING_TABLE_RESULT:
|
||||||
return 1;
|
case CREATIVE_OUTPUT:
|
||||||
case 50:
|
return 2;
|
||||||
return 2;
|
}
|
||||||
default:
|
return super.bedrockSlotToJava(slotInfoData);
|
||||||
return slot;
|
}
|
||||||
}
|
|
||||||
} return slot;
|
@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
|
@Override
|
||||||
@ -65,5 +75,4 @@ public class SmithingInventoryTranslator extends BlockInventoryTranslator {
|
|||||||
}
|
}
|
||||||
return super.javaSlotToBedrock(slot);
|
return super.javaSlotToBedrock(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* 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.ItemStackRequest;
|
||||||
|
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.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, ItemStackRequest 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStack javaOutput = ItemTranslator.translateToJava(craftData.getResultItems()[0]);
|
||||||
|
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);
|
||||||
|
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) {
|
||||||
|
switch (slotInfoData.getContainer()) {
|
||||||
|
case STONECUTTER_INPUT:
|
||||||
|
return 0;
|
||||||
|
case STONECUTTER_RESULT:
|
||||||
|
case 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, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
}
|
@ -23,16 +23,15 @@
|
|||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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.inventory.Inventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
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.ChestInventoryUpdater;
|
||||||
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
|
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 {
|
public abstract class ChestInventoryTranslator extends BaseInventoryTranslator {
|
||||||
private final InventoryUpdater updater;
|
private final InventoryUpdater updater;
|
||||||
@ -42,6 +41,16 @@ public abstract class ChestInventoryTranslator extends BaseInventoryTranslator {
|
|||||||
this.updater = new ChestInventoryUpdater(paddedSize);
|
this.updater = new ChestInventoryUpdater(paddedSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldRejectItemPlace(GeyserSession session, Inventory inventory, ContainerSlotType bedrockSourceContainer,
|
||||||
|
int javaSourceSlot, ContainerSlotType bedrockDestinationContainer, int javaDestinationSlot) {
|
||||||
|
// Reject any item placements that occur in the unusable inventory space
|
||||||
|
if (bedrockSourceContainer == ContainerSlotType.CONTAINER && javaSourceSlot >= this.size) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return bedrockDestinationContainer == ContainerSlotType.CONTAINER && javaDestinationSlot >= this.size;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateInventory(GeyserSession session, Inventory inventory) {
|
public void updateInventory(GeyserSession session, Inventory inventory) {
|
||||||
updater.updateInventory(this, session, inventory);
|
updater.updateInventory(this, session, inventory);
|
||||||
@ -53,17 +62,10 @@ public abstract class ChestInventoryTranslator extends BaseInventoryTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
|
public BedrockContainerSlot javaSlotToBedrockContainer(int javaSlot) {
|
||||||
for (InventoryActionData action : actions) {
|
if (javaSlot < this.size) {
|
||||||
if (action.getSource().getContainerId() == inventory.getId()) {
|
return new BedrockContainerSlot(ContainerSlotType.CONTAINER, javaSlot);
|
||||||
if (action.getSlot() >= size) {
|
|
||||||
updateInventory(session, inventory);
|
|
||||||
InventoryUtils.updateCursor(session);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return super.javaSlotToBedrockContainer(javaSlot);
|
||||||
super.translateActions(session, inventory, actions);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -23,32 +23,66 @@
|
|||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import com.nukkitx.nbt.NbtMap;
|
import com.nukkitx.nbt.NbtMap;
|
||||||
|
import com.nukkitx.nbt.NbtMapBuilder;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
||||||
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
|
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
|
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
||||||
|
import org.geysermc.connector.inventory.Container;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.world.block.DoubleChestValue;
|
||||||
|
import org.geysermc.connector.network.translators.world.block.entity.DoubleChestBlockEntityTranslator;
|
||||||
|
|
||||||
public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
|
public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
|
||||||
private final int javaBlockState;
|
private final int defaultJavaBlockState;
|
||||||
|
|
||||||
public DoubleChestInventoryTranslator(int size) {
|
public DoubleChestInventoryTranslator(int size) {
|
||||||
super(size, 54);
|
super(size, 54);
|
||||||
this.javaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]");
|
this.defaultJavaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareInventory(GeyserSession session, Inventory inventory) {
|
public void prepareInventory(GeyserSession session, Inventory inventory) {
|
||||||
|
// See BlockInventoryHolder - same concept there except we're also dealing with a specific block state
|
||||||
|
if (session.getLastInteractionPlayerPosition().equals(session.getPlayerEntity().getPosition())) {
|
||||||
|
int javaBlockId = session.getConnector().getWorldManager().getBlockAt(session, session.getLastInteractionBlockPosition());
|
||||||
|
String[] javaBlockString = BlockTranslator.getJavaIdBlockMap().inverse().getOrDefault(javaBlockId, "minecraft:air").split("\\[");
|
||||||
|
if (javaBlockString.length > 1 && (javaBlockString[0].equals("minecraft:chest") || javaBlockString[0].equals("minecraft:trapped_chest"))
|
||||||
|
&& !javaBlockString[1].contains("type=single")) {
|
||||||
|
inventory.setHolderPosition(session.getLastInteractionBlockPosition());
|
||||||
|
((Container) inventory).setUsingRealBlock(true, javaBlockString[0]);
|
||||||
|
|
||||||
|
NbtMapBuilder tag = NbtMap.builder()
|
||||||
|
.putString("id", "Chest")
|
||||||
|
.putInt("x", session.getLastInteractionBlockPosition().getX())
|
||||||
|
.putInt("y", session.getLastInteractionBlockPosition().getY())
|
||||||
|
.putInt("z", session.getLastInteractionBlockPosition().getZ())
|
||||||
|
.putString("CustomName", inventory.getTitle())
|
||||||
|
.putString("id", "Chest");
|
||||||
|
|
||||||
|
DoubleChestValue chestValue = BlockStateValues.getDoubleChestValues().get(javaBlockId);
|
||||||
|
DoubleChestBlockEntityTranslator.translateChestValue(tag, chestValue,
|
||||||
|
session.getLastInteractionBlockPosition().getX(), session.getLastInteractionBlockPosition().getZ());
|
||||||
|
|
||||||
|
BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
|
||||||
|
dataPacket.setData(tag.build());
|
||||||
|
dataPacket.setBlockPosition(session.getLastInteractionBlockPosition());
|
||||||
|
session.sendUpstreamPacket(dataPacket);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Vector3i position = session.getPlayerEntity().getPosition().toInt().add(Vector3i.UP);
|
Vector3i position = session.getPlayerEntity().getPosition().toInt().add(Vector3i.UP);
|
||||||
Vector3i pairPosition = position.add(Vector3i.UNIT_X);
|
Vector3i pairPosition = position.add(Vector3i.UNIT_X);
|
||||||
int bedrockBlockId = session.getBlockTranslator().getBedrockBlockId(javaBlockState);
|
int bedrockBlockId = session.getBlockTranslator().getBedrockBlockId(defaultJavaBlockState);
|
||||||
|
|
||||||
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
|
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
|
||||||
blockPacket.setDataLayer(0);
|
blockPacket.setDataLayer(0);
|
||||||
@ -105,9 +139,18 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeInventory(GeyserSession session, Inventory inventory) {
|
public void closeInventory(GeyserSession session, Inventory inventory) {
|
||||||
|
if (((Container) inventory).isUsingRealBlock()) {
|
||||||
|
// No need to reset a block since we didn't change any blocks
|
||||||
|
// But send a container close packet because we aren't destroying the original.
|
||||||
|
ContainerClosePacket packet = new ContainerClosePacket();
|
||||||
|
packet.setId((byte) inventory.getId());
|
||||||
|
packet.setUnknownBool0(true); //TODO needs to be changed in Protocol to "server-side" or something
|
||||||
|
session.sendUpstreamPacket(packet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Vector3i holderPos = inventory.getHolderPosition();
|
Vector3i holderPos = inventory.getHolderPosition();
|
||||||
Position pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
|
int realBlock = session.getConnector().getWorldManager().getBlockAt(session, holderPos);
|
||||||
int realBlock = session.getConnector().getWorldManager().getBlockAt(session, pos.getX(), pos.getY(), pos.getZ());
|
|
||||||
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
|
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
|
||||||
blockPacket.setDataLayer(0);
|
blockPacket.setDataLayer(0);
|
||||||
blockPacket.setBlockPosition(holderPos);
|
blockPacket.setBlockPosition(holderPos);
|
||||||
@ -115,8 +158,7 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
|
|||||||
session.sendUpstreamPacket(blockPacket);
|
session.sendUpstreamPacket(blockPacket);
|
||||||
|
|
||||||
holderPos = holderPos.add(Vector3i.UNIT_X);
|
holderPos = holderPos.add(Vector3i.UNIT_X);
|
||||||
pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
|
realBlock = session.getConnector().getWorldManager().getBlockAt(session, holderPos);
|
||||||
realBlock = session.getConnector().getWorldManager().getBlockAt(session, pos.getX(), pos.getY(), pos.getZ());
|
|
||||||
blockPacket = new UpdateBlockPacket();
|
blockPacket = new UpdateBlockPacket();
|
||||||
blockPacket.setDataLayer(0);
|
blockPacket.setDataLayer(0);
|
||||||
blockPacket.setBlockPosition(holderPos);
|
blockPacket.setBlockPosition(holderPos);
|
@ -23,22 +23,32 @@
|
|||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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 com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.inventory.holder.BlockInventoryHolder;
|
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.holder.InventoryHolder;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
|
||||||
|
|
||||||
public class SingleChestInventoryTranslator extends ChestInventoryTranslator {
|
public class SingleChestInventoryTranslator extends ChestInventoryTranslator {
|
||||||
private final InventoryHolder holder;
|
private final InventoryHolder holder;
|
||||||
|
|
||||||
public SingleChestInventoryTranslator(int size) {
|
public SingleChestInventoryTranslator(int size) {
|
||||||
super(size, 27);
|
super(size, 27);
|
||||||
int javaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]");
|
this.holder = new BlockInventoryHolder("minecraft:chest[facing=north,type=single,waterlogged=false]", ContainerType.CONTAINER,
|
||||||
this.holder = new BlockInventoryHolder(javaBlockState, ContainerType.CONTAINER);
|
"minecraft:ender_chest", "minecraft:trapped_chest") {
|
||||||
|
@Override
|
||||||
|
protected boolean isValidBlock(String[] javaBlockString) {
|
||||||
|
if (javaBlockString[0].equals("minecraft:ender_chest")) {
|
||||||
|
// Can't have double ender chests
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add provision to ensure this isn't a double chest
|
||||||
|
return super.isValidBlock(javaBlockString) && (javaBlockString.length > 1 && javaBlockString[1].contains("type=single"));
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
@ -23,18 +23,21 @@
|
|||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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.data.inventory.ContainerType;
|
||||||
import com.nukkitx.protocol.bedrock.packet.ContainerSetDataPacket;
|
import com.nukkitx.protocol.bedrock.packet.ContainerSetDataPacket;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
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;
|
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
|
||||||
|
|
||||||
public class FurnaceInventoryTranslator extends BlockInventoryTranslator {
|
public abstract class AbstractFurnaceInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||||
public FurnaceInventoryTranslator() {
|
AbstractFurnaceInventoryTranslator(String javaBlockIdentifier, ContainerType containerType) {
|
||||||
super(3, "minecraft:furnace[facing=north,lit=false]", ContainerType.FURNACE, new ContainerInventoryUpdater());
|
super(3, javaBlockIdentifier, containerType, ContainerInventoryUpdater.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -50,9 +53,6 @@ public class FurnaceInventoryTranslator extends BlockInventoryTranslator {
|
|||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
dataPacket.setProperty(ContainerSetDataPacket.FURNACE_TICK_COUNT);
|
dataPacket.setProperty(ContainerSetDataPacket.FURNACE_TICK_COUNT);
|
||||||
if (inventory.getWindowType() == WindowType.BLAST_FURNACE || inventory.getWindowType() == WindowType.SMOKER) {
|
|
||||||
value *= 2;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
@ -67,4 +67,15 @@ public class FurnaceInventoryTranslator extends BlockInventoryTranslator {
|
|||||||
return SlotType.FURNACE_OUTPUT;
|
return SlotType.FURNACE_OUTPUT;
|
||||||
return SlotType.NORMAL;
|
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,40 +23,32 @@
|
|||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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.ContainerType;
|
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.inventory.translators.BaseInventoryTranslator;
|
||||||
import org.geysermc.connector.network.translators.inventory.holder.BlockInventoryHolder;
|
import org.geysermc.connector.network.translators.inventory.updater.HorseInventoryUpdater;
|
||||||
import org.geysermc.connector.network.translators.inventory.holder.InventoryHolder;
|
|
||||||
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
|
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
|
||||||
|
|
||||||
public class BlockInventoryTranslator extends BaseInventoryTranslator {
|
public abstract class AbstractHorseInventoryTranslator extends BaseInventoryTranslator {
|
||||||
private final InventoryHolder holder;
|
|
||||||
private final InventoryUpdater updater;
|
private final InventoryUpdater updater;
|
||||||
|
|
||||||
public BlockInventoryTranslator(int size, String javaBlockIdentifier, ContainerType containerType, InventoryUpdater updater) {
|
public AbstractHorseInventoryTranslator(int size) {
|
||||||
super(size);
|
super(size);
|
||||||
int javaBlockState = BlockTranslator.getJavaBlockState(javaBlockIdentifier);
|
this.updater = HorseInventoryUpdater.INSTANCE;
|
||||||
this.holder = new BlockInventoryHolder(javaBlockState, containerType);
|
|
||||||
this.updater = updater;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareInventory(GeyserSession session, Inventory inventory) {
|
public void prepareInventory(GeyserSession session, Inventory inventory) {
|
||||||
holder.prepareInventory(this, session, inventory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openInventory(GeyserSession session, Inventory inventory) {
|
public void openInventory(GeyserSession session, Inventory inventory) {
|
||||||
holder.openInventory(this, session, inventory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeInventory(GeyserSession session, Inventory inventory) {
|
public void closeInventory(GeyserSession session, Inventory inventory) {
|
||||||
holder.closeInventory(this, session, inventory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* 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));
|
||||||
|
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.inventory.Inventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
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.InventoryUtils;
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
import org.geysermc.connector.utils.LanguageUtils;
|
||||||
|
|
||||||
@ -52,7 +51,7 @@ public class ChestInventoryUpdater extends InventoryUpdater {
|
|||||||
List<ItemData> bedrockItems = new ArrayList<>(paddedSize);
|
List<ItemData> bedrockItems = new ArrayList<>(paddedSize);
|
||||||
for (int i = 0; i < paddedSize; i++) {
|
for (int i = 0; i < paddedSize; i++) {
|
||||||
if (i < translator.size) {
|
if (i < translator.size) {
|
||||||
bedrockItems.add(ItemTranslator.translateToBedrock(session, inventory.getItem(i)));
|
bedrockItems.add(inventory.getItem(i).getItemData(session));
|
||||||
} else {
|
} else {
|
||||||
bedrockItems.add(UNUSUABLE_SPACE_BLOCK);
|
bedrockItems.add(UNUSUABLE_SPACE_BLOCK);
|
||||||
}
|
}
|
||||||
@ -72,7 +71,7 @@ public class ChestInventoryUpdater extends InventoryUpdater {
|
|||||||
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||||
slotPacket.setContainerId(inventory.getId());
|
slotPacket.setContainerId(inventory.getId());
|
||||||
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
|
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
|
||||||
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
|
slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
|
||||||
session.sendUpstreamPacket(slotPacket);
|
session.sendUpstreamPacket(slotPacket);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -31,18 +31,19 @@ import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
|
|||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class ContainerInventoryUpdater extends InventoryUpdater {
|
public class ContainerInventoryUpdater extends InventoryUpdater {
|
||||||
|
public static final ContainerInventoryUpdater INSTANCE = new ContainerInventoryUpdater();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
||||||
super.updateInventory(translator, session, inventory);
|
super.updateInventory(translator, session, inventory);
|
||||||
|
|
||||||
ItemData[] bedrockItems = new ItemData[translator.size];
|
ItemData[] bedrockItems = new ItemData[translator.size];
|
||||||
for (int i = 0; i < bedrockItems.length; i++) {
|
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();
|
InventoryContentPacket contentPacket = new InventoryContentPacket();
|
||||||
@ -59,7 +60,7 @@ public class ContainerInventoryUpdater extends InventoryUpdater {
|
|||||||
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||||
slotPacket.setContainerId(inventory.getId());
|
slotPacket.setContainerId(inventory.getId());
|
||||||
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
|
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
|
||||||
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
|
slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
|
||||||
session.sendUpstreamPacket(slotPacket);
|
session.sendUpstreamPacket(slotPacket);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -32,16 +32,15 @@ import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
|
|||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public abstract class InventoryUpdater {
|
public class InventoryUpdater {
|
||||||
public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
||||||
ItemData[] bedrockItems = new ItemData[36];
|
ItemData[] bedrockItems = new ItemData[36];
|
||||||
for (int i = 0; i < 36; i++) {
|
for (int i = 0; i < 36; i++) {
|
||||||
final int offset = i < 9 ? 27 : -9;
|
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();
|
InventoryContentPacket contentPacket = new InventoryContentPacket();
|
||||||
contentPacket.setContainerId(ContainerId.INVENTORY);
|
contentPacket.setContainerId(ContainerId.INVENTORY);
|
||||||
@ -54,7 +53,7 @@ public abstract class InventoryUpdater {
|
|||||||
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||||
slotPacket.setContainerId(ContainerId.INVENTORY);
|
slotPacket.setContainerId(ContainerId.INVENTORY);
|
||||||
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
|
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
|
||||||
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
|
slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
|
||||||
session.sendUpstreamPacket(slotPacket);
|
session.sendUpstreamPacket(slotPacket);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -30,11 +30,10 @@ import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
|
|||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
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
|
@Override
|
||||||
public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
||||||
super.updateInventory(translator, session, inventory);
|
super.updateInventory(translator, session, inventory);
|
||||||
@ -46,7 +45,7 @@ public class CursorInventoryUpdater extends InventoryUpdater {
|
|||||||
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||||
slotPacket.setContainerId(ContainerId.UI);
|
slotPacket.setContainerId(ContainerId.UI);
|
||||||
slotPacket.setSlot(bedrockSlot);
|
slotPacket.setSlot(bedrockSlot);
|
||||||
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i)));
|
slotPacket.setItem(inventory.getItem(i).getItemData(session));
|
||||||
session.sendUpstreamPacket(slotPacket);
|
session.sendUpstreamPacket(slotPacket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,7 +58,7 @@ public class CursorInventoryUpdater extends InventoryUpdater {
|
|||||||
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||||
slotPacket.setContainerId(ContainerId.UI);
|
slotPacket.setContainerId(ContainerId.UI);
|
||||||
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
|
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
|
||||||
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
|
slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
|
||||||
session.sendUpstreamPacket(slotPacket);
|
session.sendUpstreamPacket(slotPacket);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
@ -34,7 +34,7 @@ import lombok.ToString;
|
|||||||
@ToString
|
@ToString
|
||||||
public class ItemEntry {
|
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 javaIdentifier;
|
||||||
private final String bedrockIdentifier;
|
private final String bedrockIdentifier;
|
||||||
@ -43,6 +43,7 @@ public class ItemEntry {
|
|||||||
private final int bedrockData;
|
private final int bedrockData;
|
||||||
|
|
||||||
private final boolean block;
|
private final boolean block;
|
||||||
|
private final int stackSize;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
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.
|
* 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",
|
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;
|
public static final ItemData[] CREATIVE_ITEMS;
|
||||||
|
|
||||||
@ -158,6 +158,8 @@ public class ItemRegistry {
|
|||||||
if (bedrockIdentifier == null) {
|
if (bedrockIdentifier == null) {
|
||||||
throw new RuntimeException("Missing Bedrock ID in mappings!: " + bedrockId);
|
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_type")) {
|
||||||
if (entry.getValue().has("tool_tier")) {
|
if (entry.getValue().has("tool_tier")) {
|
||||||
ITEM_ENTRIES.put(itemIndex, new ToolItemEntry(
|
ITEM_ENTRIES.put(itemIndex, new ToolItemEntry(
|
||||||
@ -165,19 +167,22 @@ public class ItemRegistry {
|
|||||||
entry.getValue().get("bedrock_data").intValue(),
|
entry.getValue().get("bedrock_data").intValue(),
|
||||||
entry.getValue().get("tool_type").textValue(),
|
entry.getValue().get("tool_type").textValue(),
|
||||||
entry.getValue().get("tool_tier").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 {
|
} else {
|
||||||
ITEM_ENTRIES.put(itemIndex, new ToolItemEntry(
|
ITEM_ENTRIES.put(itemIndex, new ToolItemEntry(
|
||||||
entry.getKey(), bedrockIdentifier, itemIndex, bedrockId,
|
entry.getKey(), bedrockIdentifier, itemIndex, bedrockId,
|
||||||
entry.getValue().get("bedrock_data").intValue(),
|
entry.getValue().get("bedrock_data").intValue(),
|
||||||
entry.getValue().get("tool_type").textValue(),
|
entry.getValue().get("tool_type").textValue(),
|
||||||
"", entry.getValue().get("is_block").booleanValue()));
|
"", entry.getValue().get("is_block").booleanValue(),
|
||||||
|
stackSize));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ITEM_ENTRIES.put(itemIndex, new ItemEntry(
|
ITEM_ENTRIES.put(itemIndex, new ItemEntry(
|
||||||
entry.getKey(), bedrockIdentifier, itemIndex, bedrockId,
|
entry.getKey(), bedrockIdentifier, itemIndex, bedrockId,
|
||||||
entry.getValue().get("bedrock_data").intValue(),
|
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()) {
|
switch (entry.getKey()) {
|
||||||
case "minecraft:barrier":
|
case "minecraft:barrier":
|
||||||
@ -225,7 +230,7 @@ public class ItemRegistry {
|
|||||||
|
|
||||||
// Add the loadstone compass since it doesn't exist on java but we need it for item conversion
|
// 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,
|
ITEM_ENTRIES.put(itemIndex, new ItemEntry("minecraft:lodestone_compass", "minecraft:lodestone_compass", itemIndex,
|
||||||
lodestoneCompassId, 0, false));
|
lodestoneCompassId, 0, false, 1));
|
||||||
|
|
||||||
/* Load creative items */
|
/* Load creative items */
|
||||||
stream = FileUtils.getResource("bedrock/creative_items.json");
|
stream = FileUtils.getResource("bedrock/creative_items.json");
|
||||||
|
@ -124,8 +124,12 @@ public abstract class ItemTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ItemEntry bedrockItem = ItemRegistry.getItem(stack);
|
ItemEntry bedrockItem = ItemRegistry.getItem(stack);
|
||||||
|
if (bedrockItem == null) {
|
||||||
|
session.getConnector().getLogger().debug("No matching ItemEntry for " + stack);
|
||||||
|
return ItemData.AIR;
|
||||||
|
}
|
||||||
|
|
||||||
com.github.steveice10.opennbt.tag.builtin.CompoundTag nbt = stack.getNbt() != null ? stack.getNbt().clone() : null;
|
CompoundTag nbt = stack.getNbt() != null ? stack.getNbt().clone() : null;
|
||||||
|
|
||||||
// This is a fallback for maps with no nbt
|
// This is a fallback for maps with no nbt
|
||||||
if (nbt == null && bedrockItem.getJavaIdentifier().equals("minecraft:filled_map")) {
|
if (nbt == null && bedrockItem.getJavaIdentifier().equals("minecraft:filled_map")) {
|
||||||
|
@ -26,13 +26,25 @@
|
|||||||
package org.geysermc.connector.network.translators.item;
|
package org.geysermc.connector.network.translators.item;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
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.CraftingData;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
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 it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.utils.FileUtils;
|
import org.geysermc.connector.utils.FileUtils;
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
import org.geysermc.connector.utils.LanguageUtils;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@ -47,6 +59,12 @@ public class RecipeRegistry {
|
|||||||
*/
|
*/
|
||||||
public static int LAST_RECIPE_NET_ID = 0;
|
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.
|
* A list of all possible leather armor dyeing recipes.
|
||||||
* Created manually.
|
* Created manually.
|
||||||
@ -78,6 +96,12 @@ public class RecipeRegistry {
|
|||||||
*/
|
*/
|
||||||
public static final List<CraftingData> TIPPED_ARROW_RECIPES = new ObjectArrayList<>();
|
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
|
* 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_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++);
|
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++);
|
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
|
// 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
|
// 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);
|
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
|
// 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
|
// But it does display the correct output from a base leather armor, and besides visuals everything works fine
|
||||||
LEATHER_DYEING_RECIPES.add(getCraftingDataFromJsonNode(entry));
|
LEATHER_DYEING_RECIPES.add(getCraftingDataFromJsonNode(entry));
|
||||||
@ -146,9 +175,13 @@ public class RecipeRegistry {
|
|||||||
* @return the {@link CraftingData} to send to the Bedrock client.
|
* @return the {@link CraftingData} to send to the Bedrock client.
|
||||||
*/
|
*/
|
||||||
private static CraftingData getCraftingDataFromJsonNode(JsonNode node) {
|
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();
|
UUID uuid = UUID.randomUUID();
|
||||||
if (node.get("type").asInt() == 1) {
|
if (type == 1) {
|
||||||
// Shaped recipe
|
// Shaped recipe
|
||||||
List<String> shape = new ArrayList<>();
|
List<String> shape = new ArrayList<>();
|
||||||
// Get the shape of the recipe
|
// Get the shape of the recipe
|
||||||
@ -158,10 +191,12 @@ public class RecipeRegistry {
|
|||||||
|
|
||||||
// In recipes.json each recipe is mapped by a letter
|
// In recipes.json each recipe is mapped by a letter
|
||||||
Map<String, ItemData> letterToRecipe = new HashMap<>();
|
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()) {
|
while (iterator.hasNext()) {
|
||||||
Map.Entry<String, JsonNode> entry = iterator.next();
|
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());
|
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(),
|
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<>();
|
List<ItemData> inputs = new ObjectArrayList<>();
|
||||||
for (JsonNode entry : node.get("input")) {
|
for (JsonNode entry : node.get("inputs")) {
|
||||||
inputs.add(ItemRegistry.getBedrockItemFromJson(entry));
|
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
|
// Shulker box
|
||||||
return CraftingData.fromShulkerBox(uuid.toString(),
|
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(),
|
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() {
|
public static void init() {
|
||||||
|
@ -32,8 +32,8 @@ public class ToolItemEntry extends ItemEntry {
|
|||||||
private final String toolType;
|
private final String toolType;
|
||||||
private final String toolTier;
|
private final String toolTier;
|
||||||
|
|
||||||
public ToolItemEntry(String javaIdentifier, String bedrockIdentifier, int javaId, int bedrockId, int bedrockData, String toolType, String toolTier, boolean 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);
|
super(javaIdentifier, bedrockIdentifier, javaId, bedrockId, bedrockData, isBlock, stackSize);
|
||||||
this.toolType = toolType;
|
this.toolType = toolType;
|
||||||
this.toolTier = toolTier;
|
this.toolTier = toolTier;
|
||||||
}
|
}
|
||||||
|
@ -195,13 +195,14 @@ public class BannerTranslator extends ItemTranslator {
|
|||||||
blockEntityTag.put(OMINOUS_BANNER_PATTERN);
|
blockEntityTag.put(OMINOUS_BANNER_PATTERN);
|
||||||
|
|
||||||
itemStack.getNbt().put(blockEntityTag);
|
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);
|
List<NbtMap> patterns = nbtTag.getList("Patterns", NbtType.COMPOUND);
|
||||||
|
|
||||||
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
|
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
|
||||||
blockEntityTag.put(convertBannerPattern(patterns));
|
blockEntityTag.put(convertBannerPattern(patterns));
|
||||||
|
|
||||||
itemStack.getNbt().put(blockEntityTag);
|
itemStack.getNbt().put(blockEntityTag);
|
||||||
|
itemStack.getNbt().remove("Patterns"); // Remove the old Bedrock patterns list
|
||||||
}
|
}
|
||||||
|
|
||||||
return itemStack;
|
return itemStack;
|
||||||
|
@ -25,17 +25,18 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.network.translators.java;
|
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.Ingredient;
|
||||||
import com.github.steveice10.mc.protocol.data.game.recipe.Recipe;
|
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.ShapedRecipeData;
|
||||||
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData;
|
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.github.steveice10.mc.protocol.packet.ingame.server.ServerDeclareRecipesPacket;
|
||||||
import com.nukkitx.nbt.NbtMap;
|
import com.nukkitx.nbt.NbtMap;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.CraftingData;
|
import com.nukkitx.protocol.bedrock.data.inventory.CraftingData;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||||
import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
|
import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.*;
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
@ -46,13 +47,20 @@ import org.geysermc.connector.network.translators.item.*;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
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)
|
@Translator(packet = ServerDeclareRecipesPacket.class)
|
||||||
public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclareRecipesPacket> {
|
public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclareRecipesPacket> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerDeclareRecipesPacket packet, GeyserSession session) {
|
public void translate(ServerDeclareRecipesPacket packet, GeyserSession session) {
|
||||||
// Get the last known network ID (first used for the pregenerated recipes) and increment from there.
|
// 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 craftingDataPacket = new CraftingDataPacket();
|
||||||
craftingDataPacket.setCleanRecipes(true);
|
craftingDataPacket.setCleanRecipes(true);
|
||||||
for (Recipe recipe : packet.getRecipes()) {
|
for (Recipe recipe : packet.getRecipes()) {
|
||||||
@ -60,25 +68,29 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
|
|||||||
case CRAFTING_SHAPELESS: {
|
case CRAFTING_SHAPELESS: {
|
||||||
ShapelessRecipeData shapelessRecipeData = (ShapelessRecipeData) recipe.getData();
|
ShapelessRecipeData shapelessRecipeData = (ShapelessRecipeData) recipe.getData();
|
||||||
ItemData output = ItemTranslator.translateToBedrock(session, shapelessRecipeData.getResult());
|
ItemData output = ItemTranslator.translateToBedrock(session, shapelessRecipeData.getResult());
|
||||||
output = ItemData.of(output.getId(), output.getDamage(), output.getCount()); //strip NBT
|
// Strip NBT - tools won't appear in the recipe book otherwise
|
||||||
|
output = ItemData.of(output.getId(), output.getDamage(), output.getCount());
|
||||||
ItemData[][] inputCombinations = combinations(session, shapelessRecipeData.getIngredients());
|
ItemData[][] inputCombinations = combinations(session, shapelessRecipeData.getIngredients());
|
||||||
for (ItemData[] inputs : inputCombinations) {
|
for (ItemData[] inputs : inputCombinations) {
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case CRAFTING_SHAPED: {
|
case CRAFTING_SHAPED: {
|
||||||
ShapedRecipeData shapedRecipeData = (ShapedRecipeData) recipe.getData();
|
ShapedRecipeData shapedRecipeData = (ShapedRecipeData) recipe.getData();
|
||||||
ItemData output = ItemTranslator.translateToBedrock(session, shapedRecipeData.getResult());
|
ItemData output = ItemTranslator.translateToBedrock(session, shapedRecipeData.getResult());
|
||||||
output = ItemData.of(output.getId(), output.getDamage(), output.getCount()); //strip NBT
|
// See above
|
||||||
|
output = ItemData.of(output.getId(), output.getDamage(), output.getCount());
|
||||||
ItemData[][] inputCombinations = combinations(session, shapedRecipeData.getIngredients());
|
ItemData[][] inputCombinations = combinations(session, shapedRecipeData.getIngredients());
|
||||||
for (ItemData[] inputs : inputCombinations) {
|
for (ItemData[] inputs : inputCombinations) {
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
craftingDataPacket.getCraftingData().add(CraftingData.fromShaped(uuid.toString(),
|
craftingDataPacket.getCraftingData().add(CraftingData.fromShaped(uuid.toString(),
|
||||||
shapedRecipeData.getWidth(), shapedRecipeData.getHeight(), Arrays.asList(inputs),
|
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;
|
break;
|
||||||
}
|
}
|
||||||
@ -131,13 +143,68 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
|
|||||||
craftingDataPacket.getCraftingData().addAll(RecipeRegistry.LEATHER_DYEING_RECIPES);
|
craftingDataPacket.getCraftingData().addAll(RecipeRegistry.LEATHER_DYEING_RECIPES);
|
||||||
break;
|
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);
|
craftingDataPacket.getPotionMixData().addAll(PotionMixRegistry.POTION_MIXES);
|
||||||
|
|
||||||
|
Int2ObjectMap<IntList> stonecutterRecipeMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
for (Int2ObjectMap.Entry<List<StoneCuttingRecipeData>> data : unsortedStonecutterData.int2ObjectEntrySet()) {
|
||||||
|
// 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
|
||||||
|
data.getValue().sort(Comparator.comparing((stoneCuttingRecipeData ->
|
||||||
|
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.sendUpstreamPacket(craftingDataPacket);
|
||||||
|
session.setCraftingRecipes(recipeMap);
|
||||||
|
session.getUnlockedRecipes().clear();
|
||||||
|
session.setStonecutterRecipes(stonecutterRecipeMap);
|
||||||
|
session.getLastRecipeNetId().set(netId);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: rewrite
|
//TODO: rewrite
|
||||||
|
/**
|
||||||
|
* The Java server sends an array of items for each ingredient you can use per slot in the crafting grid.
|
||||||
|
* Bedrock recipes take only one ingredient per crafting grid slot.
|
||||||
|
*
|
||||||
|
* @return the Java ingredient list as an array that Bedrock can understand
|
||||||
|
*/
|
||||||
private ItemData[][] combinations(GeyserSession session, Ingredient[] ingredients) {
|
private ItemData[][] combinations(GeyserSession session, Ingredient[] ingredients) {
|
||||||
Map<Set<ItemData>, IntSet> squashedOptions = new HashMap<>();
|
Map<Set<ItemData>, IntSet> squashedOptions = new HashMap<>();
|
||||||
for (int i = 0; i < ingredients.length; i++) {
|
for (int i = 0; i < ingredients.length; i++) {
|
||||||
|
@ -35,6 +35,7 @@ import org.geysermc.connector.entity.attribute.AttributeType;
|
|||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||||
import org.geysermc.connector.utils.DimensionUtils;
|
import org.geysermc.connector.utils.DimensionUtils;
|
||||||
|
|
||||||
@Translator(packet = ServerRespawnPacket.class)
|
@Translator(packet = ServerRespawnPacket.class)
|
||||||
@ -48,7 +49,11 @@ public class JavaRespawnTranslator extends PacketTranslator<ServerRespawnPacket>
|
|||||||
// Max health must be divisible by two in bedrock
|
// Max health must be divisible by two in bedrock
|
||||||
entity.getAttributes().put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(maxHealth, (maxHealth % 2 == 1 ? maxHealth + 1 : maxHealth)));
|
entity.getAttributes().put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(maxHealth, (maxHealth % 2 == 1 ? maxHealth + 1 : maxHealth)));
|
||||||
|
|
||||||
session.getInventoryCache().setOpenInventory(null);
|
session.addInventoryTask(() -> {
|
||||||
|
session.setInventoryTranslator(InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR);
|
||||||
|
session.setOpenInventory(null);
|
||||||
|
session.setClosingInventory(false);
|
||||||
|
});
|
||||||
|
|
||||||
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
|
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
|
||||||
playerGameTypePacket.setGamemode(packet.getGamemode().ordinal());
|
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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,6 +33,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
|||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
||||||
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
||||||
|
import org.geysermc.connector.inventory.GeyserItemStack;
|
||||||
import org.geysermc.connector.inventory.PlayerInventory;
|
import org.geysermc.connector.inventory.PlayerInventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
@ -71,12 +72,12 @@ public class JavaPlayerActionAckTranslator extends PacketTranslator<ServerPlayer
|
|||||||
packet.getPosition().getY(),
|
packet.getPosition().getY(),
|
||||||
packet.getPosition().getZ()
|
packet.getPosition().getZ()
|
||||||
));
|
));
|
||||||
PlayerInventory inventory = session.getInventory();
|
PlayerInventory inventory = session.getPlayerInventory();
|
||||||
ItemStack item = inventory.getItemInHand();
|
GeyserItemStack item = inventory.getItemInHand();
|
||||||
ItemEntry itemEntry = null;
|
ItemEntry itemEntry = null;
|
||||||
CompoundTag nbtData = new CompoundTag("");
|
CompoundTag nbtData = new CompoundTag("");
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
itemEntry = ItemRegistry.getItem(item);
|
itemEntry = item.getItemEntry();
|
||||||
nbtData = item.getNbt();
|
nbtData = item.getNbt();
|
||||||
}
|
}
|
||||||
double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, packet.getNewState(), itemEntry, nbtData, session) * 20);
|
double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, packet.getNewState(), itemEntry, nbtData, session) * 20);
|
||||||
|
@ -36,12 +36,14 @@ public class JavaPlayerChangeHeldItemTranslator extends PacketTranslator<ServerP
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerPlayerChangeHeldItemPacket packet, GeyserSession session) {
|
public void translate(ServerPlayerChangeHeldItemPacket packet, GeyserSession session) {
|
||||||
PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket();
|
session.addInventoryTask(() -> {
|
||||||
hotbarPacket.setContainerId(0);
|
PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket();
|
||||||
hotbarPacket.setSelectedHotbarSlot(packet.getSlot());
|
hotbarPacket.setContainerId(0);
|
||||||
hotbarPacket.setSelectHotbarSlot(true);
|
hotbarPacket.setSelectedHotbarSlot(packet.getSlot());
|
||||||
session.sendUpstreamPacket(hotbarPacket);
|
hotbarPacket.setSelectHotbarSlot(true);
|
||||||
|
session.sendUpstreamPacket(hotbarPacket);
|
||||||
|
|
||||||
session.getInventory().setHeldItemSlot(packet.getSlot());
|
session.getPlayerInventory().setHeldItemSlot(packet.getSlot());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,9 @@ public class JavaCloseWindowTranslator extends PacketTranslator<ServerCloseWindo
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerCloseWindowPacket packet, GeyserSession session) {
|
public void translate(ServerCloseWindowPacket packet, GeyserSession session) {
|
||||||
InventoryUtils.closeWindow(session, packet.getWindowId());
|
session.addInventoryTask(() ->
|
||||||
InventoryUtils.closeInventory(session, packet.getWindowId());
|
// Sometimes the server can request a window close of ID 0... when the window isn't even open
|
||||||
|
// Don't confirm in this instance
|
||||||
|
InventoryUtils.closeInventory(session, packet.getWindowId(), (session.getOpenInventory() != null && session.getOpenInventory().getId() == 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.client.window.ClientConfirmTransactionPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerConfirmTransactionPacket;
|
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.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
@ -36,9 +37,11 @@ public class JavaConfirmTransactionTranslator extends PacketTranslator<ServerCon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerConfirmTransactionPacket packet, GeyserSession session) {
|
public void translate(ServerConfirmTransactionPacket packet, GeyserSession session) {
|
||||||
if (!packet.isAccepted()) {
|
session.addInventoryTask(() -> {
|
||||||
ClientConfirmTransactionPacket confirmPacket = new ClientConfirmTransactionPacket(packet.getWindowId(), packet.getActionId(), true);
|
if (!packet.isAccepted()) {
|
||||||
session.sendDownstreamPacket(confirmPacket);
|
ClientConfirmTransactionPacket confirmPacket = new ClientConfirmTransactionPacket(packet.getWindowId(), packet.getActionId(), true);
|
||||||
}
|
session.sendDownstreamPacket(confirmPacket);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* 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<>(4);
|
||||||
|
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) {
|
||||||
|
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());
|
||||||
|
session.sendUpstreamPacket(updateEquipPacket);
|
||||||
|
|
||||||
|
session.setInventoryTranslator(inventoryTranslator);
|
||||||
|
InventoryUtils.openInventory(session, new Container(entity.getMetadata().getString(EntityData.NAMETAG), packet.getWindowId(), packet.getNumberOfSlots(), null, session.getPlayerInventory()));
|
||||||
|
}
|
||||||
|
}
|
@ -41,35 +41,38 @@ public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowP
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerOpenWindowPacket packet, GeyserSession session) {
|
public void translate(ServerOpenWindowPacket packet, GeyserSession session) {
|
||||||
if (packet.getWindowId() == 0) {
|
session.addInventoryTask(() -> {
|
||||||
return;
|
if (packet.getWindowId() == 0) {
|
||||||
}
|
return;
|
||||||
InventoryTranslator newTranslator = InventoryTranslator.INVENTORY_TRANSLATORS.get(packet.getType());
|
}
|
||||||
Inventory openInventory = session.getInventoryCache().getOpenInventory();
|
|
||||||
if (newTranslator == null) {
|
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(), true);
|
||||||
|
}
|
||||||
|
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) {
|
if (openInventory != null) {
|
||||||
InventoryUtils.closeWindow(session, openInventory.getId());
|
// If the window type is the same, don't close.
|
||||||
InventoryUtils.closeInventory(session, openInventory.getId());
|
// In rare cases, inventories can do funny things where it keeps the same window type up but change the contents.
|
||||||
|
if (openInventory.getWindowType() != packet.getType()) {
|
||||||
|
// Sometimes the server can double-open an inventory with the same ID - don't confirm in that instance.
|
||||||
|
InventoryUtils.closeInventory(session, openInventory.getId(), openInventory.getId() != packet.getWindowId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId());
|
|
||||||
session.sendDownstreamPacket(closeWindowPacket);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String name = MessageTranslator.convertMessageLenient(packet.getName(), session.getLocale());
|
session.setInventoryTranslator(newTranslator);
|
||||||
|
InventoryUtils.openInventory(session, newInventory);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,36 +25,253 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.network.translators.java.window;
|
package org.geysermc.connector.network.translators.java.window;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
||||||
|
import com.github.steveice10.mc.protocol.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.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.CraftingData;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
|
||||||
|
import org.geysermc.connector.inventory.GeyserItemStack;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.translators.CraftingInventoryTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.translators.PlayerInventoryTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||||
import org.geysermc.connector.utils.InventoryUtils;
|
import org.geysermc.connector.utils.InventoryUtils;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Translator(packet = ServerSetSlotPacket.class)
|
@Translator(packet = ServerSetSlotPacket.class)
|
||||||
public class JavaSetSlotTranslator extends PacketTranslator<ServerSetSlotPacket> {
|
public class JavaSetSlotTranslator extends PacketTranslator<ServerSetSlotPacket> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerSetSlotPacket packet, GeyserSession session) {
|
public void translate(ServerSetSlotPacket packet, GeyserSession session) {
|
||||||
if (packet.getWindowId() == 255 && packet.getSlot() == -1) { //cursor
|
session.addInventoryTask(() -> {
|
||||||
if (session.getCraftSlot() != 0)
|
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;
|
return;
|
||||||
|
|
||||||
session.getInventory().setCursor(packet.getItem());
|
InventoryTranslator translator = session.getInventoryTranslator();
|
||||||
InventoryUtils.updateCursor(session);
|
if (translator != null) {
|
||||||
return;
|
if (session.getCraftingGridFuture() != null) {
|
||||||
}
|
session.getCraftingGridFuture().cancel(false);
|
||||||
|
}
|
||||||
|
session.setCraftingGridFuture(session.getConnector().getGeneralThreadPool().schedule(() -> session.addInventoryTask(() -> updateCraftingGrid(session, packet, inventory, translator)), 150, TimeUnit.MILLISECONDS));
|
||||||
|
|
||||||
Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId());
|
GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
|
||||||
if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null))
|
if (packet.getWindowId() == 0 && !(translator instanceof PlayerInventoryTranslator)) {
|
||||||
return;
|
// In rare cases, the window ID can still be 0 but Java treats it as valid
|
||||||
|
session.getPlayerInventory().setItem(packet.getSlot(), newItem, session);
|
||||||
|
InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, session.getPlayerInventory(), packet.getSlot());
|
||||||
|
} else {
|
||||||
|
inventory.setItem(packet.getSlot(), newItem, session);
|
||||||
|
translator.updateSlot(session, inventory, packet.getSlot());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType());
|
private static void updateCraftingGrid(GeyserSession session, ServerSetSlotPacket packet, Inventory inventory, InventoryTranslator translator) {
|
||||||
if (translator != null) {
|
if (packet.getSlot() == 0) {
|
||||||
inventory.setItem(packet.getSlot(), packet.getItem());
|
int gridSize;
|
||||||
translator.updateSlot(session, inventory, packet.getSlot());
|
if (translator instanceof PlayerInventoryTranslator) {
|
||||||
|
gridSize = 4;
|
||||||
|
} else if (translator instanceof CraftingInventoryTranslator) {
|
||||||
|
gridSize = 9;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet.getItem() == null || packet.getItem().getId() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = gridSize == 4 ? 28 : 32;
|
||||||
|
int gridDimensions = gridSize == 4 ? 2 : 3;
|
||||||
|
int firstRow = -1, height = -1;
|
||||||
|
int firstCol = -1, width = -1;
|
||||||
|
for (int row = 0; row < gridDimensions; row++) {
|
||||||
|
for (int col = 0; col < gridDimensions; col++) {
|
||||||
|
if (!inventory.getItem(col + (row * gridDimensions) + 1).isEmpty()) {
|
||||||
|
if (firstRow == -1) {
|
||||||
|
firstRow = row;
|
||||||
|
firstCol = col;
|
||||||
|
} else {
|
||||||
|
firstCol = Math.min(firstCol, col);
|
||||||
|
}
|
||||||
|
height = Math.max(height, row);
|
||||||
|
width = Math.max(width, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//empty grid
|
||||||
|
if (firstRow == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
height += -firstRow + 1;
|
||||||
|
width += -firstCol + 1;
|
||||||
|
|
||||||
|
recipes:
|
||||||
|
for (Recipe recipe : session.getCraftingRecipes().values()) {
|
||||||
|
if (recipe.getType() == RecipeType.CRAFTING_SHAPED) {
|
||||||
|
ShapedRecipeData data = (ShapedRecipeData) recipe.getData();
|
||||||
|
if (!data.getResult().equals(packet.getItem())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (data.getWidth() != width || data.getHeight() != height || width * height != data.getIngredients().length) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ingredient[] ingredients = data.getIngredients();
|
||||||
|
if (!testShapedRecipe(ingredients, inventory, gridDimensions, firstRow, height, firstCol, width)) {
|
||||||
|
Ingredient[] mirroredIngredients = new Ingredient[data.getIngredients().length];
|
||||||
|
for (int row = 0; row < height; row++) {
|
||||||
|
for (int col = 0; col < width; col++) {
|
||||||
|
mirroredIngredients[col + (row * width)] = ingredients[(width - 1 - col) + (row * width)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Arrays.equals(ingredients, mirroredIngredients) ||
|
||||||
|
!testShapedRecipe(mirroredIngredients, inventory, gridDimensions, firstRow, height, firstCol, width)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Recipe is had, don't sent packet
|
||||||
|
return;
|
||||||
|
} else if (recipe.getType() == RecipeType.CRAFTING_SHAPELESS) {
|
||||||
|
ShapelessRecipeData data = (ShapelessRecipeData) recipe.getData();
|
||||||
|
if (!data.getResult().equals(packet.getItem())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < data.getIngredients().length; i++) {
|
||||||
|
Ingredient ingredient = data.getIngredients()[i];
|
||||||
|
for (ItemStack itemStack : ingredient.getOptions()) {
|
||||||
|
boolean inventoryHasItem = false;
|
||||||
|
for (int j = 0; j < inventory.getSize(); j++) {
|
||||||
|
GeyserItemStack geyserItemStack = inventory.getItem(j);
|
||||||
|
if (geyserItemStack.isEmpty()) {
|
||||||
|
inventoryHasItem = itemStack == null || itemStack.getId() == 0;
|
||||||
|
if (inventoryHasItem) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (itemStack.equals(geyserItemStack.getItemStack(1))) {
|
||||||
|
inventoryHasItem = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!inventoryHasItem) {
|
||||||
|
continue recipes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Recipe is had, don't sent packet
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
int newRecipeId = session.getLastRecipeNetId().incrementAndGet();
|
||||||
|
|
||||||
|
ItemData[] ingredients = new ItemData[height * width];
|
||||||
|
//construct ingredient list and clear slots on client
|
||||||
|
Ingredient[] javaIngredients = new Ingredient[height * width];
|
||||||
|
int index = 0;
|
||||||
|
for (int row = firstRow; row < height + firstRow; row++) {
|
||||||
|
for (int col = firstCol; col < width + firstCol; col++) {
|
||||||
|
GeyserItemStack geyserItemStack = inventory.getItem(col + (row * gridDimensions) + 1);
|
||||||
|
ingredients[index] = geyserItemStack.getItemData(session);
|
||||||
|
ItemStack[] itemStacks = new ItemStack[] {geyserItemStack.isEmpty() ? null : geyserItemStack.getItemStack(1)};
|
||||||
|
javaIngredients[index] = new Ingredient(itemStacks);
|
||||||
|
|
||||||
|
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||||
|
slotPacket.setContainerId(ContainerId.UI);
|
||||||
|
slotPacket.setSlot(col + (row * gridDimensions) + offset);
|
||||||
|
slotPacket.setItem(ItemData.AIR);
|
||||||
|
session.sendUpstreamPacket(slotPacket);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShapedRecipeData data = new ShapedRecipeData(width, height, "", javaIngredients, packet.getItem());
|
||||||
|
// Cache this recipe so we know the client has received it
|
||||||
|
session.getCraftingRecipes().put(newRecipeId, new Recipe(RecipeType.CRAFTING_SHAPED, uuid.toString(), data));
|
||||||
|
|
||||||
|
CraftingDataPacket craftPacket = new CraftingDataPacket();
|
||||||
|
craftPacket.getCraftingData().add(CraftingData.fromShaped(
|
||||||
|
uuid.toString(),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
Arrays.asList(ingredients),
|
||||||
|
Collections.singletonList(ItemTranslator.translateToBedrock(session, packet.getItem())),
|
||||||
|
uuid,
|
||||||
|
"crafting_table",
|
||||||
|
0,
|
||||||
|
newRecipeId
|
||||||
|
));
|
||||||
|
craftPacket.setCleanRecipes(false);
|
||||||
|
session.sendUpstreamPacket(craftPacket);
|
||||||
|
|
||||||
|
index = 0;
|
||||||
|
for (int row = firstRow; row < height + firstRow; row++) {
|
||||||
|
for (int col = firstCol; col < width + firstCol; col++) {
|
||||||
|
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||||
|
slotPacket.setContainerId(ContainerId.UI);
|
||||||
|
slotPacket.setSlot(col + (row * gridDimensions) + offset);
|
||||||
|
slotPacket.setItem(ingredients[index]);
|
||||||
|
session.sendUpstreamPacket(slotPacket);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean testShapedRecipe(Ingredient[] ingredients, Inventory inventory, int gridDimensions, int firstRow, int height, int firstCol, int width) {
|
||||||
|
int ingredientIndex = 0;
|
||||||
|
for (int row = firstRow; row < height + firstRow; row++) {
|
||||||
|
for (int col = firstCol; col < width + firstCol; col++) {
|
||||||
|
GeyserItemStack geyserItemStack = inventory.getItem(col + (row * gridDimensions) + 1);
|
||||||
|
Ingredient ingredient = ingredients[ingredientIndex++];
|
||||||
|
if (ingredient.getOptions().length == 0) {
|
||||||
|
if (!geyserItemStack.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
boolean inventoryHasItem = false;
|
||||||
|
for (ItemStack item : ingredient.getOptions()) {
|
||||||
|
if (Objects.equals(geyserItemStack.getItemStack(1), item)) {
|
||||||
|
inventoryHasItem = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!inventoryHasItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,32 +26,33 @@
|
|||||||
package org.geysermc.connector.network.translators.java.window;
|
package org.geysermc.connector.network.translators.java.window;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket;
|
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.inventory.Inventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||||
|
import org.geysermc.connector.utils.InventoryUtils;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
@Translator(packet = ServerWindowItemsPacket.class)
|
@Translator(packet = ServerWindowItemsPacket.class)
|
||||||
public class JavaWindowItemsTranslator extends PacketTranslator<ServerWindowItemsPacket> {
|
public class JavaWindowItemsTranslator extends PacketTranslator<ServerWindowItemsPacket> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerWindowItemsPacket packet, GeyserSession session) {
|
public void translate(ServerWindowItemsPacket packet, GeyserSession session) {
|
||||||
Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId());
|
session.addInventoryTask(() -> {
|
||||||
if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null))
|
Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
|
||||||
return;
|
if (inventory == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (packet.getItems().length < inventory.getSize()) {
|
for (int i = 0; i < packet.getItems().length; i++) {
|
||||||
inventory.setItems(Arrays.copyOf(packet.getItems(), inventory.getSize()));
|
GeyserItemStack newItem = GeyserItemStack.from(packet.getItems()[i]);
|
||||||
} else {
|
inventory.setItem(i, newItem, session);
|
||||||
inventory.setItems(packet.getItems());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType());
|
InventoryTranslator translator = session.getInventoryTranslator();
|
||||||
if (translator != null) {
|
if (translator != null) {
|
||||||
translator.updateInventory(session, inventory);
|
translator.updateInventory(session, inventory);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren