2019-09-29 23:25:42 +00:00
|
|
|
/*
|
2021-01-01 15:10:36 +00:00
|
|
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
2019-09-29 23:25:42 +00:00
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*
|
|
|
|
* @author GeyserMC
|
|
|
|
* @link https://github.com/GeyserMC/Geyser
|
|
|
|
*/
|
|
|
|
|
|
|
|
package org.geysermc.connector.network.translators.bedrock;
|
|
|
|
|
|
|
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
2019-10-27 09:56:47 +00:00
|
|
|
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
2019-09-29 23:25:42 +00:00
|
|
|
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
2019-10-07 18:30:08 +00:00
|
|
|
import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction;
|
2019-09-29 23:25:42 +00:00
|
|
|
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
|
|
|
|
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
|
|
|
|
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
|
2019-10-07 18:30:08 +00:00
|
|
|
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket;
|
2020-05-03 04:06:53 +00:00
|
|
|
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket;
|
2019-09-29 23:25:42 +00:00
|
|
|
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket;
|
2020-04-23 06:01:33 +00:00
|
|
|
import com.nukkitx.math.vector.Vector3f;
|
2020-07-21 17:17:55 +00:00
|
|
|
import com.nukkitx.math.vector.Vector3i;
|
2020-04-29 20:01:53 +00:00
|
|
|
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
2021-05-11 01:14:30 +00:00
|
|
|
import com.nukkitx.protocol.bedrock.data.inventory.*;
|
2021-01-08 03:43:36 +00:00
|
|
|
import com.nukkitx.protocol.bedrock.packet.*;
|
2020-09-15 00:54:19 +00:00
|
|
|
import org.geysermc.connector.entity.CommandBlockMinecartEntity;
|
2020-04-23 06:01:33 +00:00
|
|
|
import org.geysermc.connector.entity.Entity;
|
2020-05-03 04:06:53 +00:00
|
|
|
import org.geysermc.connector.entity.ItemFrameEntity;
|
2020-10-29 22:35:46 +00:00
|
|
|
import org.geysermc.connector.entity.type.EntityType;
|
2020-10-16 23:25:05 +00:00
|
|
|
import org.geysermc.connector.inventory.GeyserItemStack;
|
2020-04-23 06:01:33 +00:00
|
|
|
import org.geysermc.connector.network.session.GeyserSession;
|
|
|
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
|
|
|
import org.geysermc.connector.network.translators.Translator;
|
2020-05-02 06:06:22 +00:00
|
|
|
import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler;
|
2021-07-13 01:19:40 +00:00
|
|
|
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
|
|
|
|
import org.geysermc.connector.registry.BlockRegistries;
|
|
|
|
import org.geysermc.connector.registry.type.ItemMapping;
|
|
|
|
import org.geysermc.connector.registry.type.ItemMappings;
|
2020-10-08 22:44:15 +00:00
|
|
|
import org.geysermc.connector.utils.BlockUtils;
|
2020-04-23 06:01:33 +00:00
|
|
|
|
2020-10-13 00:02:41 +00:00
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
2021-01-08 03:43:36 +00:00
|
|
|
/**
|
|
|
|
* BedrockInventoryTransactionTranslator handles most interactions between the client and the world,
|
|
|
|
* or the client and their inventory.
|
|
|
|
*/
|
2020-03-24 04:24:17 +00:00
|
|
|
@Translator(packet = InventoryTransactionPacket.class)
|
2019-09-29 23:25:42 +00:00
|
|
|
public class BedrockInventoryTransactionTranslator extends PacketTranslator<InventoryTransactionPacket> {
|
|
|
|
|
2021-01-08 03:43:36 +00:00
|
|
|
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;
|
|
|
|
|
2019-09-29 23:25:42 +00:00
|
|
|
@Override
|
2021-08-30 17:55:01 +00:00
|
|
|
public void translate(GeyserSession session, InventoryTransactionPacket packet) {
|
2021-01-08 00:40:34 +00:00
|
|
|
// Send book updates before opening inventories
|
|
|
|
session.getBookEditCache().checkForSend();
|
|
|
|
|
2021-07-13 01:19:40 +00:00
|
|
|
ItemMappings mappings = session.getItemMappings();
|
|
|
|
|
2019-09-29 23:25:42 +00:00
|
|
|
switch (packet.getTransactionType()) {
|
2019-10-20 21:25:41 +00:00
|
|
|
case NORMAL:
|
2020-10-16 23:25:05 +00:00
|
|
|
if (packet.getActions().size() == 2) {
|
|
|
|
InventoryActionData worldAction = packet.getActions().get(0);
|
|
|
|
InventoryActionData containerAction = packet.getActions().get(1);
|
|
|
|
if (worldAction.getSource().getType() == InventorySource.Type.WORLD_INTERACTION
|
|
|
|
&& worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
|
2021-08-17 00:39:29 +00:00
|
|
|
if (session.getPlayerInventory().getHeldItemSlot() != containerAction.getSlot() ||
|
|
|
|
session.getPlayerInventory().getItemInHand().isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
2020-10-16 23:25:05 +00:00
|
|
|
|
2021-08-17 00:39:29 +00:00
|
|
|
boolean dropAll = worldAction.getToItem().getCount() > 1;
|
|
|
|
ClientPlayerActionPacket dropAllPacket = new ClientPlayerActionPacket(
|
|
|
|
dropAll ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM,
|
|
|
|
BlockUtils.POSITION_ZERO,
|
|
|
|
BlockFace.DOWN
|
|
|
|
);
|
|
|
|
session.sendDownstreamPacket(dropAllPacket);
|
|
|
|
|
|
|
|
if (dropAll) {
|
|
|
|
session.getPlayerInventory().setItemInHand(GeyserItemStack.EMPTY);
|
|
|
|
} else {
|
|
|
|
session.getPlayerInventory().getItemInHand().sub(1);
|
|
|
|
}
|
2020-10-16 23:25:05 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-20 21:25:41 +00:00
|
|
|
break;
|
|
|
|
case INVENTORY_MISMATCH:
|
2019-11-28 03:55:58 +00:00
|
|
|
break;
|
2019-09-29 23:25:42 +00:00
|
|
|
case ITEM_USE:
|
2020-04-10 18:46:29 +00:00
|
|
|
switch (packet.getActionType()) {
|
|
|
|
case 0:
|
2020-09-24 16:54:18 +00:00
|
|
|
// Check to make sure the client isn't spamming interaction
|
|
|
|
// Based on Nukkit 1.0, with changes to ensure holding down still works
|
|
|
|
boolean hasAlreadyClicked = System.currentTimeMillis() - session.getLastInteractionTime() < 110.0 &&
|
2021-02-12 19:39:41 +00:00
|
|
|
packet.getBlockPosition().distanceSquared(session.getLastInteractionBlockPosition()) < 0.00001;
|
|
|
|
session.setLastInteractionBlockPosition(packet.getBlockPosition());
|
|
|
|
session.setLastInteractionPlayerPosition(session.getPlayerEntity().getPosition());
|
2020-09-24 16:54:18 +00:00
|
|
|
if (hasAlreadyClicked) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
// Only update the interaction time if it's valid - that way holding down still works.
|
|
|
|
session.setLastInteractionTime(System.currentTimeMillis());
|
|
|
|
}
|
2020-05-02 20:44:05 +00:00
|
|
|
|
|
|
|
// Bedrock sends block interact code for a Java entity so we send entity code back to Java
|
2021-07-13 01:19:40 +00:00
|
|
|
if (session.getBlockMappings().isItemFrame(packet.getBlockRuntimeId())) {
|
2021-05-09 19:44:41 +00:00
|
|
|
Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
|
|
|
|
if (itemFrameEntity != null) {
|
|
|
|
int entityId = (int) itemFrameEntity.getEntityId();
|
|
|
|
Vector3f vector = packet.getClickPosition();
|
|
|
|
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket(entityId,
|
|
|
|
InteractAction.INTERACT, Hand.MAIN_HAND, session.isSneaking());
|
|
|
|
ClientPlayerInteractEntityPacket interactAtPacket = new ClientPlayerInteractEntityPacket(entityId,
|
|
|
|
InteractAction.INTERACT_AT, vector.getX(), vector.getY(), vector.getZ(), Hand.MAIN_HAND, session.isSneaking());
|
|
|
|
session.sendDownstreamPacket(interactPacket);
|
|
|
|
session.sendDownstreamPacket(interactAtPacket);
|
|
|
|
break;
|
|
|
|
}
|
2020-05-02 20:44:05 +00:00
|
|
|
}
|
|
|
|
|
2021-01-08 03:43:36 +00:00
|
|
|
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();
|
|
|
|
|
|
|
|
// Adjust position for current eye height
|
2021-04-12 04:35:53 +00:00
|
|
|
switch (session.getPose()) {
|
|
|
|
case SNEAKING:
|
|
|
|
playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 1.27f), 0);
|
|
|
|
break;
|
|
|
|
case SWIMMING:
|
|
|
|
case FALL_FLYING: // Elytra
|
|
|
|
case SPIN_ATTACK: // Trident spin attack
|
|
|
|
playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 0.4f), 0);
|
|
|
|
break;
|
|
|
|
case SLEEPING:
|
|
|
|
playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 0.2f), 0);
|
|
|
|
break;
|
2021-01-08 03:43:36 +00:00
|
|
|
} // 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
|
|
|
|
*/
|
|
|
|
|
2021-07-13 01:19:40 +00:00
|
|
|
if (packet.getItemInHand() != null && session.getItemMappings().getSpawnEggIds().contains(packet.getItemInHand().getId())) {
|
2021-06-20 00:33:07 +00:00
|
|
|
int blockState = session.getConnector().getWorldManager().getBlockAt(session, packet.getBlockPosition());
|
2021-07-13 01:19:40 +00:00
|
|
|
if (blockState == BlockStateValues.JAVA_WATER_ID) {
|
2021-06-20 00:33:07 +00:00
|
|
|
// Otherwise causes multiple mobs to spawn - just send a use item packet
|
|
|
|
// TODO when we fix mobile bucket rotation, use it for this, too
|
|
|
|
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
|
|
|
session.sendDownstreamPacket(itemPacket);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-10 18:46:29 +00:00
|
|
|
ClientPlayerPlaceBlockPacket blockPacket = new ClientPlayerPlaceBlockPacket(
|
|
|
|
new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()),
|
2020-06-23 00:11:09 +00:00
|
|
|
BlockFace.values()[packet.getBlockFace()],
|
2020-04-10 18:46:29 +00:00
|
|
|
Hand.MAIN_HAND,
|
|
|
|
packet.getClickPosition().getX(), packet.getClickPosition().getY(), packet.getClickPosition().getZ(),
|
|
|
|
false);
|
2020-05-05 15:51:43 +00:00
|
|
|
session.sendDownstreamPacket(blockPacket);
|
2020-05-23 21:39:17 +00:00
|
|
|
|
2021-06-20 00:33:07 +00:00
|
|
|
if (packet.getItemInHand() != null) {
|
|
|
|
// Otherwise boats will not be able to be placed in survival and buckets won't work on mobile
|
2021-07-13 01:19:40 +00:00
|
|
|
if (session.getItemMappings().getBoatIds().contains(packet.getItemInHand().getId())) {
|
2020-10-13 00:02:41 +00:00
|
|
|
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
|
|
|
session.sendDownstreamPacket(itemPacket);
|
2021-08-02 02:20:15 +00:00
|
|
|
} else if (session.getItemMappings().getBucketIds().contains(packet.getItemInHand().getId())) {
|
2021-06-20 00:33:07 +00:00
|
|
|
// Let the server decide if the bucket item should change, not the client, and revert the changes the client made
|
|
|
|
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
|
|
|
slotPacket.setContainerId(ContainerId.INVENTORY);
|
|
|
|
slotPacket.setSlot(packet.getHotbarSlot());
|
|
|
|
slotPacket.setItem(packet.getItemInHand());
|
|
|
|
session.sendUpstreamPacket(slotPacket);
|
2021-08-02 02:20:15 +00:00
|
|
|
// Don't send ClientPlayerUseItemPacket for powder snow buckets
|
|
|
|
if (packet.getItemInHand().getId() != session.getItemMappings().getStoredItems().powderSnowBucket().getBedrockId()) {
|
|
|
|
// Special check for crafting tables since clients don't send BLOCK_INTERACT when interacting
|
|
|
|
int blockState = session.getConnector().getWorldManager().getBlockAt(session, packet.getBlockPosition());
|
|
|
|
if (session.isSneaking() || blockState != BlockRegistries.JAVA_IDENTIFIERS.get("minecraft:crafting_table")) {
|
|
|
|
// Delay the interaction in case the client doesn't intend to actually use the bucket
|
|
|
|
// See BedrockActionTranslator.java
|
2021-08-18 00:57:46 +00:00
|
|
|
session.setBucketScheduledFuture(session.scheduleInEventLoop(() -> {
|
2021-08-02 02:20:15 +00:00
|
|
|
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
|
|
|
session.sendDownstreamPacket(itemPacket);
|
|
|
|
}, 5, TimeUnit.MILLISECONDS));
|
|
|
|
}
|
|
|
|
}
|
2021-06-20 00:33:07 +00:00
|
|
|
}
|
2020-07-21 17:17:55 +00:00
|
|
|
}
|
2020-05-23 21:39:17 +00:00
|
|
|
|
2020-09-15 00:54:19 +00:00
|
|
|
if (packet.getActions().isEmpty()) {
|
|
|
|
if (session.getOpPermissionLevel() >= 2 && session.getGameMode() == GameMode.CREATIVE) {
|
|
|
|
// Otherwise insufficient permissions
|
2021-07-13 01:19:40 +00:00
|
|
|
int blockState = session.getBlockMappings().getJavaBlockState(packet.getBlockRuntimeId());
|
2021-07-18 19:43:17 +00:00
|
|
|
String blockName = BlockRegistries.JAVA_IDENTIFIERS.get().getOrDefault(blockState, "");
|
2020-09-15 00:54:19 +00:00
|
|
|
// In the future this can be used for structure blocks too, however not all elements
|
|
|
|
// are available in each GUI
|
|
|
|
if (blockName.contains("jigsaw")) {
|
|
|
|
ContainerOpenPacket openPacket = new ContainerOpenPacket();
|
|
|
|
openPacket.setBlockPosition(packet.getBlockPosition());
|
|
|
|
openPacket.setId((byte) 1);
|
|
|
|
openPacket.setType(ContainerType.JIGSAW_EDITOR);
|
|
|
|
openPacket.setUniqueEntityId(-1);
|
|
|
|
session.sendUpstreamPacket(openPacket);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-13 01:19:40 +00:00
|
|
|
ItemMapping handItem = mappings.getMapping(packet.getItemInHand());
|
2020-04-23 06:01:33 +00:00
|
|
|
if (handItem.isBlock()) {
|
2020-04-29 20:01:53 +00:00
|
|
|
session.setLastBlockPlacePosition(blockPos);
|
2020-04-23 06:01:33 +00:00
|
|
|
session.setLastBlockPlacedId(handItem.getJavaIdentifier());
|
|
|
|
}
|
2020-04-30 05:21:02 +00:00
|
|
|
session.setInteracting(true);
|
2020-04-10 18:46:29 +00:00
|
|
|
break;
|
|
|
|
case 1:
|
2021-05-11 01:14:30 +00:00
|
|
|
if (packet.getActions().size() == 1 && packet.getLegacySlots().size() > 0) {
|
2021-05-09 05:25:57 +00:00
|
|
|
InventoryActionData actionData = packet.getActions().get(0);
|
2021-05-11 01:14:30 +00:00
|
|
|
LegacySetItemSlotData slotData = packet.getLegacySlots().get(0);
|
|
|
|
if (slotData.getContainerId() == 6 && actionData.getToItem().getId() != 0) {
|
2021-05-09 05:25:57 +00:00
|
|
|
// The player is trying to swap out an armor piece that already has an item in it
|
|
|
|
// Java Edition does not allow this; let's revert it
|
|
|
|
session.getInventoryTranslator().updateInventory(session, session.getPlayerInventory());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handled when sneaking
|
2021-07-13 01:19:40 +00:00
|
|
|
if (session.getPlayerInventory().getItemInHand().getJavaId() == mappings.getStoredItems().shield().getJavaId()) {
|
2020-04-25 03:11:28 +00:00
|
|
|
break;
|
2020-07-21 17:17:55 +00:00
|
|
|
}
|
|
|
|
|
2021-07-13 01:19:40 +00:00
|
|
|
// Handled in ITEM_USE if the item is not milk
|
2021-06-20 00:33:07 +00:00
|
|
|
if (packet.getItemInHand() != null) {
|
2021-07-13 01:19:40 +00:00
|
|
|
if (session.getItemMappings().getBucketIds().contains(packet.getItemInHand().getId()) &&
|
|
|
|
packet.getItemInHand().getId() != session.getItemMappings().getStoredItems().milkBucket().getBedrockId()) {
|
2021-06-20 00:33:07 +00:00
|
|
|
// Handled in case 0 if the item is not milk
|
|
|
|
break;
|
2021-07-13 01:19:40 +00:00
|
|
|
} else if (session.getItemMappings().getSpawnEggIds().contains(packet.getItemInHand().getId())) {
|
2021-06-20 00:33:07 +00:00
|
|
|
// Handled in case 0
|
|
|
|
break;
|
|
|
|
}
|
2020-07-21 17:17:55 +00:00
|
|
|
}
|
|
|
|
|
2020-04-10 18:46:29 +00:00
|
|
|
ClientPlayerUseItemPacket useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
2020-05-05 15:51:43 +00:00
|
|
|
session.sendDownstreamPacket(useItemPacket);
|
2020-04-10 18:46:29 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
2021-01-08 03:43:36 +00:00
|
|
|
int blockState = session.getGameMode() == GameMode.CREATIVE ?
|
|
|
|
session.getConnector().getWorldManager().getBlockAt(session, packet.getBlockPosition()) : session.getBreakingBlock();
|
2020-04-29 20:01:53 +00:00
|
|
|
|
2021-01-08 03:43:36 +00:00
|
|
|
session.setLastBlockPlacedId(null);
|
|
|
|
session.setLastBlockPlacePosition(null);
|
|
|
|
|
|
|
|
// Same deal with vanilla block placing as above.
|
|
|
|
// 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;
|
2020-04-29 20:01:53 +00:00
|
|
|
}
|
2020-05-02 20:44:05 +00:00
|
|
|
|
2021-01-08 03:43:36 +00:00
|
|
|
LevelEventPacket blockBreakPacket = new LevelEventPacket();
|
|
|
|
blockBreakPacket.setType(LevelEventType.PARTICLE_DESTROY_BLOCK);
|
|
|
|
blockBreakPacket.setPosition(packet.getBlockPosition().toFloat());
|
2021-07-13 01:19:40 +00:00
|
|
|
blockBreakPacket.setData(session.getBlockMappings().getBedrockBlockId(blockState));
|
2021-01-08 03:43:36 +00:00
|
|
|
session.sendUpstreamPacket(blockBreakPacket);
|
2021-07-13 01:19:40 +00:00
|
|
|
session.setBreakingBlock(BlockStateValues.JAVA_AIR_ID);
|
2021-01-08 03:43:36 +00:00
|
|
|
|
2021-05-09 19:44:41 +00:00
|
|
|
Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
|
|
|
|
if (itemFrameEntity != null) {
|
|
|
|
ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) itemFrameEntity.getEntityId(),
|
|
|
|
InteractAction.ATTACK, session.isSneaking());
|
2020-05-05 15:51:43 +00:00
|
|
|
session.sendDownstreamPacket(attackPacket);
|
2020-05-02 20:44:05 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-04-10 18:46:29 +00:00
|
|
|
PlayerAction action = session.getGameMode() == GameMode.CREATIVE ? PlayerAction.START_DIGGING : PlayerAction.FINISH_DIGGING;
|
|
|
|
Position pos = new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ());
|
2020-06-23 00:11:09 +00:00
|
|
|
ClientPlayerActionPacket breakPacket = new ClientPlayerActionPacket(action, pos, BlockFace.values()[packet.getBlockFace()]);
|
2020-05-05 15:51:43 +00:00
|
|
|
session.sendDownstreamPacket(breakPacket);
|
2020-04-10 18:46:29 +00:00
|
|
|
break;
|
2019-09-29 23:25:42 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ITEM_RELEASE:
|
2019-09-29 23:39:03 +00:00
|
|
|
if (packet.getActionType() == 0) {
|
2020-04-09 02:17:29 +00:00
|
|
|
// Followed to the Minecraft Protocol specification outlined at wiki.vg
|
2021-03-18 04:53:14 +00:00
|
|
|
ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, BlockUtils.POSITION_ZERO,
|
2020-04-09 01:29:33 +00:00
|
|
|
BlockFace.DOWN);
|
2020-05-05 15:51:43 +00:00
|
|
|
session.sendDownstreamPacket(releaseItemPacket);
|
2019-09-29 23:39:03 +00:00
|
|
|
}
|
2019-09-29 23:25:42 +00:00
|
|
|
break;
|
2019-10-07 18:30:08 +00:00
|
|
|
case ITEM_USE_ON_ENTITY:
|
2019-10-16 20:32:53 +00:00
|
|
|
Entity entity = session.getEntityCache().getEntityByGeyserId(packet.getRuntimeEntityId());
|
|
|
|
if (entity == null)
|
|
|
|
return;
|
2019-10-07 18:30:08 +00:00
|
|
|
|
2020-04-18 09:13:00 +00:00
|
|
|
//https://wiki.vg/Protocol#Interact_Entity
|
|
|
|
switch (packet.getActionType()) {
|
|
|
|
case 0: //Interact
|
2020-09-15 00:54:19 +00:00
|
|
|
if (entity instanceof CommandBlockMinecartEntity) {
|
|
|
|
// The UI is handled client-side on Java Edition
|
|
|
|
// Ensure OP permission level and gamemode is appropriate
|
|
|
|
if (session.getOpPermissionLevel() < 2 || session.getGameMode() != GameMode.CREATIVE) return;
|
|
|
|
ContainerOpenPacket openPacket = new ContainerOpenPacket();
|
|
|
|
openPacket.setBlockPosition(Vector3i.ZERO);
|
|
|
|
openPacket.setId((byte) 1);
|
|
|
|
openPacket.setType(ContainerType.COMMAND_BLOCK);
|
|
|
|
openPacket.setUniqueEntityId(entity.getGeyserId());
|
|
|
|
session.sendUpstreamPacket(openPacket);
|
|
|
|
break;
|
|
|
|
}
|
2021-04-30 22:35:45 +00:00
|
|
|
Vector3f vector = packet.getClickPosition().sub(entity.getPosition());
|
2020-04-18 09:13:00 +00:00
|
|
|
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
|
2020-06-21 02:24:45 +00:00
|
|
|
InteractAction.INTERACT, Hand.MAIN_HAND, session.isSneaking());
|
2020-04-18 09:13:00 +00:00
|
|
|
ClientPlayerInteractEntityPacket interactAtPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
|
2020-06-21 02:24:45 +00:00
|
|
|
InteractAction.INTERACT_AT, vector.getX(), vector.getY(), vector.getZ(), Hand.MAIN_HAND, session.isSneaking());
|
2020-05-05 15:51:43 +00:00
|
|
|
session.sendDownstreamPacket(interactPacket);
|
|
|
|
session.sendDownstreamPacket(interactAtPacket);
|
2020-05-02 06:06:22 +00:00
|
|
|
|
2021-04-30 22:35:45 +00:00
|
|
|
EntitySoundInteractionHandler.handleEntityInteraction(session, packet.getClickPosition(), entity);
|
2020-04-18 09:13:00 +00:00
|
|
|
break;
|
|
|
|
case 1: //Attack
|
2020-10-29 22:35:46 +00:00
|
|
|
if (entity.getEntityType() == EntityType.ENDER_DRAGON) {
|
|
|
|
// Redirects the attack to its body entity, this only happens when
|
|
|
|
// attacking the underbelly of the ender dragon
|
|
|
|
ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId() + 3,
|
|
|
|
InteractAction.ATTACK, session.isSneaking());
|
|
|
|
session.sendDownstreamPacket(attackPacket);
|
|
|
|
} else {
|
|
|
|
ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
|
|
|
|
InteractAction.ATTACK, session.isSneaking());
|
|
|
|
session.sendDownstreamPacket(attackPacket);
|
|
|
|
}
|
2020-04-18 09:13:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-10-16 20:32:53 +00:00
|
|
|
break;
|
2019-09-29 23:25:42 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-08 03:43:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
2021-07-13 01:19:40 +00:00
|
|
|
updateBlockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(javaBlockState));
|
2021-01-08 03:43:36 +00:00
|
|
|
updateBlockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
|
|
|
|
session.sendUpstreamPacket(updateBlockPacket);
|
|
|
|
|
|
|
|
UpdateBlockPacket updateWaterPacket = new UpdateBlockPacket();
|
|
|
|
updateWaterPacket.setDataLayer(1);
|
|
|
|
updateWaterPacket.setBlockPosition(blockPos);
|
2021-07-13 01:19:40 +00:00
|
|
|
updateWaterPacket.setRuntimeId(BlockRegistries.WATERLOGGED.get().contains(javaBlockState) ? session.getBlockMappings().getBedrockWaterId() : session.getBlockMappings().getBedrockAirId());
|
2021-01-08 03:43:36 +00:00
|
|
|
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);
|
|
|
|
}
|
2019-09-29 23:25:42 +00:00
|
|
|
}
|