forked from GeyserMC/Geyser
Add block breaking animations that actually work (still incomplete, doesn't take enchantments or player effects into account, also doesn't account for being in the air or underwater)
This commit is contained in:
parent
350bb28c7c
commit
09cdcbdf94
6 changed files with 202 additions and 4 deletions
|
@ -48,6 +48,18 @@
|
||||||
<version>8.1.1</version>
|
<version>8.1.1</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>com.github.steveice10</groupId>
|
<groupId>com.github.steveice10</groupId>
|
||||||
<artifactId>opennbt</artifactId>
|
<artifactId>opennbt</artifactId>
|
||||||
|
|
|
@ -50,6 +50,6 @@ public class PlayerInventory extends Inventory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemStack getItemInHand() {
|
public ItemStack getItemInHand() {
|
||||||
return items[heldItemSlot];
|
return items[36 + heldItemSlot];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,13 @@ public class BlockTranslator {
|
||||||
private static final IntSet WATERLOGGED = new IntOpenHashSet();
|
private static final IntSet WATERLOGGED = new IntOpenHashSet();
|
||||||
|
|
||||||
private static final Map<BlockState, String> JAVA_ID_TO_BLOCK_ENTITY_MAP = new HashMap<>();
|
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;
|
private static final int BLOCK_STATE_VERSION = 17760256;
|
||||||
|
|
||||||
|
@ -87,6 +94,7 @@ public class BlockTranslator {
|
||||||
int waterRuntimeId = -1;
|
int waterRuntimeId = -1;
|
||||||
int javaRuntimeId = -1;
|
int javaRuntimeId = -1;
|
||||||
int bedrockRuntimeId = 0;
|
int bedrockRuntimeId = 0;
|
||||||
|
int cobwebRuntimeId = -1;
|
||||||
Iterator<Map.Entry<String, JsonNode>> blocksIterator = blocks.fields();
|
Iterator<Map.Entry<String, JsonNode>> blocksIterator = blocks.fields();
|
||||||
while (blocksIterator.hasNext()) {
|
while (blocksIterator.hasNext()) {
|
||||||
javaRuntimeId++;
|
javaRuntimeId++;
|
||||||
|
@ -95,6 +103,28 @@ public class BlockTranslator {
|
||||||
BlockState javaBlockState = new BlockState(javaRuntimeId);
|
BlockState javaBlockState = new BlockState(javaRuntimeId);
|
||||||
CompoundTag blockTag = buildBedrockState(entry.getValue());
|
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);
|
JAVA_ID_BLOCK_MAP.put(javaId, javaBlockState);
|
||||||
|
|
||||||
if (javaId.contains("sign[")) {
|
if (javaId.contains("sign[")) {
|
||||||
|
@ -131,6 +161,11 @@ public class BlockTranslator {
|
||||||
bedrockRuntimeId++;
|
bedrockRuntimeId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cobwebRuntimeId == -1) {
|
||||||
|
throw new AssertionError("Unable to find cobwebs in palette");
|
||||||
|
}
|
||||||
|
JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId;
|
||||||
|
|
||||||
if (waterRuntimeId == -1) {
|
if (waterRuntimeId == -1) {
|
||||||
throw new AssertionError("Unable to find water in palette");
|
throw new AssertionError("Unable to find water in palette");
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ import lombok.Getter;
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class ItemEntry {
|
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 String javaIdentifier;
|
||||||
private int javaId;
|
private int javaId;
|
||||||
|
@ -40,6 +40,9 @@ public class ItemEntry {
|
||||||
private int bedrockId;
|
private int bedrockId;
|
||||||
private int bedrockData;
|
private int bedrockData;
|
||||||
|
|
||||||
|
private String toolType;
|
||||||
|
private String toolTier;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
return obj == this || (obj instanceof ItemEntry && ((ItemEntry) obj).getBedrockId() == this.getBedrockId() && ((ItemEntry) obj).getJavaIdentifier().equals(this.getJavaIdentifier()));
|
return obj == this || (obj instanceof ItemEntry && ((ItemEntry) obj).getBedrockId() == this.getBedrockId() && ((ItemEntry) obj).getJavaIdentifier().equals(this.getJavaIdentifier()));
|
||||||
|
|
|
@ -25,9 +25,17 @@
|
||||||
|
|
||||||
package org.geysermc.connector.network.translators.java.entity.player;
|
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.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.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
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;
|
import org.geysermc.connector.utils.ChunkUtils;
|
||||||
|
|
||||||
public class JavaPlayerActionAckTranslator extends PacketTranslator<ServerPlayerActionAckPacket> {
|
public class JavaPlayerActionAckTranslator extends PacketTranslator<ServerPlayerActionAckPacket> {
|
||||||
|
@ -38,6 +46,142 @@ public class JavaPlayerActionAckTranslator extends PacketTranslator<ServerPlayer
|
||||||
case FINISH_DIGGING:
|
case FINISH_DIGGING:
|
||||||
ChunkUtils.updateBlock(session, packet.getNewState(), packet.getPosition());
|
ChunkUtils.updateBlock(session, packet.getNewState(), packet.getPosition());
|
||||||
break;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -103,8 +103,12 @@ public class Toolbox {
|
||||||
Iterator<Map.Entry<String, JsonNode>> iterator = items.fields();
|
Iterator<Map.Entry<String, JsonNode>> iterator = items.fields();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
Map.Entry<String, JsonNode> entry = iterator.next();
|
Map.Entry<String, JsonNode> entry = iterator.next();
|
||||||
ITEM_ENTRIES.put(itemIndex, new ItemEntry(entry.getKey(), itemIndex,
|
ITEM_ENTRIES.put(itemIndex, new ItemEntry(
|
||||||
entry.getValue().get("bedrock_id").intValue(), entry.getValue().get("bedrock_data").intValue()));
|
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++;
|
itemIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue