Block breaking refactors (#1336)

Client-side block animations and reach checks are now added.

This commit also includes cleanup in BlockChangeTranslator as well as proper Netherite tool support for calculating block breaking.

Co-authored-by: rtm516 <rtm516@users.noreply.github.com>
This commit is contained in:
Camotoy 2021-01-07 22:43:36 -05:00 committed by GitHub
parent fe23c79053
commit ade4c14911
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 224 additions and 78 deletions

View File

@ -39,12 +39,11 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlaye
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
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.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.*;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket;
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
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;
@ -64,9 +63,18 @@ import org.geysermc.connector.utils.InventoryUtils;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/**
* BedrockInventoryTransactionTranslator handles most interactions between the client and the world,
* or the client and their inventory.
*/
@Translator(packet = InventoryTransactionPacket.class) @Translator(packet = InventoryTransactionPacket.class)
public class BedrockInventoryTransactionTranslator extends PacketTranslator<InventoryTransactionPacket> { public class BedrockInventoryTransactionTranslator extends PacketTranslator<InventoryTransactionPacket> {
private static final float MAXIMUM_BLOCK_PLACING_DISTANCE = 64f;
private static final int CREATIVE_EYE_HEIGHT_PLACE_DISTANCE = 49;
private static final int SURVIVAL_EYE_HEIGHT_PLACE_DISTANCE = 36;
private static final float MAXIMUM_BLOCK_DESTROYING_DISTANCE = 36f;
@Override @Override
public void translate(InventoryTransactionPacket packet, GeyserSession session) { public void translate(InventoryTransactionPacket packet, GeyserSession session) {
// Send book updates before opening inventories // Send book updates before opening inventories
@ -112,6 +120,46 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
break; break;
} }
Vector3i blockPos = BlockUtils.getBlockPosition(packet.getBlockPosition(), packet.getBlockFace());
/*
Checks to ensure that the range will be accepted by the server.
"Not in range" doesn't refer to how far a vanilla client goes (that's a whole other mess),
but how much a server will accept from the client maximum
*/
// CraftBukkit+ check - see https://github.com/PaperMC/Paper/blob/458db6206daae76327a64f4e2a17b67a7e38b426/Spigot-Server-Patches/0532-Move-range-check-for-block-placing-up.patch
Vector3f playerPosition = session.getPlayerEntity().getPosition();
EntityFlags flags = session.getPlayerEntity().getMetadata().getFlags();
// Adjust position for current eye height
if (flags.getFlag(EntityFlag.SNEAKING)) {
playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 1.27f), 0);
} else if (flags.getFlag(EntityFlag.SWIMMING) || flags.getFlag(EntityFlag.GLIDING) || flags.getFlag(EntityFlag.DAMAGE_NEARBY_MOBS)) {
// Swimming, gliding, or using the trident spin attack
playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 0.4f), 0);
} else if (flags.getFlag(EntityFlag.SLEEPING)) {
playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 0.2f), 0);
} // else, we don't have to modify the position
float diffX = playerPosition.getX() - packet.getBlockPosition().getX();
float diffY = playerPosition.getY() - packet.getBlockPosition().getY();
float diffZ = playerPosition.getZ() - packet.getBlockPosition().getZ();
if (((diffX * diffX) + (diffY * diffY) + (diffZ * diffZ)) >
(session.getGameMode().equals(GameMode.CREATIVE) ? CREATIVE_EYE_HEIGHT_PLACE_DISTANCE : SURVIVAL_EYE_HEIGHT_PLACE_DISTANCE)) {
restoreCorrectBlock(session, blockPos, packet);
return;
}
// Vanilla check
if (!(session.getPlayerEntity().getPosition().sub(0, EntityType.PLAYER.getOffset(), 0)
.distanceSquared(packet.getBlockPosition().toFloat().add(0.5f, 0.5f, 0.5f)) < MAXIMUM_BLOCK_PLACING_DISTANCE)) {
// The client thinks that its blocks have been successfully placed. Restore the server's blocks instead.
restoreCorrectBlock(session, blockPos, packet);
return;
}
/*
Block place checks end - client is good to go
*/
ClientPlayerPlaceBlockPacket blockPacket = new ClientPlayerPlaceBlockPacket( ClientPlayerPlaceBlockPacket blockPacket = new ClientPlayerPlaceBlockPacket(
new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()), new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()),
BlockFace.values()[packet.getBlockFace()], BlockFace.values()[packet.getBlockFace()],
@ -159,7 +207,6 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
} }
} }
Vector3i blockPos = BlockUtils.getBlockPosition(packet.getBlockPosition(), packet.getBlockFace());
ItemEntry handItem = ItemRegistry.getItem(packet.getItemInHand()); ItemEntry handItem = ItemRegistry.getItem(packet.getItemInHand());
if (handItem.isBlock()) { if (handItem.isBlock()) {
session.setLastBlockPlacePosition(blockPos); session.setLastBlockPlacePosition(blockPos);
@ -184,19 +231,32 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
session.sendDownstreamPacket(useItemPacket); session.sendDownstreamPacket(useItemPacket);
break; break;
case 2: case 2:
int blockState = session.getConnector().getWorldManager().getBlockAt(session, packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()); int blockState = session.getGameMode() == GameMode.CREATIVE ?
double blockHardness = BlockTranslator.JAVA_RUNTIME_ID_TO_HARDNESS.get(blockState); session.getConnector().getWorldManager().getBlockAt(session, packet.getBlockPosition()) : session.getBreakingBlock();
if (session.getGameMode() == GameMode.CREATIVE || (session.getConnector().getConfig().isCacheChunks() && blockHardness == 0)) {
session.setLastBlockPlacedId(null);
session.setLastBlockPlacePosition(null);
LevelEventPacket blockBreakPacket = new LevelEventPacket(); session.setLastBlockPlacedId(null);
blockBreakPacket.setType(LevelEventType.PARTICLE_DESTROY_BLOCK); session.setLastBlockPlacePosition(null);
blockBreakPacket.setPosition(packet.getBlockPosition().toFloat());
blockBreakPacket.setData(BlockTranslator.getBedrockBlockId(blockState)); // Same deal with vanilla block placing as above.
session.sendUpstreamPacket(blockBreakPacket); // This is working out the distance using 3d Pythagoras and the extra value added to the Y is the sneaking height of a java player.
playerPosition = session.getPlayerEntity().getPosition();
Vector3f floatBlockPosition = packet.getBlockPosition().toFloat();
diffX = playerPosition.getX() - (floatBlockPosition.getX() + 0.5f);
diffY = (playerPosition.getY() - EntityType.PLAYER.getOffset()) - (floatBlockPosition.getY() + 0.5f) + 1.5f;
diffZ = playerPosition.getZ() - (floatBlockPosition.getZ() + 0.5f);
float distanceSquared = diffX * diffX + diffY * diffY + diffZ * diffZ;
if (distanceSquared > MAXIMUM_BLOCK_DESTROYING_DISTANCE) {
restoreCorrectBlock(session, packet.getBlockPosition(), packet);
return;
} }
LevelEventPacket blockBreakPacket = new LevelEventPacket();
blockBreakPacket.setType(LevelEventType.PARTICLE_DESTROY_BLOCK);
blockBreakPacket.setPosition(packet.getBlockPosition().toFloat());
blockBreakPacket.setData(BlockTranslator.getBedrockBlockId(blockState));
session.sendUpstreamPacket(blockBreakPacket);
session.setBreakingBlock(BlockTranslator.JAVA_AIR_ID);
long frameEntityId = ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition()); long frameEntityId = ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition());
if (frameEntityId != -1 && session.getEntityCache().getEntityByJavaId(frameEntityId) != null) { if (frameEntityId != -1 && session.getEntityCache().getEntityByJavaId(frameEntityId) != null) {
ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) frameEntityId, InteractAction.ATTACK, session.isSneaking()); ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) frameEntityId, InteractAction.ATTACK, session.isSneaking());
@ -270,4 +330,34 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
break; break;
} }
} }
/**
* Restore the correct block state from the server without updating the chunk cache.
*
* @param session the session of the Bedrock client
* @param blockPos the block position to restore
*/
private void restoreCorrectBlock(GeyserSession session, Vector3i blockPos, InventoryTransactionPacket packet) {
int javaBlockState = session.getConnector().getWorldManager().getBlockAt(session, blockPos);
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
updateBlockPacket.setBlockPosition(blockPos);
updateBlockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(javaBlockState));
updateBlockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.sendUpstreamPacket(updateBlockPacket);
UpdateBlockPacket updateWaterPacket = new UpdateBlockPacket();
updateWaterPacket.setDataLayer(1);
updateWaterPacket.setBlockPosition(blockPos);
updateWaterPacket.setRuntimeId(BlockTranslator.isWaterlogged(javaBlockState) ? BlockTranslator.BEDROCK_WATER_ID : BlockTranslator.BEDROCK_AIR_ID);
updateWaterPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.sendUpstreamPacket(updateWaterPacket);
// Reset the item in hand to prevent "missing" blocks
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.INVENTORY);
slotPacket.setSlot(packet.getHotbarSlot());
slotPacket.setItem(packet.getItemInHand());
session.sendUpstreamPacket(slotPacket);
}
} }

