1
0
Fork 0
mirror of https://github.com/GeyserMC/Geyser.git synced 2025-04-17 19:12:14 +02:00
This commit is contained in:
chris 2025-04-13 12:54:54 +02:00 committed by GitHub
commit bae635ef47
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 271 additions and 193 deletions

View file

@ -144,7 +144,7 @@ public class ItemMappings implements DefinitionRegistry<ItemDefinition> {
return lightBlock;
}
boolean isBlock = data.getBlockDefinition() != null;
boolean isBlock = data.getBlockDefinition() != null && data.getBlockDefinition().getRuntimeId() != 0;
boolean hasDamage = data.getDamage() != 0;
for (ItemMapping mapping : this.items) {

View file

@ -407,9 +407,24 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Setter
private BedrockDimension bedrockDimension = this.bedrockOverworldDimension;
/**
* Stores the blockstate of the block being currently broken.
*/
@Setter
private int breakingBlock;
/**
* Stores the block break position of the currently broken block.
*/
@Setter
private Vector3i blockBreakPosition;
/**
* Stores the block breaking progress of the currently broken block.
*/
@Setter
private double blockBreakProgress;
@Setter
private Vector3i lastBlockPlacePosition;
@ -1608,7 +1623,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.SERVER);
startGamePacket.setRewindHistorySize(0);
startGamePacket.setServerAuthoritativeBlockBreaking(false);
startGamePacket.setServerAuthoritativeBlockBreaking(true);
startGamePacket.setServerId("");
startGamePacket.setWorldId("");

View file

@ -572,9 +572,9 @@ public abstract class InventoryTranslator {
case CRAFT_RESULTS_DEPRECATED: // Tends to be called for UI inventories
case CRAFT_RECIPE_OPTIONAL: // Anvils and cartography tables will handle this
case CRAFT_LOOM: // Looms 1.17.40+
case CRAFT_REPAIR_AND_DISENCHANT: { // Grindstones 1.17.40+
case CRAFT_REPAIR_AND_DISENCHANT: // Grindstones 1.17.40+
case MINE_BLOCK: // Server auth block breaking, confirms durability change
break;
}
default:
return rejectRequest(request);
}

View file

