2019-08-10 00:15:06 +00:00
/ *
2022-01-01 19:03:05 +00:00
* Copyright ( c ) 2019 - 2022 GeyserMC . http : //geysermc.org
2019-08-10 00:15:06 +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
* /
2021-11-20 23:29:46 +00:00
package org.geysermc.geyser.translator.protocol.bedrock.entity.player ;
2019-08-10 00:15:06 +00:00
2021-11-18 03:02:38 +00:00
import com.github.steveice10.mc.protocol.data.game.entity.object.Direction ;
2022-10-30 03:02:11 +00:00
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode ;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand ;
import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction ;
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction ;
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerState ;
2023-07-21 23:25:01 +00:00
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.* ;
2022-10-30 00:23:21 +00:00
import org.cloudburstmc.math.vector.Vector3f ;
import org.cloudburstmc.math.vector.Vector3i ;
2022-10-30 03:02:11 +00:00
import org.cloudburstmc.protocol.bedrock.data.LevelEvent ;
2022-10-30 00:23:21 +00:00
import org.cloudburstmc.protocol.bedrock.data.PlayerActionType ;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType ;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag ;
2023-07-21 23:25:01 +00:00
import org.cloudburstmc.protocol.bedrock.packet.* ;
2021-11-20 23:29:46 +00:00
import org.geysermc.geyser.entity.type.Entity ;
import org.geysermc.geyser.entity.type.ItemFrameEntity ;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity ;
2022-09-27 23:24:50 +00:00
import org.geysermc.geyser.level.block.BlockStateValues ;
2021-11-20 21:34:30 +00:00
import org.geysermc.geyser.registry.BlockRegistries ;
2023-04-07 01:47:37 +00:00
import org.geysermc.geyser.registry.type.BlockMapping ;
2021-12-06 00:03:47 +00:00
import org.geysermc.geyser.session.GeyserSession ;
import org.geysermc.geyser.translator.protocol.PacketTranslator ;
import org.geysermc.geyser.translator.protocol.Translator ;
2021-11-20 23:29:46 +00:00
import org.geysermc.geyser.util.BlockUtils ;
2020-02-16 18:40:54 +00:00
2020-03-24 04:24:17 +00:00
@Translator ( packet = PlayerActionPacket . class )
2019-08-10 00:15:06 +00:00
public class BedrockActionTranslator extends PacketTranslator < PlayerActionPacket > {
@Override
2021-11-22 19:52:26 +00:00
public void translate ( GeyserSession session , PlayerActionPacket packet ) {
2021-07-08 02:44:53 +00:00
SessionPlayerEntity entity = session . getPlayerEntity ( ) ;
2019-09-14 01:12:36 +00:00
2021-01-08 00:40:34 +00:00
// Send book update before any player action
2021-02-17 23:00:53 +00:00
if ( packet . getAction ( ) ! = PlayerActionType . RESPAWN ) {
2021-01-08 00:40:34 +00:00
session . getBookEditCache ( ) . checkForSend ( ) ;
}
2019-09-27 20:50:48 +00:00
Vector3i vector = packet . getBlockPosition ( ) ;
2019-08-10 00:15:06 +00:00
switch ( packet . getAction ( ) ) {
case RESPAWN :
2020-07-30 20:31:12 +00:00
// Respawn process is finished and the server and client are both OK with respawning.
EntityEventPacket eventPacket = new EntityEventPacket ( ) ;
eventPacket . setRuntimeEntityId ( entity . getGeyserId ( ) ) ;
eventPacket . setType ( EntityEventType . RESPAWN ) ;
eventPacket . setData ( 0 ) ;
session . sendUpstreamPacket ( eventPacket ) ;
2020-10-29 20:44:45 +00:00
// Resend attributes or else in rare cases the user can think they're not dead when they are, upon joining the server
2021-07-08 02:44:53 +00:00
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket ( ) ;
attributesPacket . setRuntimeEntityId ( entity . getGeyserId ( ) ) ;
2021-12-06 00:03:47 +00:00
attributesPacket . getAttributes ( ) . addAll ( entity . getAttributes ( ) . values ( ) ) ;
2021-07-08 02:44:53 +00:00
session . sendUpstreamPacket ( attributesPacket ) ;
2022-10-30 00:08:41 +00:00
// Bounding box must be sent after a player dies and respawns since 1.19.40
entity . updateBoundingBox ( ) ;
2023-05-09 05:41:57 +00:00
// Needed here since 1.19.81 for dimension switching
session . getEntityCache ( ) . updateBossBars ( ) ;
2019-08-10 00:15:06 +00:00
break ;
2020-02-15 10:18:41 +00:00
case START_SWIMMING :
2021-12-26 03:46:16 +00:00
if ( ! entity . getFlag ( EntityFlag . SWIMMING ) ) {
2021-12-21 00:25:11 +00:00
ServerboundPlayerCommandPacket startSwimPacket = new ServerboundPlayerCommandPacket ( entity . getEntityId ( ) , PlayerState . START_SPRINTING ) ;
2021-12-18 16:43:57 +00:00
session . sendDownstreamPacket ( startSwimPacket ) ;
2021-12-26 03:46:16 +00:00
session . setSwimming ( true ) ;
2021-12-18 16:43:57 +00:00
}
2020-02-15 10:18:41 +00:00
break ;
case STOP_SWIMMING :
2021-12-18 16:43:57 +00:00
// Prevent packet spam when Bedrock players are crawling near the edge of a block
2021-12-26 03:46:16 +00:00
if ( ! session . getCollisionManager ( ) . mustPlayerCrawlHere ( ) ) {
2021-12-21 00:25:11 +00:00
ServerboundPlayerCommandPacket stopSwimPacket = new ServerboundPlayerCommandPacket ( entity . getEntityId ( ) , PlayerState . STOP_SPRINTING ) ;
2021-12-18 16:43:57 +00:00
session . sendDownstreamPacket ( stopSwimPacket ) ;
2021-12-26 03:46:16 +00:00
session . setSwimming ( false ) ;
2021-12-18 16:43:57 +00:00
}
2020-02-15 10:18:41 +00:00
break ;
2019-09-14 01:12:36 +00:00
case START_GLIDE :
2020-07-18 20:57:37 +00:00
// Otherwise gliding will not work in creative
2021-11-13 03:44:15 +00:00
ServerboundPlayerAbilitiesPacket playerAbilitiesPacket = new ServerboundPlayerAbilitiesPacket ( false ) ;
2020-07-18 20:57:37 +00:00
session . sendDownstreamPacket ( playerAbilitiesPacket ) ;
2019-09-14 01:12:36 +00:00
case STOP_GLIDE :
2021-12-21 00:25:11 +00:00
ServerboundPlayerCommandPacket glidePacket = new ServerboundPlayerCommandPacket ( entity . getEntityId ( ) , PlayerState . START_ELYTRA_FLYING ) ;
2020-05-05 15:51:43 +00:00
session . sendDownstreamPacket ( glidePacket ) ;
2019-09-14 01:12:36 +00:00
break ;
case START_SNEAK :
2021-12-21 00:25:11 +00:00
ServerboundPlayerCommandPacket startSneakPacket = new ServerboundPlayerCommandPacket ( entity . getEntityId ( ) , PlayerState . START_SNEAKING ) ;
2020-05-05 15:51:43 +00:00
session . sendDownstreamPacket ( startSneakPacket ) ;
2021-03-18 04:53:14 +00:00
2022-03-15 17:34:56 +00:00
session . startSneaking ( ) ;
2019-09-14 01:12:36 +00:00
break ;
case STOP_SNEAK :
2021-12-21 00:25:11 +00:00
ServerboundPlayerCommandPacket stopSneakPacket = new ServerboundPlayerCommandPacket ( entity . getEntityId ( ) , PlayerState . STOP_SNEAKING ) ;
2020-05-05 15:51:43 +00:00
session . sendDownstreamPacket ( stopSneakPacket ) ;
2021-03-18 04:53:14 +00:00
2022-03-15 17:34:56 +00:00
session . stopSneaking ( ) ;
2019-09-14 01:12:36 +00:00
break ;
case START_SPRINT :
2021-12-26 03:46:16 +00:00
if ( ! entity . getFlag ( EntityFlag . SWIMMING ) ) {
2021-12-21 00:25:11 +00:00
ServerboundPlayerCommandPacket startSprintPacket = new ServerboundPlayerCommandPacket ( entity . getEntityId ( ) , PlayerState . START_SPRINTING ) ;
2021-12-18 16:43:57 +00:00
session . sendDownstreamPacket ( startSprintPacket ) ;
session . setSprinting ( true ) ;
}
2019-09-14 01:12:36 +00:00
break ;
case STOP_SPRINT :
2021-12-26 03:46:16 +00:00
if ( ! entity . getFlag ( EntityFlag . SWIMMING ) ) {
2021-12-21 00:25:11 +00:00
ServerboundPlayerCommandPacket stopSprintPacket = new ServerboundPlayerCommandPacket ( entity . getEntityId ( ) , PlayerState . STOP_SPRINTING ) ;
2021-12-18 16:43:57 +00:00
session . sendDownstreamPacket ( stopSprintPacket ) ;
}
2020-02-16 18:40:54 +00:00
session . setSprinting ( false ) ;
2019-09-14 01:12:36 +00:00
break ;
case DROP_ITEM :
2022-05-25 19:55:15 +00:00
ServerboundPlayerActionPacket dropItemPacket = new ServerboundPlayerActionPacket ( PlayerAction . DROP_ITEM ,
2022-09-27 23:24:50 +00:00
vector , Direction . VALUES [ packet . getFace ( ) ] , 0 ) ;
2020-05-05 15:51:43 +00:00
session . sendDownstreamPacket ( dropItemPacket ) ;
2019-09-14 01:12:36 +00:00
break ;
case STOP_SLEEP :
2021-12-21 00:25:11 +00:00
ServerboundPlayerCommandPacket stopSleepingPacket = new ServerboundPlayerCommandPacket ( entity . getEntityId ( ) , PlayerState . LEAVE_BED ) ;
2020-05-05 15:51:43 +00:00
session . sendDownstreamPacket ( stopSleepingPacket ) ;
2019-09-14 01:12:36 +00:00
break ;
2023-07-30 20:48:29 +00:00
case START_BREAK : {
2021-06-01 19:36:33 +00:00
// Start the block breaking animation
2023-07-30 20:48:29 +00:00
// 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 ;
2021-06-01 19:36:33 +00:00
}
2021-01-08 03:43:36 +00:00
2023-07-30 20:48:29 +00:00
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 . getSessionBreakTime ( session , BlockRegistries . JAVA_BLOCKS . get ( blockState ) ) * 20 ;
startBreak . setData ( ( int ) ( 65535 / breakTime ) ) ;
session . setBreakingBlock ( blockState ) ;
session . sendUpstreamPacket ( startBreak ) ;
2021-06-01 19:36:33 +00:00
// Account for fire - the client likes to hit the block behind.
2021-12-06 00:03:47 +00:00
Vector3i fireBlockPos = BlockUtils . getBlockPosition ( vector , packet . getFace ( ) ) ;
2021-11-20 21:34:30 +00:00
int blockUp = session . getGeyser ( ) . getWorldManager ( ) . getBlockAt ( session , fireBlockPos ) ;
2023-04-07 01:47:37 +00:00
String identifier = BlockRegistries . JAVA_BLOCKS . getOrDefault ( blockUp , BlockMapping . AIR ) . getJavaIdentifier ( ) ;
2021-06-01 19:36:33 +00:00
if ( identifier . startsWith ( " minecraft:fire " ) | | identifier . startsWith ( " minecraft:soul_fire " ) ) {
2022-05-25 23:17:49 +00:00
ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket ( PlayerAction . START_DIGGING , fireBlockPos ,
2022-07-10 03:02:19 +00:00
Direction . VALUES [ packet . getFace ( ) ] , session . getWorldCache ( ) . nextPredictionSequence ( ) ) ;
2021-06-01 19:36:33 +00:00
session . sendDownstreamPacket ( startBreakingPacket ) ;
2020-08-08 22:59:03 +00:00
}
2021-06-01 19:36:33 +00:00
2022-05-25 19:55:15 +00:00
ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket ( PlayerAction . START_DIGGING ,
2022-07-10 03:02:19 +00:00
vector , Direction . VALUES [ packet . getFace ( ) ] , session . getWorldCache ( ) . nextPredictionSequence ( ) ) ;
2020-05-05 15:51:43 +00:00
session . sendDownstreamPacket ( startBreakingPacket ) ;
2019-09-30 22:17:36 +00:00
break ;
2023-07-30 20:48:29 +00:00
}
2019-09-30 22:17:36 +00:00
case CONTINUE_BREAK :
2021-01-08 03:43:36 +00:00
if ( session . getGameMode ( ) = = GameMode . CREATIVE ) {
break ;
}
2022-07-10 03:02:19 +00:00
int breakingBlock = session . getBreakingBlock ( ) ;
if ( breakingBlock = = - 1 ) {
2022-09-27 23:24:50 +00:00
breakingBlock = BlockStateValues . JAVA_AIR_ID ;
2022-07-10 03:02:19 +00:00
}
2021-06-28 23:26:02 +00:00
Vector3f vectorFloat = vector . toFloat ( ) ;
2020-04-23 04:40:49 +00:00
LevelEventPacket continueBreakPacket = new LevelEventPacket ( ) ;
2022-10-30 03:02:11 +00:00
continueBreakPacket . setType ( LevelEvent . PARTICLE_CRACK_BLOCK ) ;
2022-07-10 03:02:19 +00:00
continueBreakPacket . setData ( ( session . getBlockMappings ( ) . getBedrockBlockId ( breakingBlock ) ) | ( packet . getFace ( ) < < 24 ) ) ;
2021-06-28 23:26:02 +00:00
continueBreakPacket . setPosition ( vectorFloat ) ;
2020-05-05 15:51:43 +00:00
session . sendUpstreamPacket ( continueBreakPacket ) ;
2021-06-28 23:26:02 +00:00
// Update the break time in the event that player conditions changed (jumping, effects applied)
LevelEventPacket updateBreak = new LevelEventPacket ( ) ;
2022-10-30 03:02:11 +00:00
updateBreak . setType ( LevelEvent . BLOCK_UPDATE_BREAK ) ;
2021-06-28 23:26:02 +00:00
updateBreak . setPosition ( vectorFloat ) ;
2022-07-10 03:02:19 +00:00
double breakTime = BlockUtils . getSessionBreakTime ( session , BlockRegistries . JAVA_BLOCKS . get ( breakingBlock ) ) * 20 ;
2021-06-28 23:26:02 +00:00
updateBreak . setData ( ( int ) ( 65535 / breakTime ) ) ;
session . sendUpstreamPacket ( updateBreak ) ;
2019-09-27 23:04:58 +00:00
break ;
2019-09-27 21:38:52 +00:00
case ABORT_BREAK :
2021-03-12 02:11:46 +00:00
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
2021-12-06 00:03:47 +00:00
Entity itemFrameEntity = ItemFrameEntity . getItemFrameEntity ( session , vector ) ;
2021-05-09 19:44:41 +00:00
if ( itemFrameEntity ! = null ) {
2021-12-21 00:25:11 +00:00
ServerboundInteractPacket interactPacket = new ServerboundInteractPacket ( itemFrameEntity . getEntityId ( ) ,
2021-03-12 02:11:46 +00:00
InteractAction . ATTACK , Hand . MAIN_HAND , session . isSneaking ( ) ) ;
session . sendDownstreamPacket ( interactPacket ) ;
break ;
}
}
2022-09-27 23:24:50 +00:00
ServerboundPlayerActionPacket abortBreakingPacket = new ServerboundPlayerActionPacket ( PlayerAction . CANCEL_DIGGING , vector , Direction . DOWN , 0 ) ;
2020-05-05 15:51:43 +00:00
session . sendDownstreamPacket ( abortBreakingPacket ) ;
2021-01-08 03:43:36 +00:00
LevelEventPacket stopBreak = new LevelEventPacket ( ) ;
2022-10-30 03:02:11 +00:00
stopBreak . setType ( LevelEvent . BLOCK_STOP_BREAK ) ;
2021-01-08 03:43:36 +00:00
stopBreak . setPosition ( vector . toFloat ( ) ) ;
stopBreak . setData ( 0 ) ;
2022-07-10 03:02:19 +00:00
session . setBreakingBlock ( - 1 ) ;
2021-01-08 03:43:36 +00:00
session . sendUpstreamPacket ( stopBreak ) ;
2019-09-27 23:04:58 +00:00
break ;
2019-09-30 22:17:36 +00:00
case STOP_BREAK :
2019-10-27 09:56:47 +00:00
// Handled in BedrockInventoryTransactionTranslator
2019-09-27 23:04:58 +00:00
break ;
2019-12-27 11:29:46 +00:00
case DIMENSION_CHANGE_SUCCESS :
2021-01-04 00:06:20 +00:00
//sometimes the client doesn't feel like loading
PlayStatusPacket spawnPacket = new PlayStatusPacket ( ) ;
spawnPacket . setStatus ( PlayStatusPacket . Status . PLAYER_SPAWN ) ;
session . sendUpstreamPacket ( spawnPacket ) ;
2021-07-08 02:44:53 +00:00
attributesPacket = new UpdateAttributesPacket ( ) ;
attributesPacket . setRuntimeEntityId ( entity . getGeyserId ( ) ) ;
2021-12-06 00:03:47 +00:00
attributesPacket . getAttributes ( ) . addAll ( entity . getAttributes ( ) . values ( ) ) ;
2021-07-08 02:44:53 +00:00
session . sendUpstreamPacket ( attributesPacket ) ;
2019-12-27 11:29:46 +00:00
break ;
2020-02-16 18:40:54 +00:00
case JUMP :
2021-06-28 23:26:02 +00:00
entity . setOnGround ( false ) ; // Increase block break time while jumping
2020-02-16 18:40:54 +00:00
break ;
2023-07-21 23:25:01 +00:00
case MISSED_SWING :
// TODO Re-evaluate after pre-1.20.10 is supported?
if ( session . getArmAnimationTicks ( ) = = - 1 ) {
session . sendDownstreamPacket ( new ServerboundSwingPacket ( Hand . MAIN_HAND ) ) ;
session . activateArmAnimationTicking ( ) ;
// Send packet to Bedrock so it knows
AnimatePacket animatePacket = new AnimatePacket ( ) ;
animatePacket . setRuntimeEntityId ( session . getPlayerEntity ( ) . getGeyserId ( ) ) ;
animatePacket . setAction ( AnimatePacket . Action . SWING_ARM ) ;
session . sendUpstreamPacket ( animatePacket ) ;
}
break ;
2019-08-10 00:15:06 +00:00
}
}
}