183 lines
6.5 KiB
Go
183 lines
6.5 KiB
Go
|
|
package session
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"math"
|
||
|
|
|
||
|
|
"github.com/df-mc/dragonfly/server/block/cube"
|
||
|
|
"github.com/df-mc/dragonfly/server/world"
|
||
|
|
"github.com/go-gl/mathgl/mgl32"
|
||
|
|
"github.com/go-gl/mathgl/mgl64"
|
||
|
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||
|
|
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||
|
|
)
|
||
|
|
|
||
|
|
// PlayerAuthInputHandler handles the PlayerAuthInput packet.
|
||
|
|
type PlayerAuthInputHandler struct{}
|
||
|
|
|
||
|
|
// Handle ...
|
||
|
|
func (h PlayerAuthInputHandler) Handle(p packet.Packet, s *Session, tx *world.Tx, c Controllable) error {
|
||
|
|
pk := p.(*packet.PlayerAuthInput)
|
||
|
|
if err := h.handleMovement(pk, s, c); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
return h.handleActions(pk, s, tx, c)
|
||
|
|
}
|
||
|
|
|
||
|
|
// handleMovement handles the movement part of the packet.PlayerAuthInput.
|
||
|
|
func (h PlayerAuthInputHandler) handleMovement(pk *packet.PlayerAuthInput, s *Session, c Controllable) error {
|
||
|
|
yaw, pitch := c.Rotation().Elem()
|
||
|
|
pos := c.Position()
|
||
|
|
|
||
|
|
reference := []float64{pitch, yaw, yaw, pos[0], pos[1], pos[2]}
|
||
|
|
for i, v := range [...]*float32{&pk.Pitch, &pk.Yaw, &pk.HeadYaw, &pk.Position[0], &pk.Position[1], &pk.Position[2]} {
|
||
|
|
f := float64(*v)
|
||
|
|
if math.IsNaN(f) || math.IsInf(f, 1) || math.IsInf(f, 0) {
|
||
|
|
// Sometimes, the PlayerAuthInput packet is in fact sent with NaN/INF after being teleported (to another
|
||
|
|
// world), see #425. For this reason, we don't actually return an error if this happens, because this will
|
||
|
|
// result in the player being kicked. Just log it and replace the NaN value with the one we have tracked
|
||
|
|
// server-side.
|
||
|
|
s.conf.Log.Debug("process packet: PlayerAuthInput: found nan/inf values. assuming server-side values", "pos", fmt.Sprint(pk.Position), "yaw", pk.Yaw, "head-yaw", pk.HeadYaw, "pitch", pk.Pitch)
|
||
|
|
*v = float32(reference[i])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pk.Position = pk.Position.Sub(mgl32.Vec3{0, 1.62}) // Sub the base offset of players from the pos.
|
||
|
|
|
||
|
|
newPos := vec32To64(pk.Position)
|
||
|
|
deltaPos, deltaYaw, deltaPitch := newPos.Sub(pos), float64(pk.Yaw)-yaw, float64(pk.Pitch)-pitch
|
||
|
|
if mgl64.FloatEqual(deltaPos.Len(), 0) && mgl64.FloatEqual(deltaYaw, 0) && mgl64.FloatEqual(deltaPitch, 0) {
|
||
|
|
// The PlayerAuthInput packet is sent every tick, so don't do anything if the position and rotation
|
||
|
|
// were unchanged.
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
if expected := s.teleportPos.Load(); expected != nil {
|
||
|
|
if newPos.Sub(*expected).Len() > 1 {
|
||
|
|
// The player has moved before it received the teleport packet. Ignore this movement entirely and
|
||
|
|
// wait for the client to sync itself back to the server. Once we get a movement that is close
|
||
|
|
// enough to the teleport position, we'll allow the player to move around again.
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
s.teleportPos.Store(nil)
|
||
|
|
}
|
||
|
|
|
||
|
|
s.moving = true
|
||
|
|
c.Move(deltaPos, deltaYaw, deltaPitch)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// handleActions handles the actions with the world that are present in the PlayerAuthInput packet.
|
||
|
|
func (h PlayerAuthInputHandler) handleActions(pk *packet.PlayerAuthInput, s *Session, tx *world.Tx, c Controllable) error {
|
||
|
|
if pk.InputData.Load(packet.InputFlagPerformItemInteraction) {
|
||
|
|
if err := h.handleUseItemData(pk.ItemInteractionData, s, c); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if pk.InputData.Load(packet.InputFlagPerformBlockActions) {
|
||
|
|
if err := h.handleBlockActions(pk.BlockActions, s, c); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
h.handleInputFlags(pk.InputData, s, c)
|
||
|
|
|
||
|
|
if pk.InputData.Load(packet.InputFlagPerformItemStackRequest) {
|
||
|
|
s.inTransaction.Store(true)
|
||
|
|
defer s.inTransaction.Store(false)
|
||
|
|
|
||
|
|
// As of 1.18 this is now used for sending item stack requests such as when mining a block.
|
||
|
|
sh := s.handlers[packet.IDItemStackRequest].(*ItemStackRequestHandler)
|
||
|
|
if err := sh.handleRequest(pk.ItemStackRequest, s, tx, c); err != nil {
|
||
|
|
// Item stacks being out of sync isn't uncommon, so don't error. Just debug the error and let the
|
||
|
|
// revert do its work.
|
||
|
|
s.conf.Log.Debug("process packet: PlayerAuthInput: resolve item stack request: " + err.Error())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// handleInputFlags handles the toggleable input flags set in a PlayerAuthInput packet.
|
||
|
|
func (h PlayerAuthInputHandler) handleInputFlags(flags protocol.Bitset, s *Session, c Controllable) {
|
||
|
|
if flags.Load(packet.InputFlagStartSprinting) {
|
||
|
|
c.StartSprinting()
|
||
|
|
}
|
||
|
|
if flags.Load(packet.InputFlagStopSprinting) {
|
||
|
|
c.StopSprinting()
|
||
|
|
}
|
||
|
|
if flags.Load(packet.InputFlagStartSneaking) {
|
||
|
|
c.StartSneaking()
|
||
|
|
}
|
||
|
|
if flags.Load(packet.InputFlagStopSneaking) {
|
||
|
|
c.StopSneaking()
|
||
|
|
}
|
||
|
|
if flags.Load(packet.InputFlagStartSwimming) {
|
||
|
|
c.StartSwimming()
|
||
|
|
}
|
||
|
|
if flags.Load(packet.InputFlagStopSwimming) {
|
||
|
|
c.StopSwimming()
|
||
|
|
}
|
||
|
|
if flags.Load(packet.InputFlagStartGliding) {
|
||
|
|
c.StartGliding()
|
||
|
|
}
|
||
|
|
if flags.Load(packet.InputFlagStopGliding) {
|
||
|
|
c.StopGliding()
|
||
|
|
}
|
||
|
|
if flags.Load(packet.InputFlagStartJumping) {
|
||
|
|
c.Jump()
|
||
|
|
}
|
||
|
|
if flags.Load(packet.InputFlagStartCrawling) {
|
||
|
|
c.StartCrawling()
|
||
|
|
}
|
||
|
|
if flags.Load(packet.InputFlagStopCrawling) {
|
||
|
|
c.StopCrawling()
|
||
|
|
}
|
||
|
|
if flags.Load(packet.InputFlagMissedSwing) {
|
||
|
|
s.swingingArm.Store(true)
|
||
|
|
defer s.swingingArm.Store(false)
|
||
|
|
c.PunchAir()
|
||
|
|
}
|
||
|
|
if flags.Load(packet.InputFlagStartFlying) {
|
||
|
|
if !c.GameMode().AllowsFlying() {
|
||
|
|
s.conf.Log.Debug("process packet: PlayerAuthInput: flying flag enabled while unable to fly")
|
||
|
|
s.SendAbilities(c)
|
||
|
|
} else {
|
||
|
|
c.StartFlying()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if flags.Load(packet.InputFlagStopFlying) {
|
||
|
|
c.StopFlying()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// handleUseItemData handles the protocol.UseItemTransactionData found in a packet.PlayerAuthInput.
|
||
|
|
func (h PlayerAuthInputHandler) handleUseItemData(data protocol.UseItemTransactionData, s *Session, c Controllable) error {
|
||
|
|
s.swingingArm.Store(true)
|
||
|
|
defer s.swingingArm.Store(false)
|
||
|
|
|
||
|
|
held, _ := c.HeldItems()
|
||
|
|
if !held.Equal(stackToItem(s.br, data.HeldItem.Stack)) {
|
||
|
|
s.conf.Log.Debug("process packet: PlayerAuthInput: UseItemTransaction: mismatch between actual held item and client held item")
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
pos := cube.Pos{int(data.BlockPosition[0]), int(data.BlockPosition[1]), int(data.BlockPosition[2])}
|
||
|
|
|
||
|
|
// Seems like this is only used for breaking blocks at the moment.
|
||
|
|
switch data.ActionType {
|
||
|
|
case protocol.UseItemActionBreakBlock:
|
||
|
|
c.BreakBlock(pos)
|
||
|
|
default:
|
||
|
|
return fmt.Errorf("unhandled UseItem ActionType for PlayerAuthInput packet %v", data.ActionType)
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// handleBlockActions handles a slice of protocol.PlayerBlockAction present in a PlayerAuthInput packet.
|
||
|
|
func (h PlayerAuthInputHandler) handleBlockActions(a []protocol.PlayerBlockAction, s *Session, c Controllable) error {
|
||
|
|
for _, action := range a {
|
||
|
|
if err := handlePlayerAction(action.Action, action.Face, action.BlockPos, selfEntityRuntimeID, s, c); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|