@ -175,6 +175,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
break;
case ITEM_USE:
switch (packet.getActionType()) {
// Block placing
case 0 -> {
final Vector3i packetBlockPosition = packet.getBlockPosition();
Vector3i blockPos = BlockUtils.getBlockPosition(packetBlockPosition, packet.getBlockFace());

View file

@ -32,7 +32,9 @@ import org.cloudburstmc.protocol.bedrock.data.PlayerActionType;
import org.cloudburstmc.protocol.bedrock.data.PlayerBlockActionData;
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.block.custom.CustomBlockState;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.ItemFrameEntity;
import org.geysermc.geyser.inventory.GeyserItemStack;
@ -44,6 +46,7 @@ import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.SkullCache;
import org.geysermc.geyser.translator.item.CustomItemTranslator;
import org.geysermc.geyser.translator.protocol.bedrock.BedrockInventoryTransactionTranslator;
import org.geysermc.geyser.util.BlockUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
@ -62,164 +65,259 @@ final class BedrockBlockActions {
session.getBookEditCache().checkForSend();
for (PlayerBlockActionData blockActionData : playerActions) {
handle(session, blockActionData);
}
}
PlayerActionType action = blockActionData.getAction();
Vector3i vector = blockActionData.getBlockPosition();
int blockFace = blockActionData.getFace();
private static void handle(GeyserSession session, PlayerBlockActionData blockActionData) {
PlayerActionType action = blockActionData.getAction();
Vector3i vector = blockActionData.getBlockPosition();
int blockFace = blockActionData.getFace();
switch (action) {
case DROP_ITEM -> {
ServerboundPlayerActionPacket dropItemPacket = new ServerboundPlayerActionPacket(PlayerAction.DROP_ITEM,
vector, Direction.VALUES[blockFace], 0);
session.sendDownstreamGamePacket(dropItemPacket);
}
case START_BREAK -> {
// Ignore START_BREAK when the player is CREATIVE to avoid Spigot receiving 2 packets it interpets as block breaking. https://github.com/GeyserMC/Geyser/issues/4021
if (session.getGameMode() == GameMode.CREATIVE) {
break;
GeyserImpl.getInstance().getLogger().info(blockActionData.toString());
switch (action) {
case DROP_ITEM -> {
ServerboundPlayerActionPacket dropItemPacket = new ServerboundPlayerActionPacket(PlayerAction.DROP_ITEM,
vector, Direction.VALUES[blockFace], 0);
session.sendDownstreamGamePacket(dropItemPacket);
}
if (!canMine(session, vector)) {
return;
}
// Start the block breaking animation
int blockState = session.getGeyser().getWorldManager().getBlockAt(session, vector);
LevelEventPacket startBreak = new LevelEventPacket();
startBreak.setType(LevelEvent.BLOCK_START_BREAK);
startBreak.setPosition(vector.toFloat());
double breakTime = BlockUtils.getSessionBreakTimeTicks(session, BlockState.of(blockState).block());
// If the block is custom or the breaking item is custom, we must keep track of break time ourselves
GeyserItemStack item = session.getPlayerInventory().getItemInHand();
ItemMapping mapping = item.getMapping(session);
ItemDefinition customItem = mapping.isTool() ? CustomItemTranslator.getCustomItem(item.getComponents(), mapping) : null;
CustomBlockState blockStateOverride = BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get(blockState);
SkullCache.Skull skull = session.getSkullCache().getSkulls().get(vector);
session.setBlockBreakStartTime(0);
if (blockStateOverride != null || customItem != null || (skull != null && skull.getBlockDefinition() != null)) {
session.setBlockBreakStartTime(System.currentTimeMillis());
}
startBreak.setData((int) (65535 / breakTime));
session.setBreakingBlock(blockState);
session.sendUpstreamPacket(startBreak);
// Account for fire - the client likes to hit the block behind.
Vector3i fireBlockPos = BlockUtils.getBlockPosition(vector, blockFace);
Block block = session.getGeyser().getWorldManager().blockAt(session, fireBlockPos).block();
Direction direction = Direction.VALUES[blockFace];
if (block == Blocks.FIRE || block == Blocks.SOUL_FIRE) {
ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.START_DIGGING, fireBlockPos,
direction, session.getWorldCache().nextPredictionSequence());
session.sendDownstreamGamePacket(startBreakingPacket);
}
ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.START_DIGGING,
vector, direction, session.getWorldCache().nextPredictionSequence());
session.sendDownstreamGamePacket(startBreakingPacket);
spawnBlockBreakParticles(session, direction, vector, BlockState.of(blockState));
}
case CONTINUE_BREAK -> {
if (session.getGameMode() == GameMode.CREATIVE) {
break;
}
if (!canMine(session, vector)) {
return;
}
int breakingBlock = session.getBreakingBlock();
if (breakingBlock == -1) {
breakingBlock = Block.JAVA_AIR_ID;
}
Vector3f vectorFloat = vector.toFloat();
BlockState breakingBlockState = BlockState.of(breakingBlock);
Direction direction = Direction.VALUES[blockFace];
spawnBlockBreakParticles(session, direction, vector, breakingBlockState);
double breakTime = BlockUtils.getSessionBreakTimeTicks(session, breakingBlockState.block());
// If the block is custom, we must keep track of when it should break ourselves
long blockBreakStartTime = session.getBlockBreakStartTime();
if (blockBreakStartTime != 0) {
long timeSinceStart = System.currentTimeMillis() - blockBreakStartTime;
// We need to add a slight delay to the break time, otherwise the client breaks blocks too fast
if (timeSinceStart >= (breakTime += 2) * 50) {
// Play break sound and particle
LevelEventPacket effectPacket = new LevelEventPacket();
effectPacket.setPosition(vectorFloat);
effectPacket.setType(LevelEvent.PARTICLE_DESTROY_BLOCK);
effectPacket.setData(session.getBlockMappings().getBedrockBlockId(breakingBlock));
session.sendUpstreamPacket(effectPacket);
// Break the block
ServerboundPlayerActionPacket finishBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.FINISH_DIGGING,
vector, direction, session.getWorldCache().nextPredictionSequence());
session.sendDownstreamGamePacket(finishBreakingPacket);
session.setBlockBreakStartTime(0);
case START_BREAK -> handleStartBreak(session, vector, blockFace);
case BLOCK_CONTINUE_DESTROY -> {
if (session.getGameMode() == GameMode.CREATIVE || isSteeringBoat(session, vector)) {
break;
}
}
// Update the break time in the event that player conditions changed (jumping, effects applied)
LevelEventPacket updateBreak = new LevelEventPacket();
updateBreak.setType(LevelEvent.BLOCK_UPDATE_BREAK);
updateBreak.setPosition(vectorFloat);
updateBreak.setData((int) (65535 / breakTime));
session.sendUpstreamPacket(updateBreak);
}
case ABORT_BREAK -> {
if (session.getGameMode() != GameMode.CREATIVE) {
// As of 1.16.210: item frame items are taken out here.
// Survival also sends START_BREAK, but by attaching our process here adventure mode also works
Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, vector);
if (itemFrameEntity != null) {
ServerboundInteractPacket interactPacket = new ServerboundInteractPacket(itemFrameEntity.getEntityId(),
InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking());
session.sendDownstreamGamePacket(interactPacket);
Direction direction = Direction.VALUES[blockFace];
// The Bedrock client won't send a new start_break packet, but just continue breaking blocks
if (!vector.equals(session.getBlockBreakPosition())) {
if (session.getBlockBreakProgress() < 1) {
// Abort block breaking for previous block, and start breaking this new block
// Otherwise the client will continue showing a block breaking animation for the previous block.
stopBreakingBlock(session, session.getBlockBreakPosition().toFloat());
ServerboundPlayerActionPacket abortBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.CANCEL_DIGGING, session.getBlockBreakPosition(), Direction.DOWN, 0);
session.sendDownstreamGamePacket(abortBreakingPacket);
}
// Start breaking the new block
handleStartBreak(session, vector, blockFace);
break;
}
int breakingBlock = session.getBreakingBlock();
if (breakingBlock == -1) {
breakingBlock = Block.JAVA_AIR_ID;
}
Vector3f vectorFloat = vector.toFloat();
BlockState breakingBlockState = BlockState.of(breakingBlock);
spawnBlockBreakParticles(session, direction, vector, breakingBlockState);
double breakTime = BlockUtils.getSessionBreakTimeTicks(session, breakingBlockState.block());
// If the block is custom, we must keep track of when it should break ourselves
long blockBreakStartTime = session.getBlockBreakStartTime();
if (blockBreakStartTime != 0) {
long timeSinceStart = System.currentTimeMillis() - blockBreakStartTime;
// We need to add a slight delay to the break time, otherwise the client breaks blocks too fast
if (timeSinceStart >= (breakTime += 2) * 50) {
stopBreakingBlock(session, vector.toFloat());
// Play break sound and particle
LevelEventPacket effectPacket = new LevelEventPacket();
effectPacket.setPosition(vectorFloat);
effectPacket.setType(LevelEvent.PARTICLE_DESTROY_BLOCK);
effectPacket.setData(session.getBlockMappings().getBedrockBlockId(breakingBlock));
session.sendUpstreamPacket(effectPacket);
// Break the block
sendJavaPlayerActionPacket(session, PlayerAction.FINISH_DIGGING, vector, Direction.values()[blockFace]);
resetSessionVariables(session);
break;
}
}
// Update the break time in the event that player conditions changed (jumping, effects applied)
LevelEventPacket updateBreak = new LevelEventPacket();
updateBreak.setType(LevelEvent.BLOCK_UPDATE_BREAK);
updateBreak.setPosition(vectorFloat);
updateBreak.setData((int) (65535 / breakTime));
session.sendUpstreamPacket(updateBreak);
}
case ABORT_BREAK -> {
if (session.getGameMode() != GameMode.CREATIVE) {
// As of 1.16.210: item frame items are taken out here.
// Survival also sends START_BREAK, but by attaching our process here adventure mode also works
Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, vector);
if (itemFrameEntity != null) {
ServerboundInteractPacket interactPacket = new ServerboundInteractPacket(itemFrameEntity.getEntityId(),
InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking());
session.sendDownstreamGamePacket(interactPacket);
break;
}
}
ServerboundPlayerActionPacket abortBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.CANCEL_DIGGING, vector, Direction.DOWN, 0);
session.sendDownstreamGamePacket(abortBreakingPacket);
// Bedrock "confirms" that it stopped breaking blocks by sending an abort packet after breaking the block
if (session.getBlockBreakPosition() == null) {
break;
}
LevelEventPacket stopBreak = new LevelEventPacket();
stopBreak.setType(LevelEvent.BLOCK_STOP_BREAK);
stopBreak.setPosition(vector.toFloat());
stopBreak.setData(0);
session.setBreakingBlock(-1);
session.setBlockBreakStartTime(0);
session.sendUpstreamPacket(stopBreak);
}
// Handled in BedrockInventoryTransactionTranslator
case STOP_BREAK -> {
ServerboundPlayerActionPacket abortBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.CANCEL_DIGGING, vector, Direction.DOWN, 0);
session.sendDownstreamGamePacket(abortBreakingPacket);
stopBreakingBlock(session, session.getBlockBreakPosition().toFloat());
resetSessionVariables(session);
}
// Handled in BedrockInventoryTransactionTranslator
case BLOCK_PREDICT_DESTROY -> {
handleBlockDestroy(session, vector, blockFace);
}
}
}
}
private static boolean canMine(GeyserSession session, Vector3i vector) {
private static void handleStartBreak(GeyserSession session, Vector3i vector, int blockFace) {
// Only send block breaking in the BLOCK_PREDICT_DESTROY case
if (session.getGameMode() == GameMode.CREATIVE || isSteeringBoat(session, vector)) {
return;
}
Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, vector);
if (itemFrameEntity != null) {
// Will be handled in ABORT_BREAK
return;
}
Vector3f playerPosition = session.getPlayerEntity().getPosition();
playerPosition = playerPosition.down(EntityDefinitions.PLAYER.offset() - session.getEyeHeight());
if (!BedrockInventoryTransactionTranslator.canInteractWithBlock(session, playerPosition, vector)) {
resetSessionVariables(session);
stopBreakingBlock(session, vector.toFloat());
return;
}
int blockState = session.getGeyser().getWorldManager().getBlockAt(session, vector);
double breakTime = BlockUtils.getSessionBreakTimeTicks(session, BlockState.of(blockState).block());
// Start the block breaking animation
LevelEventPacket startBreak = new LevelEventPacket();
startBreak.setType(LevelEvent.BLOCK_START_BREAK);
startBreak.setPosition(vector.toFloat());
startBreak.setData((int) (65535 / breakTime));
session.sendUpstreamPacket(startBreak);
session.setBreakingBlock(blockState);
session.setBlockBreakPosition(vector);
session.setBlockBreakStartTime(0);
// If the block is custom or the breaking item is custom, we must keep track of break time ourselves
// TODO 1.21.5 use tool attribute
GeyserItemStack item = session.getPlayerInventory().getItemInHand();
ItemMapping mapping = item.getMapping(session);
ItemDefinition customItem = mapping.isTool() ? CustomItemTranslator.getCustomItem(item.getComponents(), mapping) : null;
CustomBlockState blockStateOverride = BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get(blockState);
SkullCache.Skull skull = session.getSkullCache().getSkulls().get(vector);
if (blockStateOverride != null || customItem != null || (skull != null && skull.getBlockDefinition() != null)) {
session.setBlockBreakStartTime(System.currentTimeMillis());
}
Direction direction = Direction.VALUES[blockFace];
spawnBlockBreakParticles(session, direction, vector, BlockState.of(blockState));
// Account for fire - the client likes to hit the block behind.
Vector3i fireBlockPos = BlockUtils.getBlockPosition(vector, blockFace);
Block block = session.getGeyser().getWorldManager().blockAt(session, fireBlockPos).block();
if (block == Blocks.FIRE || block == Blocks.SOUL_FIRE) {
// TODO test
sendJavaPlayerActionPacket(session, PlayerAction.START_DIGGING, fireBlockPos, direction);
sendJavaPlayerActionPacket(session, PlayerAction.FINISH_DIGGING, fireBlockPos, direction);
}
sendJavaPlayerActionPacket(session, PlayerAction.START_DIGGING, vector, direction);
}
private static void handleBlockDestroy(GeyserSession session, Vector3i vector, int blockFace) {
boolean creative = session.getGameMode() == GameMode.CREATIVE;
int blockState = creative ? session.getGeyser().getWorldManager().getBlockAt(session, vector) : session.getBreakingBlock();
// TODO move
Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, vector);
if (itemFrameEntity != null) {
ServerboundInteractPacket attackPacket = new ServerboundInteractPacket(itemFrameEntity.getEntityId(),
InteractAction.ATTACK, session.isSneaking());
session.sendDownstreamGamePacket(attackPacket);
return;
}
// Already done this check in survival mode
if (creative) {
Vector3f playerPosition = session.getPlayerEntity().getPosition();
playerPosition = playerPosition.down(EntityDefinitions.PLAYER.offset() - session.getEyeHeight());
if (!BedrockInventoryTransactionTranslator.canInteractWithBlock(session, playerPosition, vector)) {
BedrockInventoryTransactionTranslator.restoreCorrectBlock(session, vector);
return;
}
} else {
if (!vector.equals(session.getBlockBreakPosition())) {
// Restore correct block if we aren't breaking this one
BedrockInventoryTransactionTranslator.restoreCorrectBlock(session, vector);
return;
}
}
session.setLastBlockPlaced(null);
session.setLastBlockPlacePosition(null);
// Same deal with vanilla block placing as above.
if (!session.getWorldBorder().isInsideBorderBoundaries()) {
BedrockInventoryTransactionTranslator.restoreCorrectBlock(session, vector);
return;
}
// -1 means we don't know what block they're breaking
if (blockState == -1) {
blockState = Block.JAVA_AIR_ID;
}
// TODO
session.setBlockBreakProgress(1.0);
stopBreakingBlock(session, vector.toFloat());
LevelEventPacket blockDestroyParticlePacket = new LevelEventPacket();
blockDestroyParticlePacket.setType(LevelEvent.PARTICLE_DESTROY_BLOCK);
blockDestroyParticlePacket.setPosition(vector.toFloat());
blockDestroyParticlePacket.setData(session.getBlockMappings().getBedrockBlockId(blockState));
session.sendUpstreamPacket(blockDestroyParticlePacket);
resetSessionVariables(session);
PlayerAction action = session.getGameMode() == GameMode.CREATIVE ? PlayerAction.START_DIGGING : PlayerAction.FINISH_DIGGING;
sendJavaPlayerActionPacket(session, action, vector, Direction.VALUES[blockFace]);
session.getWorldCache().markPositionInSequence(vector);
}
private static boolean isSteeringBoat(GeyserSession session, Vector3i vector) {
if (session.isHandsBusy()) {
session.setBreakingBlock(-1);
session.setBlockBreakStartTime(0);
LevelEventPacket stopBreak = new LevelEventPacket();
stopBreak.setType(LevelEvent.BLOCK_STOP_BREAK);
stopBreak.setPosition(vector.toFloat());
stopBreak.setData(0);
session.setBreakingBlock(-1);
session.sendUpstreamPacket(stopBreak);
return false;
resetSessionVariables(session);
stopBreakingBlock(session, vector.toFloat());
return true;
}
return true;
return false;
}
private static void spawnBlockBreakParticles(GeyserSession session, Direction direction, Vector3i position, BlockState blockState) {
private static void resetSessionVariables(GeyserSession session) {
session.setBreakingBlock(-1);
session.setBlockBreakPosition(null);
session.setBlockBreakStartTime(0);
}
private static void stopBreakingBlock(GeyserSession session, Vector3f vector) {
LevelEventPacket stopBreak = new LevelEventPacket();
stopBreak.setType(LevelEvent.BLOCK_STOP_BREAK);
stopBreak.setPosition(vector.toFloat());
stopBreak.setData(0);
session.sendUpstreamPacket(stopBreak);
}
public static void spawnBlockBreakParticles(GeyserSession session, Direction direction, Vector3i position, BlockState blockState) {
LevelEventPacket levelEventPacket = new LevelEventPacket();
switch (direction) {
case UP -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_UP);
@ -233,4 +331,11 @@ final class BedrockBlockActions {
levelEventPacket.setData(session.getBlockMappings().getBedrockBlock(blockState).getRuntimeId());
session.sendUpstreamPacket(levelEventPacket);
}
private static void sendJavaPlayerActionPacket(GeyserSession session, PlayerAction action, Vector3i vector, Direction direction) {
int sequence = session.getWorldCache().nextPredictionSequence();
ServerboundPlayerActionPacket packet = new ServerboundPlayerActionPacket(action, vector, direction, sequence);
session.sendDownstreamGamePacket(packet);
GeyserImpl.getInstance().getLogger().info(packet.toString());
}
}

View file

@ -30,16 +30,14 @@ import org.cloudburstmc.math.vector.Vector2f;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.data.InputMode;
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
import org.cloudburstmc.protocol.bedrock.data.PlayerActionType;
import org.cloudburstmc.protocol.bedrock.data.PlayerAuthInputData;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.inventory.transaction.ItemUseTransaction;
import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.ItemStackResponsePacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerActionPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.BoatEntity;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.ItemFrameEntity;
@ -47,25 +45,21 @@ import org.geysermc.geyser.entity.type.living.animal.horse.AbstractHorseEntity;
import org.geysermc.geyser.entity.type.living.animal.horse.LlamaEntity;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.translator.protocol.bedrock.BedrockInventoryTransactionTranslator;
import org.geysermc.geyser.util.CooldownUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractAction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerState;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundMoveVehiclePacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerCommandPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;
import java.util.List;
import java.util.Set;
@Translator(packet = PlayerAuthInputPacket.class)
@ -86,6 +80,7 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
for (PlayerAuthInputData input : inputData) {
switch (input) {
case PERFORM_ITEM_INTERACTION -> processItemUseTransaction(session, packet.getItemUseTransaction());
case PERFORM_ITEM_STACK_REQUEST -> session.getInventoryTranslator().translateRequests(session, session.getPlayerInventory(), List.of(packet.getItemStackRequest()));
case PERFORM_BLOCK_ACTIONS -> BedrockBlockActions.translate(session, packet.getPlayerActions());
case START_SPRINTING -> {
if (!entity.getFlag(EntityFlag.SWIMMING)) {
@ -185,51 +180,13 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
private static void processItemUseTransaction(GeyserSession session, ItemUseTransaction transaction) {
if (transaction.getActionType() == 2) {
int blockState = session.getGameMode() == GameMode.CREATIVE ?
session.getGeyser().getWorldManager().getBlockAt(session, transaction.getBlockPosition()) : session.getBreakingBlock();
session.setLastBlockPlaced(null);
session.setLastBlockPlacePosition(null);
// Same deal with vanilla block placing as above.
if (!session.getWorldBorder().isInsideBorderBoundaries()) {
BedrockInventoryTransactionTranslator.restoreCorrectBlock(session, transaction.getBlockPosition());
return;
}
Vector3f playerPosition = session.getPlayerEntity().getPosition();
playerPosition = playerPosition.down(EntityDefinitions.PLAYER.offset() - session.getEyeHeight());
if (!BedrockInventoryTransactionTranslator.canInteractWithBlock(session, playerPosition, transaction.getBlockPosition())) {
BedrockInventoryTransactionTranslator.restoreCorrectBlock(session, transaction.getBlockPosition());
return;
}
int sequence = session.getWorldCache().nextPredictionSequence();
session.getWorldCache().markPositionInSequence(transaction.getBlockPosition());
// -1 means we don't know what block they're breaking
if (blockState == -1) {
blockState = Block.JAVA_AIR_ID;
}
LevelEventPacket blockBreakPacket = new LevelEventPacket();
blockBreakPacket.setType(LevelEvent.PARTICLE_DESTROY_BLOCK);
blockBreakPacket.setPosition(transaction.getBlockPosition().toFloat());
blockBreakPacket.setData(session.getBlockMappings().getBedrockBlockId(blockState));
session.sendUpstreamPacket(blockBreakPacket);
session.setBreakingBlock(-1);
// TODO test
Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, transaction.getBlockPosition());
if (itemFrameEntity != null) {
ServerboundInteractPacket attackPacket = new ServerboundInteractPacket(itemFrameEntity.getEntityId(),
InteractAction.ATTACK, session.isSneaking());
session.sendDownstreamGamePacket(attackPacket);
return;
}
PlayerAction action = session.getGameMode() == GameMode.CREATIVE ? PlayerAction.START_DIGGING : PlayerAction.FINISH_DIGGING;
ServerboundPlayerActionPacket breakPacket = new ServerboundPlayerActionPacket(action, transaction.getBlockPosition(), Direction.VALUES[transaction.getBlockFace()], sequence);
session.sendDownstreamGamePacket(breakPacket);
} else {
session.getGeyser().getLogger().error("Unhandled item use transaction type!");
if (session.getGeyser().getLogger().isDebug()) {

View file

@ -39,7 +39,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.ToolData;
public final class BlockUtils {
/**
* Returns the total mining progress added by mining the block in a single tick
* Returns the total mining progress added (in %) by mining the block in a single tick
* @return the mining progress added by this tick.
*/
public static float getBlockMiningProgressPerTick(GeyserSession session, Block block, GeyserItemStack itemInHand) {