2019-08-03 03:38:09 +00:00
/ *
2020-01-09 03:05:42 +00:00
* Copyright ( c ) 2019 - 2020 GeyserMC . http : //geysermc.org
2019-08-03 03:38:09 +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.entity ;
2019-09-16 22:28:29 +00:00
import com.github.steveice10.mc.auth.data.GameProfile ;
2020-04-04 06:27:34 +00:00
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata ;
import com.github.steveice10.mc.protocol.data.message.TextMessage ;
2020-05-10 19:38:39 +00:00
import com.github.steveice10.opennbt.tag.builtin.CompoundTag ;
2019-10-09 18:39:38 +00:00
import com.nukkitx.math.vector.Vector3f ;
2020-06-23 00:11:09 +00:00
import com.nukkitx.protocol.bedrock.data.AttributeData ;
import com.nukkitx.protocol.bedrock.data.PlayerPermission ;
import com.nukkitx.protocol.bedrock.data.command.CommandPermission ;
import com.nukkitx.protocol.bedrock.data.entity.EntityData ;
import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData ;
2020-05-10 19:38:39 +00:00
import com.nukkitx.protocol.bedrock.packet.* ;
2019-08-03 03:38:09 +00:00
import lombok.Getter ;
import lombok.Setter ;
2019-12-21 17:35:48 +00:00
import org.geysermc.connector.GeyserConnector ;
2020-07-01 16:28:03 +00:00
import org.geysermc.connector.entity.attribute.Attribute ;
import org.geysermc.connector.entity.attribute.AttributeType ;
2019-08-03 03:38:09 +00:00
import org.geysermc.connector.entity.type.EntityType ;
import org.geysermc.connector.network.session.GeyserSession ;
2020-06-23 00:11:09 +00:00
import org.geysermc.connector.network.session.cache.EntityEffectCache ;
2020-04-04 06:27:34 +00:00
import org.geysermc.connector.scoreboard.Team ;
2020-07-01 16:28:03 +00:00
import org.geysermc.connector.utils.AttributeUtils ;
2020-04-04 06:27:34 +00:00
import org.geysermc.connector.utils.MessageUtils ;
2019-10-09 18:39:38 +00:00
import org.geysermc.connector.utils.SkinUtils ;
2019-08-03 03:38:09 +00:00
2020-05-14 16:30:33 +00:00
import java.util.ArrayList ;
import java.util.List ;
2020-07-01 16:28:03 +00:00
import java.util.Map ;
2019-08-03 06:51:05 +00:00
import java.util.UUID ;
2020-05-10 19:38:39 +00:00
import java.util.concurrent.TimeUnit ;
2019-08-03 06:51:05 +00:00
2019-09-16 22:28:29 +00:00
@Getter @Setter
2019-12-01 00:19:03 +00:00
public class PlayerEntity extends LivingEntity {
2019-09-25 21:52:28 +00:00
private GameProfile profile ;
2019-08-03 06:51:05 +00:00
private UUID uuid ;
2019-09-16 22:28:29 +00:00
private String username ;
2019-09-25 21:52:28 +00:00
private long lastSkinUpdate = - 1 ;
2019-10-02 20:45:29 +00:00
private boolean playerList = true ;
2020-03-22 22:59:34 +00:00
private final EntityEffectCache effectCache ;
2019-08-03 03:38:09 +00:00
2020-05-10 19:38:39 +00:00
private Entity leftParrot ;
private Entity rightParrot ;
2019-09-21 07:42:44 +00:00
public PlayerEntity ( GameProfile gameProfile , long entityId , long geyserId , Vector3f position , Vector3f motion , Vector3f rotation ) {
super ( entityId , geyserId , EntityType . PLAYER , position , motion , rotation ) ;
2019-08-03 03:38:09 +00:00
2019-09-25 21:52:28 +00:00
profile = gameProfile ;
2019-09-16 22:28:29 +00:00
uuid = gameProfile . getId ( ) ;
username = gameProfile . getName ( ) ;
2020-03-22 22:59:34 +00:00
effectCache = new EntityEffectCache ( ) ;
2019-09-25 21:52:28 +00:00
if ( geyserId = = 1 ) valid = true ;
2019-08-03 03:38:09 +00:00
}
2019-10-02 20:45:29 +00:00
@Override
public boolean despawnEntity ( GeyserSession session ) {
super . despawnEntity ( session ) ;
return ! playerList ; // don't remove from cache when still on playerlist
}
2019-08-03 03:38:09 +00:00
@Override
public void spawnEntity ( GeyserSession session ) {
2019-09-25 21:52:28 +00:00
if ( geyserId = = 1 ) return ;
2019-08-03 03:38:09 +00:00
AddPlayerPacket addPlayerPacket = new AddPlayerPacket ( ) ;
2019-08-03 06:51:05 +00:00
addPlayerPacket . setUuid ( uuid ) ;
2019-09-16 22:28:29 +00:00
addPlayerPacket . setUsername ( username ) ;
2019-09-25 21:52:28 +00:00
addPlayerPacket . setRuntimeEntityId ( geyserId ) ;
addPlayerPacket . setUniqueEntityId ( geyserId ) ;
2020-04-29 16:06:25 +00:00
addPlayerPacket . setPosition ( position . clone ( ) . sub ( 0 , EntityType . PLAYER . getOffset ( ) , 0 ) ) ;
2019-10-02 20:45:29 +00:00
addPlayerPacket . setRotation ( getBedrockRotation ( ) ) ;
2019-09-25 21:52:28 +00:00
addPlayerPacket . setMotion ( motion ) ;
2019-08-03 03:38:09 +00:00
addPlayerPacket . setHand ( hand ) ;
2020-02-06 00:55:34 +00:00
addPlayerPacket . getAdventureSettings ( ) . setCommandPermission ( CommandPermission . NORMAL ) ;
addPlayerPacket . getAdventureSettings ( ) . setPlayerPermission ( PlayerPermission . VISITOR ) ;
2019-09-25 21:52:28 +00:00
addPlayerPacket . setDeviceId ( " " ) ;
addPlayerPacket . setPlatformChatId ( " " ) ;
2020-02-14 02:04:22 +00:00
addPlayerPacket . getMetadata ( ) . putAll ( metadata ) ;
2019-08-03 06:51:05 +00:00
2020-05-23 21:39:17 +00:00
long linkedEntityId = session . getEntityCache ( ) . getCachedPlayerEntityLink ( entityId ) ;
if ( linkedEntityId ! = - 1 ) {
2020-06-23 00:11:09 +00:00
addPlayerPacket . getEntityLinks ( ) . add ( new EntityLinkData ( session . getEntityCache ( ) . getEntityByJavaId ( linkedEntityId ) . getGeyserId ( ) , geyserId , EntityLinkData . Type . RIDER , false ) ) ;
2020-05-23 21:39:17 +00:00
}
2019-08-03 06:51:05 +00:00
valid = true ;
2020-05-05 15:51:43 +00:00
session . sendUpstreamPacket ( addPlayerPacket ) ;
2020-02-14 02:04:22 +00:00
updateEquipment ( session ) ;
updateBedrockAttributes ( session ) ;
2019-08-03 03:38:09 +00:00
}
2019-10-09 18:39:38 +00:00
public void sendPlayer ( GeyserSession session ) {
2020-04-29 16:04:45 +00:00
if ( session . getEntityCache ( ) . getPlayerEntity ( uuid ) = = null )
return ;
2019-10-09 18:39:38 +00:00
if ( getLastSkinUpdate ( ) = = - 1 ) {
if ( playerList ) {
PlayerListPacket playerList = new PlayerListPacket ( ) ;
2020-02-06 00:55:34 +00:00
playerList . setAction ( PlayerListPacket . Action . ADD ) ;
2019-10-09 18:39:38 +00:00
playerList . getEntries ( ) . add ( SkinUtils . buildDefaultEntry ( profile , geyserId ) ) ;
2020-05-05 15:51:43 +00:00
session . sendUpstreamPacket ( playerList ) ;
2019-10-09 18:39:38 +00:00
}
}
if ( session . getUpstream ( ) . isInitialized ( ) & & session . getEntityCache ( ) . getEntityByGeyserId ( geyserId ) = = null ) {
session . getEntityCache ( ) . spawnEntity ( this ) ;
} else {
spawnEntity ( session ) ;
}
if ( ! playerList ) {
// remove from playerlist if player isn't on playerlist
2019-12-21 17:35:48 +00:00
GeyserConnector . getInstance ( ) . getGeneralThreadPool ( ) . execute ( ( ) - > {
2019-10-09 18:39:38 +00:00
PlayerListPacket playerList = new PlayerListPacket ( ) ;
2020-02-06 00:55:34 +00:00
playerList . setAction ( PlayerListPacket . Action . REMOVE ) ;
2019-10-09 18:39:38 +00:00
playerList . getEntries ( ) . add ( new PlayerListPacket . Entry ( uuid ) ) ;
2020-05-05 15:51:43 +00:00
session . sendUpstreamPacket ( playerList ) ;
2019-10-09 18:39:38 +00:00
} ) ;
}
}
2020-02-15 23:39:34 +00:00
@Override
2020-04-29 16:06:25 +00:00
public void moveAbsolute ( GeyserSession session , Vector3f position , Vector3f rotation , boolean isOnGround , boolean teleported ) {
2020-02-15 23:39:34 +00:00
setPosition ( position ) ;
setRotation ( rotation ) ;
2020-06-16 23:58:06 +00:00
setOnGround ( isOnGround ) ;
2020-05-23 23:26:20 +00:00
2020-02-15 23:39:34 +00:00
MovePlayerPacket movePlayerPacket = new MovePlayerPacket ( ) ;
movePlayerPacket . setRuntimeEntityId ( geyserId ) ;
movePlayerPacket . setPosition ( this . position ) ;
movePlayerPacket . setRotation ( getBedrockRotation ( ) ) ;
movePlayerPacket . setOnGround ( isOnGround ) ;
2020-04-29 16:06:25 +00:00
movePlayerPacket . setMode ( teleported ? MovePlayerPacket . Mode . TELEPORT : MovePlayerPacket . Mode . NORMAL ) ;
if ( teleported ) {
movePlayerPacket . setTeleportationCause ( MovePlayerPacket . TeleportationCause . UNKNOWN ) ;
}
2020-02-15 23:39:34 +00:00
2020-05-05 15:51:43 +00:00
session . sendUpstreamPacket ( movePlayerPacket ) ;
2020-05-10 19:38:39 +00:00
if ( leftParrot ! = null ) {
leftParrot . moveAbsolute ( session , position , rotation , true , teleported ) ;
}
if ( rightParrot ! = null ) {
rightParrot . moveAbsolute ( session , position , rotation , true , teleported ) ;
}
2020-02-15 23:39:34 +00:00
}
@Override
public void moveRelative ( GeyserSession session , double relX , double relY , double relZ , Vector3f rotation , boolean isOnGround ) {
setRotation ( rotation ) ;
this . position = Vector3f . from ( position . getX ( ) + relX , position . getY ( ) + relY , position . getZ ( ) + relZ ) ;
2020-06-16 23:58:06 +00:00
setOnGround ( isOnGround ) ;
2020-05-23 23:26:20 +00:00
2020-02-15 23:39:34 +00:00
MovePlayerPacket movePlayerPacket = new MovePlayerPacket ( ) ;
movePlayerPacket . setRuntimeEntityId ( geyserId ) ;
movePlayerPacket . setPosition ( position ) ;
movePlayerPacket . setRotation ( getBedrockRotation ( ) ) ;
movePlayerPacket . setOnGround ( isOnGround ) ;
movePlayerPacket . setMode ( MovePlayerPacket . Mode . NORMAL ) ;
2020-05-05 15:51:43 +00:00
session . sendUpstreamPacket ( movePlayerPacket ) ;
2020-05-10 19:38:39 +00:00
if ( leftParrot ! = null ) {
leftParrot . moveRelative ( session , relX , relY , relZ , rotation , true ) ;
}
if ( rightParrot ! = null ) {
rightParrot . moveRelative ( session , relX , relY , relZ , rotation , true ) ;
}
2020-02-15 23:39:34 +00:00
}
2020-06-16 23:58:06 +00:00
@Override
public void updateHeadLookRotation ( GeyserSession session , float headYaw ) {
moveRelative ( session , 0 , 0 , 0 , Vector3f . from ( rotation . getX ( ) , rotation . getY ( ) , headYaw ) , onGround ) ;
MovePlayerPacket movePlayerPacket = new MovePlayerPacket ( ) ;
movePlayerPacket . setRuntimeEntityId ( geyserId ) ;
movePlayerPacket . setPosition ( position ) ;
movePlayerPacket . setRotation ( getBedrockRotation ( ) ) ;
2020-06-23 00:11:09 +00:00
movePlayerPacket . setMode ( MovePlayerPacket . Mode . HEAD_ROTATION ) ;
2020-06-16 23:58:06 +00:00
session . sendUpstreamPacket ( movePlayerPacket ) ;
}
@Override
public void updatePositionAndRotation ( GeyserSession session , double moveX , double moveY , double moveZ , float yaw , float pitch , boolean isOnGround ) {
moveRelative ( session , moveX , moveY , moveZ , yaw , pitch , isOnGround ) ;
}
@Override
public void updateRotation ( GeyserSession session , float yaw , float pitch , boolean isOnGround ) {
super . updateRotation ( session , yaw , pitch , isOnGround ) ;
// Both packets need to be sent or else player head rotation isn't correctly updated
MovePlayerPacket movePlayerPacket = new MovePlayerPacket ( ) ;
movePlayerPacket . setRuntimeEntityId ( geyserId ) ;
movePlayerPacket . setPosition ( position ) ;
movePlayerPacket . setRotation ( getBedrockRotation ( ) ) ;
movePlayerPacket . setOnGround ( isOnGround ) ;
2020-06-23 00:11:09 +00:00
movePlayerPacket . setMode ( MovePlayerPacket . Mode . HEAD_ROTATION ) ;
2020-06-16 23:58:06 +00:00
session . sendUpstreamPacket ( movePlayerPacket ) ;
}
2020-02-15 23:39:34 +00:00
@Override
public void setPosition ( Vector3f position ) {
this . position = position . add ( 0 , entityType . getOffset ( ) , 0 ) ;
}
2020-04-04 06:27:34 +00:00
@Override
public void updateBedrockMetadata ( EntityMetadata entityMetadata , GeyserSession session ) {
super . updateBedrockMetadata ( entityMetadata , session ) ;
if ( entityMetadata . getId ( ) = = 2 ) {
// System.out.println(session.getScoreboardCache().getScoreboard().getObjectives().keySet());
for ( Team team : session . getScoreboardCache ( ) . getScoreboard ( ) . getTeams ( ) . values ( ) ) {
// session.getConnector().getLogger().info("team name " + team.getName());
// session.getConnector().getLogger().info("team entities " + team.getEntities());
}
String username = this . username ;
TextMessage name = ( TextMessage ) entityMetadata . getValue ( ) ;
if ( name ! = null ) {
username = MessageUtils . getBedrockMessage ( name ) ;
}
Team team = session . getScoreboardCache ( ) . getScoreboard ( ) . getTeamFor ( username ) ;
if ( team ! = null ) {
// session.getConnector().getLogger().info("team name es " + team.getName() + " with prefix " + team.getPrefix() + " and suffix " + team.getSuffix());
metadata . put ( EntityData . NAMETAG , team . getPrefix ( ) + MessageUtils . toChatColor ( team . getColor ( ) ) + username + team . getSuffix ( ) ) ;
}
}
2020-05-10 19:38:39 +00:00
2020-05-14 16:30:33 +00:00
// Extra hearts - is not metadata but an attribute on Bedrock
if ( entityMetadata . getId ( ) = = 14 ) {
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket ( ) ;
attributesPacket . setRuntimeEntityId ( geyserId ) ;
2020-06-23 00:11:09 +00:00
List < AttributeData > attributes = new ArrayList < > ( ) ;
2020-05-14 16:30:33 +00:00
// Setting to a higher maximum since plugins/datapacks can probably extend the Bedrock soft limit
2020-06-23 00:11:09 +00:00
attributes . add ( new AttributeData ( " minecraft:absorption " , 0 . 0f , 1024f , ( float ) entityMetadata . getValue ( ) , 0 . 0f ) ) ;
2020-05-14 16:30:33 +00:00
attributesPacket . setAttributes ( attributes ) ;
session . sendUpstreamPacket ( attributesPacket ) ;
}
2020-05-10 19:38:39 +00:00
// Parrot occupying shoulder
2020-06-16 23:58:06 +00:00
if ( ( entityMetadata . getId ( ) = = 18 & & leftParrot = = null ) | | ( entityMetadata . getId ( ) = = 19 & & rightParrot = = null ) ) { // null check since this code just creates the parrot
2020-05-10 19:38:39 +00:00
CompoundTag tag = ( CompoundTag ) entityMetadata . getValue ( ) ;
if ( tag ! = null & & ! tag . isEmpty ( ) ) {
// The parrot is a separate entity in Bedrock, but part of the player entity in Java
Entity parrot = new Entity ( 0 , session . getEntityCache ( ) . getNextEntityId ( ) . incrementAndGet ( ) ,
EntityType . PARROT , position , motion , rotation ) ;
parrot . spawnEntity ( session ) ;
parrot . getMetadata ( ) . put ( EntityData . VARIANT , tag . get ( " Variant " ) . getValue ( ) ) ;
// Different position whether the parrot is left or right
float offset = ( entityMetadata . getId ( ) = = 18 ) ? 0 . 4f : - 0 . 4f ;
parrot . getMetadata ( ) . put ( EntityData . RIDER_SEAT_POSITION , Vector3f . from ( offset , - 0 . 22 , - 0 . 1 ) ) ;
parrot . getMetadata ( ) . put ( EntityData . RIDER_ROTATION_LOCKED , 1 ) ;
parrot . updateBedrockMetadata ( session ) ;
SetEntityLinkPacket linkPacket = new SetEntityLinkPacket ( ) ;
2020-06-23 00:11:09 +00:00
EntityLinkData . Type type = ( entityMetadata . getId ( ) = = 18 ) ? EntityLinkData . Type . RIDER : EntityLinkData . Type . PASSENGER ;
linkPacket . setEntityLink ( new EntityLinkData ( geyserId , parrot . getGeyserId ( ) , type , false ) ) ;
2020-05-10 19:38:39 +00:00
// Delay, or else spawned-in players won't get the link
// TODO: Find a better solution. This problem also exists with item frames
session . getConnector ( ) . getGeneralThreadPool ( ) . schedule ( ( ) - > session . sendUpstreamPacket ( linkPacket ) , 500 , TimeUnit . MILLISECONDS ) ;
if ( entityMetadata . getId ( ) = = 18 ) {
leftParrot = parrot ;
} else {
rightParrot = parrot ;
}
} else {
Entity parrot = ( entityMetadata . getId ( ) = = 18 ? leftParrot : rightParrot ) ;
if ( parrot ! = null ) {
parrot . despawnEntity ( session ) ;
if ( entityMetadata . getId ( ) = = 18 ) {
leftParrot = null ;
} else {
rightParrot = null ;
}
}
}
}
2020-04-04 06:27:34 +00:00
}
2020-07-01 16:28:03 +00:00
@Override
public void updateBedrockAttributes ( GeyserSession session ) { // TODO: Don't use duplicated code
if ( ! valid ) return ;
List < AttributeData > attributes = new ArrayList < > ( ) ;
for ( Map . Entry < AttributeType , Attribute > entry : this . attributes . entrySet ( ) ) {
if ( ! entry . getValue ( ) . getType ( ) . isBedrockAttribute ( ) )
continue ;
attributes . add ( AttributeUtils . getBedrockAttribute ( entry . getValue ( ) ) ) ;
}
UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket ( ) ;
updateAttributesPacket . setRuntimeEntityId ( geyserId ) ;
updateAttributesPacket . setAttributes ( attributes ) ;
session . sendUpstreamPacket ( updateAttributesPacket ) ;
}
2019-08-03 03:38:09 +00:00
}