diff --git a/connector/pom.xml b/connector/pom.xml index 6d56fbcad..a5cf6f292 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -48,6 +48,18 @@ <version>8.1.1</version> <scope>compile</scope> </dependency> + <dependency> + <groupId>com.nukkitx.fastutil</groupId> + <artifactId>fastutil-int-double-maps</artifactId> + <version>8.3.1</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>com.nukkitx.fastutil</groupId> + <artifactId>fastutil-int-boolean-maps</artifactId> + <version>8.3.1</version> + <scope>compile</scope> + </dependency> <dependency> <groupId>com.github.steveice10</groupId> <artifactId>opennbt</artifactId> diff --git a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java index a11ce856b..52fb786bc 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java @@ -50,6 +50,6 @@ public class PlayerInventory extends Inventory { } public ItemStack getItemInHand() { - return items[heldItemSlot]; + return items[36 + heldItemSlot]; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java index 18ac3e6dc..330987615 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java @@ -52,6 +52,13 @@ public class BlockTranslator { private static final IntSet WATERLOGGED = new IntOpenHashSet(); private static final Map<BlockState, String> JAVA_ID_TO_BLOCK_ENTITY_MAP = new HashMap<>(); + public static final Int2DoubleMap JAVA_RUNTIME_ID_TO_HARDNESS = new Int2DoubleOpenHashMap(); + public static final Int2BooleanMap JAVA_RUNTIME_ID_TO_CAN_BREAK_WITH_HAND = new Int2BooleanOpenHashMap(); + public static final Int2ObjectMap<String> JAVA_RUNTIME_ID_TO_TOOL_TYPE = new Int2ObjectOpenHashMap<>(); + + // For block breaking animation math + public static final List<Integer> JAVA_RUNTIME_WOOL_IDS = new ArrayList<>(); + public static final int JAVA_RUNTIME_COBWEB_ID; private static final int BLOCK_STATE_VERSION = 17760256; @@ -87,6 +94,7 @@ public class BlockTranslator { int waterRuntimeId = -1; int javaRuntimeId = -1; int bedrockRuntimeId = 0; + int cobwebRuntimeId = -1; Iterator<Map.Entry<String, JsonNode>> blocksIterator = blocks.fields(); while (blocksIterator.hasNext()) { javaRuntimeId++; @@ -95,6 +103,28 @@ public class BlockTranslator { BlockState javaBlockState = new BlockState(javaRuntimeId); CompoundTag blockTag = buildBedrockState(entry.getValue()); + // TODO fix this, (no block should have a null hardness) + JsonNode hardnessNode = entry.getValue().get("block_hardness"); + if (hardnessNode != null) { + JAVA_RUNTIME_ID_TO_HARDNESS.put(javaRuntimeId, hardnessNode.doubleValue()); + } + + JAVA_RUNTIME_ID_TO_CAN_BREAK_WITH_HAND.put(javaRuntimeId, entry.getValue().get("can_break_with_hand").booleanValue()); + + JsonNode toolTypeNode = entry.getValue().get("tool_type"); + if (toolTypeNode != null) { + JAVA_RUNTIME_ID_TO_TOOL_TYPE.put(javaRuntimeId, toolTypeNode.textValue()); + } + + if (javaId.contains("wool")) { + JAVA_RUNTIME_WOOL_IDS.add(javaRuntimeId); + } + + + if (javaId.contains("cobweb")) { + cobwebRuntimeId = javaRuntimeId; + } + JAVA_ID_BLOCK_MAP.put(javaId, javaBlockState); if (javaId.contains("sign[")) { @@ -131,6 +161,11 @@ public class BlockTranslator { bedrockRuntimeId++; } + if (cobwebRuntimeId == -1) { + throw new AssertionError("Unable to find cobwebs in palette"); + } + JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId; + if (waterRuntimeId == -1) { throw new AssertionError("Unable to find water in palette"); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java index fd4f0b020..b5a9eb42c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java @@ -32,7 +32,7 @@ import lombok.Getter; @AllArgsConstructor public class ItemEntry { - public static ItemEntry AIR = new ItemEntry("minecraft:air", 0, 0, 0); + public static ItemEntry AIR = new ItemEntry("minecraft:air", 0, 0, 0, "none", "none"); private String javaIdentifier; private int javaId; @@ -40,6 +40,9 @@ public class ItemEntry { private int bedrockId; private int bedrockData; + private String toolType; + private String toolTier; + @Override public boolean equals(Object obj) { return obj == this || (obj instanceof ItemEntry && ((ItemEntry) obj).getBedrockId() == this.getBedrockId() && ((ItemEntry) obj).getJavaIdentifier().equals(this.getJavaIdentifier())); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java index 451081a1a..34b1646ad 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java @@ -25,9 +25,17 @@ package org.geysermc.connector.network.translators.java.entity.player; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerActionAckPacket; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.LevelEventType; +import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; +import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.TranslatorsInit; +import org.geysermc.connector.network.translators.block.BlockTranslator; +import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.utils.ChunkUtils; public class JavaPlayerActionAckTranslator extends PacketTranslator<ServerPlayerActionAckPacket> { @@ -38,6 +46,142 @@ public class JavaPlayerActionAckTranslator extends PacketTranslator<ServerPlayer case FINISH_DIGGING: ChunkUtils.updateBlock(session, packet.getNewState(), packet.getPosition()); break; + + case START_DIGGING: { + LevelEventPacket levelEvent = new LevelEventPacket(); + levelEvent.setType(LevelEventType.BLOCK_START_BREAK); + levelEvent.setPosition(Vector3f.from( + packet.getPosition().getX(), + packet.getPosition().getY(), + packet.getPosition().getZ() + )); + double blockHardness = BlockTranslator.JAVA_RUNTIME_ID_TO_HARDNESS.get(packet.getNewState().getId()); + + PlayerInventory inventory = session.getInventory(); + ItemStack item = inventory.getItemInHand(); + ItemEntry itemEntry = null; + if (item != null) { + itemEntry = TranslatorsInit.getItemTranslator().getItem(item); + } + double breakTime = Math.ceil(getBreakTime(blockHardness, packet.getNewState().getId(), itemEntry) * 20); + System.out.println("breakTime = " + breakTime); + int data = (int) (65535 / breakTime); + System.out.println("data = " + data); + levelEvent.setData((int) (65535 / breakTime)); + session.getUpstream().sendPacket(levelEvent); + break; + } + + case CANCEL_DIGGING: { + LevelEventPacket levelEvent = new LevelEventPacket(); + levelEvent.setType(LevelEventType.BLOCK_STOP_BREAK); + levelEvent.setPosition(Vector3f.from( + packet.getPosition().getX(), + packet.getPosition().getY(), + packet.getPosition().getZ() + )); + levelEvent.setData(0); + session.getUpstream().sendPacket(levelEvent); + break; + } } } + + /*private static double speedBonusByEfficiencyLore0(int efficiencyLoreLevel) { + if (efficiencyLoreLevel == 0) return 0; + return efficiencyLoreLevel * efficiencyLoreLevel + 1; + }*/ + + /*private static double speedRateByHasteLore0(int hasteLoreLevel) { + return 1.0 + (0.2 * hasteLoreLevel); + }*/ + + + private boolean correctTool(String blockToolType, String itemToolType) { + return (blockToolType.equals("sword") && itemToolType.equals("sword")) || + (blockToolType.equals("shovel") && itemToolType.equals("shovel")) || + (blockToolType.equals("pickaxe") && itemToolType.equals("pickaxe")) || + (blockToolType.equals("axe") && itemToolType.equals("axe")) || + (blockToolType.equals("shears") && itemToolType.equals("shears")) || + blockToolType.equals(""); + } + + private double toolBreakTimeBonus0(String toolType, String toolTier, boolean isWoolBlock, boolean isCobweb) { + if (toolType.equals("sword")) return isCobweb ? 15.0 : 1.0; + if (toolType.equals("shears")) return isWoolBlock ? 5.0 : 15.0; + if (toolType.equals("none")) return 1.0; + switch (toolTier) { + case "wooden": + return 2.0; + case "stone": + return 4.0; + case "iron": + return 6.0; + case "diamond": + return 8.0; + case "golden": + return 12.0; + default: + return 1.0; + } + } + + //http://minecraft.gamepedia.com/Breaking + private double breakTime0(double blockHardness, String toolTier, boolean canHarvestWithHand, boolean correctTool, + String toolType, boolean isWoolBlock, boolean isCobweb + /*int efficiencyLoreLevel, int hasteEffectLevel, + boolean insideOfWaterWithoutAquaAffinity, boolean outOfWaterButNotOnGround*/) { + System.out.println("blockHardness = " + blockHardness); + double baseTime = ((correctTool || canHarvestWithHand) ? 1.5 : 5.0) * blockHardness; + System.out.println("baseTime = " + baseTime); + double speed = 1.0 / baseTime; + System.out.println("speed = " + speed); + + if (correctTool) speed *= toolBreakTimeBonus0(toolType,toolTier, isWoolBlock, isCobweb); + System.out.println("speed = " + speed); + // TODO implement this math + //speed += speedBonusByEfficiencyLore0(efficiencyLoreLevel); + //speed *= speedRateByHasteLore0(hasteEffectLevel); + //if (insideOfWaterWithoutAquaAffinity) speed *= 0.2; + //if (outOfWaterButNotOnGround) speed *= 0.2; + return 1.0 / speed; + } + + private double getBreakTime(double blockHardness, int blockId, ItemEntry item) { + String blockToolType = BlockTranslator.JAVA_RUNTIME_ID_TO_TOOL_TYPE.getOrDefault(blockId, ""); + boolean canHarvestWithHand = BlockTranslator.JAVA_RUNTIME_ID_TO_CAN_BREAK_WITH_HAND.get(blockId); + System.out.println("canHarvestWithHand = " + canHarvestWithHand); + String toolTier = "none"; + if (item != null) { + toolTier = item.getToolTier(); + } + String toolType = "none"; + if (item != null) { + toolType = item.getToolType(); + } + boolean correctTool = correctTool(blockToolType, toolType); + System.out.println("correctTool = " + correctTool); + System.out.println("itemToolType = " + toolType); + System.out.println("toolTier = " + toolTier); + boolean isWoolBlock = BlockTranslator.JAVA_RUNTIME_WOOL_IDS.contains(blockId); + boolean isCobweb = blockId == BlockTranslator.JAVA_RUNTIME_COBWEB_ID; + System.out.println("isWoolBlock = " + isWoolBlock); + System.out.println("isCobweb = " + isCobweb); + + //int efficiencyLoreLevel = Optional.ofNullable(item.getEnchantment(Enchantment.ID_EFFICIENCY)) + // .map(Enchantment::getLevel).orElse(0); + //int hasteEffectLevel = Optional.ofNullable(player.getEffect(Effect.HASTE)) + // .map(Effect::getAmplifier).orElse(0); + //boolean insideOfWaterWithoutAquaAffinity = player.isInsideOfWater() && + // Optional.ofNullable(player.getInventory().getHelmet().getEnchantment(Enchantment.ID_WATER_WORKER)) + // .map(Enchantment::getLevel).map(l -> l >= 1).orElse(false); + //boolean outOfWaterButNotOnGround = (!player.isInsideOfWater()) && (!player.isOnGround()); + //return breakTime0(blockHardness, correctTool, canHarvestWithHand, blockId, itemToolType, itemTier, + // efficiencyLoreLevel, hasteEffectLevel, insideOfWaterWithoutAquaAffinity, outOfWaterButNotOnGround); + double returnValue = breakTime0(blockHardness, toolTier, canHarvestWithHand, correctTool, toolType, isWoolBlock, isCobweb); + System.out.println("returnValue = " + returnValue); + return returnValue; + } } + + diff --git a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java index 9601b5819..ee4917c74 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java +++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java @@ -103,8 +103,12 @@ public class Toolbox { Iterator<Map.Entry<String, JsonNode>> iterator = items.fields(); while (iterator.hasNext()) { Map.Entry<String, JsonNode> entry = iterator.next(); - ITEM_ENTRIES.put(itemIndex, new ItemEntry(entry.getKey(), itemIndex, - entry.getValue().get("bedrock_id").intValue(), entry.getValue().get("bedrock_data").intValue())); + ITEM_ENTRIES.put(itemIndex, new ItemEntry( + entry.getKey(), itemIndex, + entry.getValue().get("bedrock_id").intValue(), + entry.getValue().get("bedrock_data").intValue(), + entry.getValue().get("tool_type").textValue(), + entry.getValue().get("tool_tier").textValue())); itemIndex++; }