View File

@ -25,13 +25,16 @@
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.GameMode;
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerState; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerState;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerAbilitiesPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerAbilitiesPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerStatePacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerStatePacket;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
@ -40,9 +43,12 @@ import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket; 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.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.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;
@ -130,6 +136,27 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
break; break;
case START_BREAK: case START_BREAK:
if (session.getConnector().getConfig().isCacheChunks()) { if (session.getConnector().getConfig().isCacheChunks()) {
// Start the block breaking animation
if (session.getGameMode() != GameMode.CREATIVE) {
int blockState = session.getConnector().getWorldManager().getBlockAt(session, vector);
double blockHardness = BlockTranslator.JAVA_RUNTIME_ID_TO_HARDNESS.get(blockState);
LevelEventPacket startBreak = new LevelEventPacket();
startBreak.setType(LevelEventType.BLOCK_START_BREAK);
startBreak.setPosition(vector.toFloat());
PlayerInventory inventory = session.getInventory();
ItemStack item = inventory.getItemInHand();
ItemEntry itemEntry = null;
CompoundTag nbtData = new CompoundTag("");
if (item != null) {
itemEntry = ItemRegistry.getItem(item);
nbtData = item.getNbt();
}
double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, blockState, itemEntry, nbtData, session) * 20);
startBreak.setData((int) (65535 / breakTime));
session.setBreakingBlock(blockState);
session.sendUpstreamPacket(startBreak);
}
// Account for fire - the client likes to hit the block behind. // Account for fire - the client likes to hit the block behind.
Vector3i fireBlockPos = BlockUtils.getBlockPosition(packet.getBlockPosition(), packet.getFace()); Vector3i fireBlockPos = BlockUtils.getBlockPosition(packet.getBlockPosition(), packet.getFace());
int blockUp = session.getConnector().getWorldManager().getBlockAt(session, fireBlockPos); int blockUp = session.getConnector().getWorldManager().getBlockAt(session, fireBlockPos);
@ -138,24 +165,33 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, new Position(fireBlockPos.getX(), ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, new Position(fireBlockPos.getX(),
fireBlockPos.getY(), fireBlockPos.getZ()), BlockFace.values()[packet.getFace()]); fireBlockPos.getY(), fireBlockPos.getZ()), BlockFace.values()[packet.getFace()]);
session.sendDownstreamPacket(startBreakingPacket); session.sendDownstreamPacket(startBreakingPacket);
break; if (session.getGameMode() == GameMode.CREATIVE) {
break;
}
} }
} }
ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, new Position(packet.getBlockPosition().getX(), ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, position, BlockFace.values()[packet.getFace()]);
packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()), BlockFace.values()[packet.getFace()]);
session.sendDownstreamPacket(startBreakingPacket); session.sendDownstreamPacket(startBreakingPacket);
break; break;
case CONTINUE_BREAK: case CONTINUE_BREAK:
if (session.getGameMode() == GameMode.CREATIVE) {
break;
}
LevelEventPacket continueBreakPacket = new LevelEventPacket(); LevelEventPacket continueBreakPacket = new LevelEventPacket();
continueBreakPacket.setType(LevelEventType.PARTICLE_CRACK_BLOCK); continueBreakPacket.setType(LevelEventType.PARTICLE_CRACK_BLOCK);
continueBreakPacket.setData(BlockTranslator.getBedrockBlockId(session.getBreakingBlock())); continueBreakPacket.setData((BlockTranslator.getBedrockBlockId(session.getBreakingBlock())) | (packet.getFace() << 24));
continueBreakPacket.setPosition(packet.getBlockPosition().toFloat()); continueBreakPacket.setPosition(vector.toFloat());
session.sendUpstreamPacket(continueBreakPacket); session.sendUpstreamPacket(continueBreakPacket);
break; break;
case ABORT_BREAK: case ABORT_BREAK:
ClientPlayerActionPacket abortBreakingPacket = new ClientPlayerActionPacket(PlayerAction.CANCEL_DIGGING, new Position(packet.getBlockPosition().getX(), ClientPlayerActionPacket abortBreakingPacket = new ClientPlayerActionPacket(PlayerAction.CANCEL_DIGGING, position, BlockFace.DOWN);
packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()), BlockFace.DOWN);
session.sendDownstreamPacket(abortBreakingPacket); session.sendDownstreamPacket(abortBreakingPacket);
LevelEventPacket stopBreak = new LevelEventPacket();
stopBreak.setType(LevelEventType.BLOCK_STOP_BREAK);
stopBreak.setPosition(vector.toFloat());
stopBreak.setData(0);
session.setBreakingBlock(BlockTranslator.JAVA_AIR_ID);
session.sendUpstreamPacket(stopBreak);
break; break;
case STOP_BREAK: case STOP_BREAK:
// Handled in BedrockInventoryTransactionTranslator // Handled in BedrockInventoryTransactionTranslator

