3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-12-27 00:23:03 +01:00

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)

Dieser Commit ist enthalten in:
William Johnstone 2020-03-21 20:14:09 +00:00
Ursprung 350bb28c7c
Commit 09cdcbdf94
6 geänderte Dateien mit 202 neuen und 4 gelöschten Zeilen

Datei anzeigen

@ -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>

Datei anzeigen

@ -50,6 +50,6 @@ public class PlayerInventory extends Inventory {
} }
public ItemStack getItemInHand() { public ItemStack getItemInHand() {
return items[heldItemSlot]; return items[36 + heldItemSlot];
} }
} }

Datei anzeigen

@ -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");
} }

Datei anzeigen

@ -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()));

Datei anzeigen

@ -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;
} }
} }

Datei anzeigen

@ -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++;
} }