func (p *Player) addHealth(health float64) {
	p.health.AddHealth(health)
	p.session().SendHealth(p.health)
}
addHealth adds health to the player's current health.
package player
import (
	"fmt"
	"github.com/df-mc/dragonfly/server/block"
	blockAction "github.com/df-mc/dragonfly/server/block/action"
	"github.com/df-mc/dragonfly/server/block/cube"
	"github.com/df-mc/dragonfly/server/cmd"
	"github.com/df-mc/dragonfly/server/entity"
	"github.com/df-mc/dragonfly/server/entity/action"
	"github.com/df-mc/dragonfly/server/entity/damage"
	"github.com/df-mc/dragonfly/server/entity/effect"
	"github.com/df-mc/dragonfly/server/entity/healing"
	"github.com/df-mc/dragonfly/server/entity/physics"
	"github.com/df-mc/dragonfly/server/event"
	"github.com/df-mc/dragonfly/server/item"
	"github.com/df-mc/dragonfly/server/item/armour"
	"github.com/df-mc/dragonfly/server/item/enchantment"
	"github.com/df-mc/dragonfly/server/item/inventory"
	"github.com/df-mc/dragonfly/server/item/tool"
	"github.com/df-mc/dragonfly/server/player/bossbar"
	"github.com/df-mc/dragonfly/server/player/chat"
	"github.com/df-mc/dragonfly/server/player/form"
	"github.com/df-mc/dragonfly/server/player/scoreboard"
	"github.com/df-mc/dragonfly/server/player/skin"
	"github.com/df-mc/dragonfly/server/player/title"
	"github.com/df-mc/dragonfly/server/session"
	"github.com/df-mc/dragonfly/server/world"
	"github.com/df-mc/dragonfly/server/world/particle"
	"github.com/df-mc/dragonfly/server/world/sound"
	"github.com/go-gl/mathgl/mgl64"
	"github.com/google/uuid"
	"go.uber.org/atomic"
	"golang.org/x/text/language"
	"math"
	"math/rand"
	"net"
	"strings"
	"sync"
	"time"
)
type Player struct {
	name                                string
	uuid                                uuid.UUID
	xuid                                string
	locale                              language.Tag
	pos, vel                            atomic.Value
	nameTag                             atomic.String
	yaw, pitch, absorptionHealth, scale atomic.Float64
	gameModeMu sync.RWMutex
	gameMode   world.GameMode
	skinMu sync.RWMutex
	skin   skin.Skin
	sMutex sync.RWMutex
	s *session.Session
	hMutex sync.RWMutex
	h Handler
	inv, offHand *inventory.Inventory
	armour       *inventory.Armour
	heldSlot     *atomic.Uint32
	sneaking, sprinting, swimming, flying,
	invisible, immobile, onGround, usingItem atomic.Bool
	usingSince atomic.Int64
	fireTicks    atomic.Int64
	fallDistance atomic.Float64
	speed    atomic.Float64
	health   *entity.HealthManager
	effects  *entity.EffectManager
	immunity atomic.Value
	mc *entity.MovementComputer
	breaking          atomic.Bool
	breakingPos       atomic.Value
	lastBreakDuration time.Duration
	breakParticleCounter atomic.Uint32
	hunger *hungerManager
}
func New(name string, skin skin.Skin, pos mgl64.Vec3) *Player {
	p := &Player{}
	*p = Player{
		inv: inventory.New(36, func(slot int, item item.Stack) {
			if slot == int(p.heldSlot.Load()) {
				p.broadcastItems(slot, item)
			}
		}),
		uuid:     uuid.New(),
		offHand:  inventory.New(1, p.broadcastItems),
		armour:   inventory.NewArmour(p.broadcastArmour),
		hunger:   newHungerManager(),
		health:   entity.NewHealthManager(),
		effects:  entity.NewEffectManager(),
		gameMode: world.GameModeAdventure{},
		h:        NopHandler{},
		name:     name,
		skin:     skin,
		speed:    *atomic.NewFloat64(0.1),
		nameTag:  *atomic.NewString(name),
		heldSlot: atomic.NewUint32(0),
		locale:   language.BritishEnglish,
		scale:    *atomic.NewFloat64(1),
	}
	p.mc = &entity.MovementComputer{Gravity: 0.08, Drag: 0.02, DragBeforeGravity: true}
	p.pos.Store(pos)
	p.vel.Store(mgl64.Vec3{})
	p.immunity.Store(time.Now())
	p.breakingPos.Store(cube.Pos{})
	return p
}
func NewWithSession(name, xuid string, uuid uuid.UUID, skin skin.Skin, s *session.Session, pos mgl64.Vec3, data *Data) *Player {
	p := New(name, skin, pos)
	p.s, p.uuid, p.xuid, p.skin = s, uuid, xuid, skin
	p.inv, p.offHand, p.armour, p.heldSlot = s.HandleInventories()
	p.locale, _ = language.Parse(strings.Replace(s.ClientData().LanguageCode, "_", "-", 1))
	chat.Global.Subscribe(p)
	if data != nil {
		p.load(*data)
	}
	return p
}
func (p *Player) Name() string {
	return p.name
}
func (p *Player) UUID() uuid.UUID {
	return p.uuid
}
func (p *Player) XUID() string {
	return p.xuid
}
func (p *Player) Addr() net.Addr {
	if p.session() == session.Nop {
		return nil
	}
	return p.session().Addr()
}
func (p *Player) Skin() skin.Skin {
	p.skinMu.RLock()
	defer p.skinMu.RUnlock()
	return p.skin
}
func (p *Player) SetSkin(skin skin.Skin) {
	if p.Dead() {
		return
	}
	ctx := event.C()
	p.handler().HandleSkinChange(ctx, skin)
	ctx.Continue(func() {
		p.skinMu.Lock()
		p.skin = skin
		p.skinMu.Unlock()
		for _, v := range p.viewers() {
			v.ViewSkin(p)
		}
	})
	ctx.Stop(func() {
		p.session().ViewSkin(p)
	})
}
func (p *Player) Locale() language.Tag {
	return p.locale
}
func (p *Player) Handle(h Handler) {
	p.hMutex.Lock()
	defer p.hMutex.Unlock()
	if h == nil {
		h = NopHandler{}
	}
	p.h = h
}
func (p *Player) Message(a ...interface{}) {
	p.session().SendMessage(format(a))
}
func (p *Player) Messagef(f string, a ...interface{}) {
	msg := fmt.Sprintf(f, a...)
	p.session().SendMessage(msg)
}
func (p *Player) SendPopup(a ...interface{}) {
	p.session().SendPopup(format(a))
}
func (p *Player) SendTip(a ...interface{}) {
	p.session().SendTip(format(a))
}
func (p *Player) ResetFallDistance() {
	p.fallDistance.Store(0)
}
func (p *Player) FallDistance() float64 {
	return p.fallDistance.Load()
}
func (p *Player) SendTitle(t title.Title) {
	p.session().SetTitleDurations(t.FadeInDuration(), t.Duration(), t.FadeOutDuration())
	p.session().SendTitle(t.Text())
	if t.Subtitle() != "" {
		p.session().SendSubtitle(t.Subtitle())
	}
	if t.ActionText() != "" {
		p.session().SendActionBarMessage(t.ActionText())
	}
}
func (p *Player) SendScoreboard(scoreboard *scoreboard.Scoreboard) {
	p.session().SendScoreboard(scoreboard)
}
func (p *Player) RemoveScoreboard() {
	p.session().RemoveScoreboard()
}
func (p *Player) SendBossBar(bar bossbar.BossBar) {
	p.session().SendBossBar(bar.Text(), bar.HealthPercentage())
}
func (p *Player) RemoveBossBar() {
	p.session().RemoveBossBar()
}
func (p *Player) Chat(msg ...interface{}) {
	message := format(msg)
	ctx := event.C()
	p.handler().HandleChat(ctx, &message)
	ctx.Continue(func() {
		_, _ = fmt.Fprintf(chat.Global, "<%v> %v\n", p.name, message)
	})
}
func (p *Player) ExecuteCommand(commandLine string) {
	if p.Dead() {
		return
	}
	args := strings.Split(commandLine, " ")
	commandName := strings.TrimPrefix(args[0], "/")
	command, ok := cmd.ByAlias(commandName)
	if !ok {
		output := &cmd.Output{}
		output.Errorf("Unknown command '%v'", commandName)
		p.SendCommandOutput(output)
		return
	}
	ctx := event.C()
	p.handler().HandleCommandExecution(ctx, command, args[1:])
	ctx.Continue(func() {
		command.Execute(strings.TrimPrefix(strings.TrimPrefix(commandLine, "/"+commandName), " "), p)
	})
}
func (p *Player) Disconnect(msg ...interface{}) {
	p.session().Disconnect(format(msg))
	p.close()
}
func (p *Player) Transfer(address string) (err error) {
	addr, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return err
	}
	ctx := event.C()
	p.handler().HandleTransfer(ctx, addr)
	ctx.Continue(func() {
		p.session().Transfer(addr.IP, addr.Port)
	})
	return
}
func (p *Player) SendCommandOutput(output *cmd.Output) {
	p.session().SendCommandOutput(output)
}
func (p *Player) SendForm(f form.Form) {
	p.session().SendForm(f)
}
func (p *Player) ShowCoordinates() {
	p.session().EnableCoordinates(true)
}
func (p *Player) HideCoordinates() {
	p.session().EnableCoordinates(false)
}
func (p *Player) EnableInstantRespawn() {
	p.session().EnableInstantRespawn(true)
}
func (p *Player) DisableInstantRespawn() {
	p.session().EnableInstantRespawn(false)
}
func (p *Player) SetNameTag(name string) {
	p.nameTag.Store(name)
	p.updateState()
}
func (p *Player) NameTag() string {
	return p.nameTag.Load()
}
func (p *Player) SetSpeed(speed float64) {
	p.speed.Store(speed)
	p.session().SendSpeed(speed)
}
func (p *Player) Speed() float64 {
	return p.speed.Load()
}
func (p *Player) Health() float64 {
	return p.health.Health()
}
func (p *Player) MaxHealth() float64 {
	return p.health.MaxHealth()
}
func (p *Player) SetMaxHealth(health float64) {
	p.health.SetMaxHealth(health)
	p.session().SendHealth(p.health)
}