View File

@ -31,8 +31,8 @@ import com.nukkitx.protocol.bedrock.data.LevelEventType;
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.packet.EntityEventPacket; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
import com.nukkitx.protocol.bedrock.packet.LevelSoundEvent2Packet;
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import com.nukkitx.protocol.bedrock.packet.LevelSoundEvent2Packet;
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket; import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket;
import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Entity;
@ -183,6 +183,19 @@ public class JavaEntityStatusTranslator extends PacketTranslator<ServerEntitySta
return; return;
} }
break; break;
case LIVING_EQUIPMENT_BREAK_HEAD:
case LIVING_EQUIPMENT_BREAK_CHEST:
case LIVING_EQUIPMENT_BREAK_LEGS:
case LIVING_EQUIPMENT_BREAK_FEET:
case LIVING_EQUIPMENT_BREAK_MAIN_HAND:
case LIVING_EQUIPMENT_BREAK_OFF_HAND:
LevelSoundEvent2Packet equipmentBreakPacket = new LevelSoundEvent2Packet();
equipmentBreakPacket.setSound(SoundEvent.BREAK);
equipmentBreakPacket.setPosition(entity.getPosition());
equipmentBreakPacket.setExtraData(-1);
equipmentBreakPacket.setIdentifier("");
session.sendUpstreamPacket(equipmentBreakPacket);
return;
} }
session.sendUpstreamPacket(entityEventPacket); session.sendUpstreamPacket(entityEventPacket);

View File

@ -27,19 +27,20 @@ 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.data.game.entity.metadata.ItemStack;
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.PlayerAction;
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.github.steveice10.opennbt.tag.builtin.*; 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.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.item.ItemEntry;
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.network.translators.item.ItemEntry;
import org.geysermc.connector.utils.BlockUtils; import org.geysermc.connector.utils.BlockUtils;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.utils.ChunkUtils;
@Translator(packet = ServerPlayerActionAckPacket.class) @Translator(packet = ServerPlayerActionAckPacket.class)
@ -47,54 +48,54 @@ public class JavaPlayerActionAckTranslator extends PacketTranslator<ServerPlayer
@Override @Override
public void translate(ServerPlayerActionAckPacket packet, GeyserSession session) { public void translate(ServerPlayerActionAckPacket packet, GeyserSession session) {
LevelEventPacket levelEvent = new LevelEventPacket(); ChunkUtils.updateBlock(session, packet.getNewState(), packet.getPosition());
switch (packet.getAction()) { if (packet.getAction() == PlayerAction.START_DIGGING && !packet.isSuccessful()) {
case FINISH_DIGGING: LevelEventPacket stopBreak = new LevelEventPacket();
double blockHardness = BlockTranslator.JAVA_RUNTIME_ID_TO_HARDNESS.get(session.getBreakingBlock()); stopBreak.setType(LevelEventType.BLOCK_STOP_BREAK);
if (session.getGameMode() != GameMode.CREATIVE && blockHardness != 0) { stopBreak.setPosition(Vector3f.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()));
levelEvent.setType(LevelEventType.PARTICLE_DESTROY_BLOCK); stopBreak.setData(0);
levelEvent.setPosition(Vector3f.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ())); session.setBreakingBlock(BlockTranslator.JAVA_AIR_ID);
levelEvent.setData(BlockTranslator.getBedrockBlockId(session.getBreakingBlock())); session.sendUpstreamPacket(stopBreak);
}
if (!session.getConnector().getConfig().isCacheChunks()) {
LevelEventPacket levelEvent = new LevelEventPacket();
switch (packet.getAction()) {
case START_DIGGING:
if (session.getGameMode() == GameMode.CREATIVE) {
break;
}
double blockHardness = BlockTranslator.JAVA_RUNTIME_ID_TO_HARDNESS.get(packet.getNewState());
levelEvent.setType(LevelEventType.BLOCK_START_BREAK);
levelEvent.setPosition(Vector3f.from(
packet.getPosition().getX(),
packet.getPosition().getY(),
packet.getPosition().getZ()
));
PlayerInventory inventory = session.getInventory();
ItemStack item = inventory.getItemInHand();
ItemEntry itemEntry = null;
CompoundTag nbtData = new CompoundTag("");
if (item != null) {
itemEntry = ItemRegistry.getItem(item);
nbtData = item.getNbt();
}
double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, packet.getNewState(), itemEntry, nbtData, session) * 20);
levelEvent.setData((int) (65535 / breakTime));
session.setBreakingBlock(packet.getNewState());
session.sendUpstreamPacket(levelEvent); session.sendUpstreamPacket(levelEvent);
session.setBreakingBlock(0);
}
ChunkUtils.updateBlock(session, packet.getNewState(), packet.getPosition());
break;
case START_DIGGING:
if (session.getGameMode() == GameMode.CREATIVE) {
break; break;
} case CANCEL_DIGGING:
blockHardness = BlockTranslator.JAVA_RUNTIME_ID_TO_HARDNESS.get(packet.getNewState()); levelEvent.setType(LevelEventType.BLOCK_STOP_BREAK);
levelEvent.setType(LevelEventType.BLOCK_START_BREAK); levelEvent.setPosition(Vector3f.from(
levelEvent.setPosition(Vector3f.from( packet.getPosition().getX(),
packet.getPosition().getX(), packet.getPosition().getY(),
packet.getPosition().getY(), packet.getPosition().getZ()
packet.getPosition().getZ() ));
)); levelEvent.setData(0);
PlayerInventory inventory = session.getInventory(); session.setBreakingBlock(0);
ItemStack item = inventory.getItemInHand(); session.sendUpstreamPacket(levelEvent);
ItemEntry itemEntry = null; break;
CompoundTag nbtData = new CompoundTag(""); }
if (item != null) {
itemEntry = ItemRegistry.getItem(item);
nbtData = item.getNbt();
}
double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, packet.getNewState(), itemEntry, nbtData, session) * 20);
levelEvent.setData((int) (65535 / breakTime));
session.setBreakingBlock(packet.getNewState());
session.sendUpstreamPacket(levelEvent);
break;
case CANCEL_DIGGING:
levelEvent.setType(LevelEventType.BLOCK_STOP_BREAK);
levelEvent.setPosition(Vector3f.from(
packet.getPosition().getX(),
packet.getPosition().getY(),
packet.getPosition().getZ()
));
levelEvent.setData(0);
session.setBreakingBlock(0);
session.sendUpstreamPacket(levelEvent);
break;
} }
} }
} }

View File

@ -26,6 +26,7 @@
package org.geysermc.connector.network.translators.java.world; package org.geysermc.connector.network.translators.java.world;
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.packet.ingame.server.world.ServerBlockChangePacket;
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.data.SoundEvent;
import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket; import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
@ -37,17 +38,17 @@ import org.geysermc.connector.network.translators.sound.BlockSoundInteractionHan
import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.utils.ChunkUtils;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerBlockChangePacket;
@Translator(packet = ServerBlockChangePacket.class) @Translator(packet = ServerBlockChangePacket.class)
public class JavaBlockChangeTranslator extends PacketTranslator<ServerBlockChangePacket> { public class JavaBlockChangeTranslator extends PacketTranslator<ServerBlockChangePacket> {
@Override @Override
public void translate(ServerBlockChangePacket packet, GeyserSession session) { public void translate(ServerBlockChangePacket packet, GeyserSession session) {
Position pos = packet.getRecord().getPosition(); Position pos = packet.getRecord().getPosition();
boolean updatePlacement = !(session.getConnector().getConfig().isCacheChunks() && session.getConnector().getWorldManager().getBlockAt(session, pos.getX(), pos.getY(), pos.getZ()) == packet.getRecord().getBlock()); boolean updatePlacement = session.getConnector().getPlatformType() != PlatformType.SPIGOT && // Spigot simply listens for the block place event
ChunkUtils.updateBlock(session, packet.getRecord().getBlock(), packet.getRecord().getPosition()); !(session.getConnector().getConfig().isCacheChunks() &&
if (updatePlacement && session.getConnector().getPlatformType() != PlatformType.SPIGOT) { session.getConnector().getWorldManager().getBlockAt(session, pos) == packet.getRecord().getBlock());
ChunkUtils.updateBlock(session, packet.getRecord().getBlock(), pos);
if (updatePlacement) {
this.checkPlace(session, packet); this.checkPlace(session, packet);
} }
this.checkInteract(session, packet); this.checkInteract(session, packet);

View File

@ -87,7 +87,9 @@ public class BlockTranslator {
*/ */
public static final int BEDROCK_RUNTIME_COMMAND_BLOCK_ID; public static final int BEDROCK_RUNTIME_COMMAND_BLOCK_ID;
// For block breaking animation math /**
* A list of all Java runtime wool IDs, for use with block breaking math and shears
*/
public static final IntSet JAVA_RUNTIME_WOOL_IDS = new IntOpenHashSet(); public static final IntSet JAVA_RUNTIME_WOOL_IDS = new IntOpenHashSet();
public static final int JAVA_RUNTIME_COBWEB_ID; public static final int JAVA_RUNTIME_COBWEB_ID;

View File

@ -50,6 +50,7 @@ public class BlockUtils {
if (toolType.equals("shears")) return isWoolBlock ? 5.0 : 15.0; if (toolType.equals("shears")) return isWoolBlock ? 5.0 : 15.0;
if (toolType.equals("")) return 1.0; if (toolType.equals("")) return 1.0;
switch (toolTier) { switch (toolTier) {
// https://minecraft.gamepedia.com/Breaking#Speed
case "wooden": case "wooden":
return 2.0; return 2.0;
case "stone": case "stone":
@ -58,6 +59,8 @@ public class BlockUtils {
return 6.0; return 6.0;
case "diamond": case "diamond":
return 8.0; return 8.0;
case "netherite":
return 9.0;
case "golden": case "golden":
return 12.0; return 12.0;
default: default: