github.com/df-mc/dragonfly@v0.9.13/server/player/player.go (about) 1 package player 2 3 import ( 4 "fmt" 5 "math" 6 "math/rand" 7 "net" 8 "strings" 9 "sync" 10 "time" 11 12 "github.com/df-mc/atomic" 13 "github.com/df-mc/dragonfly/server/block" 14 "github.com/df-mc/dragonfly/server/block/cube" 15 "github.com/df-mc/dragonfly/server/block/model" 16 "github.com/df-mc/dragonfly/server/cmd" 17 "github.com/df-mc/dragonfly/server/entity" 18 "github.com/df-mc/dragonfly/server/entity/effect" 19 "github.com/df-mc/dragonfly/server/event" 20 "github.com/df-mc/dragonfly/server/internal/sliceutil" 21 "github.com/df-mc/dragonfly/server/item" 22 "github.com/df-mc/dragonfly/server/item/enchantment" 23 "github.com/df-mc/dragonfly/server/item/inventory" 24 "github.com/df-mc/dragonfly/server/player/bossbar" 25 "github.com/df-mc/dragonfly/server/player/chat" 26 "github.com/df-mc/dragonfly/server/player/form" 27 "github.com/df-mc/dragonfly/server/player/scoreboard" 28 "github.com/df-mc/dragonfly/server/player/skin" 29 "github.com/df-mc/dragonfly/server/player/title" 30 "github.com/df-mc/dragonfly/server/session" 31 "github.com/df-mc/dragonfly/server/world" 32 "github.com/df-mc/dragonfly/server/world/particle" 33 "github.com/df-mc/dragonfly/server/world/sound" 34 "github.com/go-gl/mathgl/mgl64" 35 "github.com/google/uuid" 36 "golang.org/x/text/language" 37 ) 38 39 // Player is an implementation of a player entity. It has methods that implement the behaviour that players 40 // need to play in the world. 41 type Player struct { 42 name string 43 uuid uuid.UUID 44 xuid string 45 locale language.Tag 46 pos, vel atomic.Value[mgl64.Vec3] 47 nameTag atomic.Value[string] 48 scoreTag atomic.Value[string] 49 yaw, pitch, absorptionHealth, scale atomic.Float64 50 once sync.Once 51 52 gameMode atomic.Value[world.GameMode] 53 54 skin atomic.Value[skin.Skin] 55 // s holds the session of the player. This field should not be used directly, but instead, 56 // Player.session() should be called. 57 s atomic.Value[*session.Session] 58 // h holds the current Handler of the player. It may be changed at any time by calling the Handle method. 59 h atomic.Value[Handler] 60 61 inv, offHand, enderChest *inventory.Inventory 62 armour *inventory.Armour 63 heldSlot *atomic.Uint32 64 65 sneaking, sprinting, swimming, gliding, flying, 66 invisible, immobile, onGround, usingItem atomic.Bool 67 usingSince atomic.Int64 68 69 glideTicks atomic.Int64 70 fireTicks atomic.Int64 71 fallDistance atomic.Float64 72 73 breathing bool 74 airSupplyTicks atomic.Int64 75 maxAirSupplyTicks atomic.Int64 76 77 cooldownMu sync.Mutex 78 cooldowns map[string]time.Time 79 // lastTickedWorld holds the world that the player was in, in the last tick. 80 lastTickedWorld *world.World 81 82 speed atomic.Float64 83 health *entity.HealthManager 84 experience *entity.ExperienceManager 85 effects *entity.EffectManager 86 87 lastXPPickup atomic.Value[time.Time] 88 immunityTicks atomic.Int64 89 90 deathMu sync.Mutex 91 deathPos *mgl64.Vec3 92 deathDimension world.Dimension 93 94 enchantSeed atomic.Int64 95 96 mc *entity.MovementComputer 97 98 collidedVertically, collidedHorizontally atomic.Bool 99 100 breaking atomic.Bool 101 breakingPos atomic.Value[cube.Pos] 102 lastBreakDuration time.Duration 103 104 breakParticleCounter atomic.Uint32 105 106 hunger *hungerManager 107 } 108 109 // New returns a new initialised player. A random UUID is generated for the player, so that it may be 110 // identified over network. You can either pass on player data you want to load or 111 // you can leave the data as nil to use default data. 112 func New(name string, skin skin.Skin, pos mgl64.Vec3) *Player { 113 p := &Player{} 114 *p = Player{ 115 inv: inventory.New(36, func(slot int, before, after item.Stack) { 116 if slot == int(p.heldSlot.Load()) { 117 p.broadcastItems(slot, before, after) 118 } 119 }), 120 enderChest: inventory.New(27, nil), 121 uuid: uuid.New(), 122 offHand: inventory.New(1, p.broadcastItems), 123 armour: inventory.NewArmour(p.broadcastArmour), 124 hunger: newHungerManager(), 125 health: entity.NewHealthManager(20, 20), 126 experience: entity.NewExperienceManager(), 127 effects: entity.NewEffectManager(), 128 gameMode: *atomic.NewValue[world.GameMode](world.GameModeSurvival), 129 h: *atomic.NewValue[Handler](NopHandler{}), 130 name: name, 131 skin: *atomic.NewValue(skin), 132 speed: *atomic.NewFloat64(0.1), 133 nameTag: *atomic.NewValue(name), 134 heldSlot: atomic.NewUint32(0), 135 locale: language.BritishEnglish, 136 breathing: true, 137 airSupplyTicks: *atomic.NewInt64(300), 138 maxAirSupplyTicks: *atomic.NewInt64(300), 139 enchantSeed: *atomic.NewInt64(rand.Int63()), 140 scale: *atomic.NewFloat64(1), 141 pos: *atomic.NewValue(pos), 142 cooldowns: make(map[string]time.Time), 143 mc: &entity.MovementComputer{Gravity: 0.08, Drag: 0.02, DragBeforeGravity: true}, 144 } 145 return p 146 } 147 148 // NewWithSession returns a new player for a network session, so that the network session can control the 149 // player. 150 // A set of additional fields must be provided to initialise the player with the client's data, such as the 151 // name and the skin of the player. You can either pass on player data you want to load or 152 // you can leave the data as nil to use default data. 153 func NewWithSession(name, xuid string, uuid uuid.UUID, skin skin.Skin, s *session.Session, pos mgl64.Vec3, data *Data) *Player { 154 p := New(name, skin, pos) 155 p.s, p.uuid, p.xuid, p.skin = *atomic.NewValue(s), uuid, xuid, *atomic.NewValue(skin) 156 p.inv, p.offHand, p.enderChest, p.armour, p.heldSlot = s.HandleInventories() 157 p.locale, _ = language.Parse(strings.Replace(s.ClientData().LanguageCode, "_", "-", 1)) 158 if data != nil { 159 p.load(*data) 160 } 161 return p 162 } 163 164 // Type returns the world.EntityType for the Player. 165 func (p *Player) Type() world.EntityType { 166 return Type{} 167 } 168 169 // Name returns the username of the player. If the player is controlled by a client, it is the username of 170 // the client. (Typically the XBOX Live name) 171 func (p *Player) Name() string { 172 return p.name 173 } 174 175 // UUID returns the UUID of the player. This UUID will remain consistent with an XBOX Live account, and will, 176 // unlike the name of the player, never change. 177 // It is therefore recommended using the UUID over the name of the player. Additionally, it is recommended to 178 // use the UUID over the XUID because of its standard format. 179 func (p *Player) UUID() uuid.UUID { 180 return p.uuid 181 } 182 183 // XUID returns the XBOX Live user ID of the player. It will remain consistent with the XBOX Live account, 184 // and will not change in the lifetime of an account. 185 // The XUID is a number that can be parsed as an int64. No more information on what it represents is 186 // available, and the UUID should be preferred. 187 // The XUID returned is empty if the Player is not connected to a network session or if the Player is not 188 // authenticated with XBOX Live. 189 func (p *Player) XUID() string { 190 return p.xuid 191 } 192 193 // DeviceID returns the device ID of the player. If the Player is not connected to a network session, an empty string is 194 // returned. Otherwise, the device ID the network session sent in the ClientData is returned. 195 func (p *Player) DeviceID() string { 196 if p.session() == session.Nop { 197 return "" 198 } 199 return p.session().ClientData().DeviceID 200 } 201 202 // DeviceModel returns the device model of the player. If the Player is not connected to a network session, an empty 203 // string is returned. Otherwise, the device model the network session sent in the ClientData is returned. 204 func (p *Player) DeviceModel() string { 205 if p.session() == session.Nop { 206 return "" 207 } 208 return p.session().ClientData().DeviceModel 209 } 210 211 // SelfSignedID returns the self-signed ID of the player. If the Player is not connected to a network session, an empty 212 // string is returned. Otherwise, the self-signed ID the network session sent in the ClientData is returned. 213 func (p *Player) SelfSignedID() string { 214 if p.session() == session.Nop { 215 return "" 216 } 217 return p.session().ClientData().SelfSignedID 218 } 219 220 // Addr returns the net.Addr of the Player. If the Player is not connected to a network session, nil is returned. 221 func (p *Player) Addr() net.Addr { 222 if p.session() == session.Nop { 223 return nil 224 } 225 return p.session().Addr() 226 } 227 228 // Skin returns the skin that a player is currently using. This skin will be visible to other players 229 // that the player is shown to. 230 // If the player was not connected to a network session, a default skin will be set. 231 func (p *Player) Skin() skin.Skin { 232 return p.skin.Load() 233 } 234 235 // SetSkin changes the skin of the player. This skin will be visible to other players that the player 236 // is shown to. 237 func (p *Player) SetSkin(skin skin.Skin) { 238 if p.Dead() { 239 return 240 } 241 ctx := event.C() 242 if p.Handler().HandleSkinChange(ctx, &skin); ctx.Cancelled() { 243 p.session().ViewSkin(p) 244 return 245 } 246 p.skin.Store(skin) 247 for _, v := range p.viewers() { 248 v.ViewSkin(p) 249 } 250 } 251 252 // Locale returns the language and locale of the Player, as selected in the Player's settings. 253 func (p *Player) Locale() language.Tag { 254 return p.locale 255 } 256 257 // Handle changes the current Handler of the player. As a result, events called by the player will call 258 // handlers of the Handler passed. 259 // Handle sets the player's Handler to NopHandler if nil is passed. 260 func (p *Player) Handle(h Handler) { 261 if h == nil { 262 h = NopHandler{} 263 } 264 p.h.Store(h) 265 } 266 267 // Message sends a formatted message to the player. The message is formatted following the rules of 268 // fmt.Sprintln, however the newline at the end is not written. 269 func (p *Player) Message(a ...any) { 270 p.session().SendMessage(format(a)) 271 } 272 273 // Messagef sends a formatted message using a specific format to the player. The message is formatted 274 // according to the fmt.Sprintf formatting rules. 275 func (p *Player) Messagef(f string, a ...any) { 276 p.session().SendMessage(fmt.Sprintf(f, a...)) 277 } 278 279 // SendPopup sends a formatted popup to the player. The popup is shown above the hotbar of the player and 280 // overwrites/is overwritten by the name of the item equipped. 281 // The popup is formatted following the rules of fmt.Sprintln without a newline at the end. 282 func (p *Player) SendPopup(a ...any) { 283 p.session().SendPopup(format(a)) 284 } 285 286 // SendTip sends a tip to the player. The tip is shown in the middle of the screen of the player. 287 // The tip is formatted following the rules of fmt.Sprintln without a newline at the end. 288 func (p *Player) SendTip(a ...any) { 289 p.session().SendTip(format(a)) 290 } 291 292 // SendJukeboxPopup sends a formatted jukebox popup to the player. This popup is shown above the hotbar of the player. 293 // The popup is close to the position of an action bar message and the text has no background. 294 func (p *Player) SendJukeboxPopup(a ...any) { 295 p.session().SendJukeboxPopup(format(a)) 296 } 297 298 // SendToast sends a toast to the player. This toast is shown at the top of the screen, similar to achievements or pack 299 // loading. 300 func (p *Player) SendToast(title, message string) { 301 p.session().SendToast(title, message) 302 } 303 304 // ResetFallDistance resets the player's fall distance. 305 func (p *Player) ResetFallDistance() { 306 p.fallDistance.Store(0) 307 } 308 309 // FallDistance returns the player's fall distance. 310 func (p *Player) FallDistance() float64 { 311 return p.fallDistance.Load() 312 } 313 314 // SendTitle sends a title to the player. The title may be configured to change the duration it is displayed 315 // and the text it shows. 316 // If non-empty, the subtitle is shown in a smaller font below the title. The same counts for the action text 317 // of the title, which is shown in a font similar to that of a tip/popup. 318 func (p *Player) SendTitle(t title.Title) { 319 p.session().SetTitleDurations(t.FadeInDuration(), t.Duration(), t.FadeOutDuration()) 320 if t.Text() != "" || t.Subtitle() != "" { 321 p.session().SendTitle(t.Text()) 322 if t.Subtitle() != "" { 323 p.session().SendSubtitle(t.Subtitle()) 324 } 325 } 326 if t.ActionText() != "" { 327 p.session().SendActionBarMessage(t.ActionText()) 328 } 329 } 330 331 // SendScoreboard sends a scoreboard to the player. The scoreboard will be present indefinitely until removed 332 // by the caller. 333 // SendScoreboard may be called at any time to change the scoreboard of the player. 334 func (p *Player) SendScoreboard(scoreboard *scoreboard.Scoreboard) { 335 p.session().SendScoreboard(scoreboard) 336 } 337 338 // RemoveScoreboard removes any scoreboard currently present on the screen of the player. Nothing happens if 339 // the player has no scoreboard currently active. 340 func (p *Player) RemoveScoreboard() { 341 p.session().RemoveScoreboard() 342 } 343 344 // SendBossBar sends a boss bar to the player, so that it will be shown indefinitely at the top of the 345 // player's screen. 346 // The boss bar may be removed by calling Player.RemoveBossBar(). 347 func (p *Player) SendBossBar(bar bossbar.BossBar) { 348 p.session().SendBossBar(bar.Text(), bar.Colour().Uint8(), bar.HealthPercentage()) 349 } 350 351 // RemoveBossBar removes any boss bar currently active on the player's screen. If no boss bar is currently 352 // present, nothing happens. 353 func (p *Player) RemoveBossBar() { 354 p.session().RemoveBossBar() 355 } 356 357 // Chat writes a message in the global chat (chat.Global). The message is prefixed with the name of the 358 // player and is formatted following the rules of fmt.Sprintln. 359 func (p *Player) Chat(msg ...any) { 360 message := format(msg) 361 ctx := event.C() 362 if p.Handler().HandleChat(ctx, &message); ctx.Cancelled() { 363 return 364 } 365 _, _ = fmt.Fprintf(chat.Global, "<%v> %v\n", p.name, message) 366 } 367 368 // ExecuteCommand executes a command passed as the player. If the command could not be found, or if the usage 369 // was incorrect, an error message is sent to the player. This message should start with a "/" for the command to be 370 // recognised. 371 func (p *Player) ExecuteCommand(commandLine string) { 372 if p.Dead() { 373 return 374 } 375 args := strings.Split(commandLine, " ") 376 command, ok := cmd.ByAlias(args[0][1:]) 377 if !ok { 378 o := &cmd.Output{} 379 o.Errorf("Unknown command: %v. Please check that the command exists and that you have permission to use it.", args[0]) 380 p.SendCommandOutput(o) 381 return 382 } 383 ctx := event.C() 384 if p.Handler().HandleCommandExecution(ctx, command, args[1:]); ctx.Cancelled() { 385 return 386 } 387 command.Execute(strings.Join(args[1:], " "), p) 388 } 389 390 // Transfer transfers the player to a server at the address passed. If the address could not be resolved, an 391 // error is returned. If it is returned, the player is closed and transferred to the server. 392 func (p *Player) Transfer(address string) error { 393 addr, err := net.ResolveUDPAddr("udp", address) 394 if err != nil { 395 return err 396 } 397 398 ctx := event.C() 399 if p.Handler().HandleTransfer(ctx, addr); ctx.Cancelled() { 400 return nil 401 } 402 p.session().Transfer(addr.IP, addr.Port) 403 return nil 404 } 405 406 // SendCommandOutput sends the output of a command to the player. 407 func (p *Player) SendCommandOutput(output *cmd.Output) { 408 p.session().SendCommandOutput(output) 409 } 410 411 // SendForm sends a form to the player for the client to fill out. Once the client fills it out, the Submit 412 // method of the form will be called. 413 // Note that the client may also close the form instead of filling it out, which will result in the form not 414 // having its Submit method called at all. Forms should never depend on the player actually filling out the 415 // form. 416 func (p *Player) SendForm(f form.Form) { 417 p.session().SendForm(f) 418 } 419 420 // ShowCoordinates enables the vanilla coordinates for the player. 421 func (p *Player) ShowCoordinates() { 422 p.session().EnableCoordinates(true) 423 } 424 425 // HideCoordinates disables the vanilla coordinates for the player. 426 func (p *Player) HideCoordinates() { 427 p.session().EnableCoordinates(false) 428 } 429 430 // EnableInstantRespawn enables the vanilla instant respawn for the player. 431 func (p *Player) EnableInstantRespawn() { 432 p.session().EnableInstantRespawn(true) 433 } 434 435 // DisableInstantRespawn disables the vanilla instant respawn for the player. 436 func (p *Player) DisableInstantRespawn() { 437 p.session().EnableInstantRespawn(false) 438 } 439 440 // SetNameTag changes the name tag displayed over the player in-game. Changing the name tag does not change 441 // the player's name in, for example, the player list or the chat. 442 func (p *Player) SetNameTag(name string) { 443 p.nameTag.Store(name) 444 p.updateState() 445 } 446 447 // NameTag returns the current name tag of the Player as shown in-game. It can be changed using SetNameTag. 448 func (p *Player) NameTag() string { 449 return p.nameTag.Load() 450 } 451 452 // SetScoreTag changes the score tag displayed over the player in-game. The score tag is displayed under the player's 453 // name tag. 454 func (p *Player) SetScoreTag(a ...any) { 455 p.scoreTag.Store(format(a)) 456 p.updateState() 457 } 458 459 // ScoreTag returns the current score tag of the player. It can be changed using SetScoreTag and by default is empty. 460 func (p *Player) ScoreTag() string { 461 return p.scoreTag.Load() 462 } 463 464 // SetSpeed sets the speed of the player. The value passed is the blocks/tick speed that the player will then 465 // obtain. 466 func (p *Player) SetSpeed(speed float64) { 467 p.speed.Store(speed) 468 p.session().SendSpeed(speed) 469 } 470 471 // Speed returns the speed of the player, returning a value that indicates the blocks/tick speed. The default 472 // speed of a player is 0.1. 473 func (p *Player) Speed() float64 { 474 return p.speed.Load() 475 } 476 477 // Health returns the current health of the player. It will always be lower than Player.MaxHealth(). 478 func (p *Player) Health() float64 { 479 return p.health.Health() 480 } 481 482 // MaxHealth returns the maximum amount of health that a player may have. The MaxHealth will always be higher 483 // than Player.Health(). 484 func (p *Player) MaxHealth() float64 { 485 return p.health.MaxHealth() 486 } 487 488 // SetMaxHealth sets the maximum health of the player. If the current health of the player is higher than the 489 // new maximum health, the health is set to the new maximum. 490 // SetMaxHealth panics if the max health passed is 0 or lower. 491 func (p *Player) SetMaxHealth(health float64) { 492 p.health.SetMaxHealth(health) 493 p.session().SendHealth(p.health) 494 } 495 496 // addHealth adds health to the player's current health. 497 func (p *Player) addHealth(health float64) { 498 p.health.AddHealth(health) 499 p.session().SendHealth(p.health) 500 } 501 502 // Heal heals the entity for a given amount of health. The source passed 503 // represents the cause of the healing, for example entity.FoodHealingSource if 504 // the entity healed by having a full food bar. If the health added to the 505 // original health exceeds the entity's max health, Heal will not add the full 506 // amount. If the health passed is negative, Heal will not do anything. 507 func (p *Player) Heal(health float64, source world.HealingSource) { 508 if p.Dead() || health < 0 || !p.GameMode().AllowsTakingDamage() { 509 return 510 } 511 ctx := event.C() 512 if p.Handler().HandleHeal(ctx, &health, source); ctx.Cancelled() { 513 return 514 } 515 p.addHealth(health) 516 } 517 518 // updateFallState is called to update the entities falling state. 519 func (p *Player) updateFallState(distanceThisTick float64) { 520 fallDistance := p.fallDistance.Load() 521 if p.OnGround() { 522 if fallDistance > 0 { 523 p.fall(fallDistance) 524 p.ResetFallDistance() 525 } 526 } else if distanceThisTick < fallDistance { 527 p.fallDistance.Sub(distanceThisTick) 528 } else { 529 p.ResetFallDistance() 530 } 531 } 532 533 // fall is called when a falling entity hits the ground. 534 func (p *Player) fall(distance float64) { 535 var ( 536 w = p.World() 537 pos = cube.PosFromVec3(p.Position()) 538 b = w.Block(pos) 539 ) 540 if len(b.Model().BBox(pos, w)) == 0 { 541 pos = pos.Sub(cube.Pos{0, 1}) 542 b = w.Block(pos) 543 } 544 if h, ok := b.(block.EntityLander); ok { 545 h.EntityLand(pos, w, p, &distance) 546 } 547 dmg := distance - 3 548 if boost, ok := p.Effect(effect.JumpBoost{}); ok { 549 dmg -= float64(boost.Level()) 550 } 551 if dmg < 0.5 { 552 return 553 } 554 p.Hurt(math.Ceil(dmg), entity.FallDamageSource{}) 555 } 556 557 // Hurt hurts the player for a given amount of damage. The source passed 558 // represents the cause of the damage, for example entity.AttackDamageSource if 559 // the player is attacked by another entity. If the final damage exceeds the 560 // health that the player currently has, the player is killed and will have to 561 // respawn. 562 // If the damage passed is negative, Hurt will not do anything. Hurt returns the 563 // final damage dealt to the Player and if the Player was vulnerable to this 564 // kind of damage. 565 func (p *Player) Hurt(dmg float64, src world.DamageSource) (float64, bool) { 566 if _, ok := p.Effect(effect.FireResistance{}); (ok && src.Fire()) || p.Dead() || !p.GameMode().AllowsTakingDamage() { 567 return 0, false 568 } 569 immunity := time.Second / 2 570 ctx := event.C() 571 if p.Handler().HandleHurt(ctx, &dmg, &immunity, src); ctx.Cancelled() { 572 return 0, false 573 } 574 if dmg < 0 { 575 return 0, true 576 } 577 578 totalDamage := p.FinalDamageFrom(dmg, src) 579 damageLeft := totalDamage 580 581 if a := p.Absorption(); a > 0 { 582 if damageLeft > a { 583 p.SetAbsorption(0) 584 damageLeft -= a 585 } else { 586 p.SetAbsorption(a - damageLeft) 587 damageLeft = 0 588 } 589 } 590 p.addHealth(-damageLeft) 591 592 if src.ReducedByArmour() { 593 p.Exhaust(0.1) 594 p.Armour().Damage(dmg, p.damageItem) 595 var origin world.Entity 596 if s, ok := src.(entity.AttackDamageSource); ok { 597 origin = s.Attacker 598 } else if s, ok := src.(entity.ProjectileDamageSource); ok { 599 origin = s.Owner 600 } 601 if l, ok := origin.(entity.Living); ok { 602 thornsDmg := p.Armour().ThornsDamage(p.damageItem) 603 if thornsDmg > 0 { 604 l.Hurt(thornsDmg, enchantment.ThornsDamageSource{Owner: p}) 605 } 606 } 607 } 608 609 w, pos := p.World(), p.Position() 610 for _, viewer := range p.viewers() { 611 viewer.ViewEntityAction(p, entity.HurtAction{}) 612 } 613 if src.Fire() { 614 w.PlaySound(pos, sound.Burning{}) 615 } else if _, ok := src.(entity.DrowningDamageSource); ok { 616 w.PlaySound(pos, sound.Drowning{}) 617 } 618 619 p.SetAttackImmunity(immunity) 620 if p.Dead() { 621 p.kill(src) 622 } 623 return totalDamage, true 624 } 625 626 // FinalDamageFrom resolves the final damage received by the player if it is attacked by the source passed 627 // with the damage passed. FinalDamageFrom takes into account things such as the armour worn and the 628 // enchantments on the individual pieces. 629 // The damage returned will be at the least 0. 630 func (p *Player) FinalDamageFrom(dmg float64, src world.DamageSource) float64 { 631 dmg = math.Max(dmg, 0) 632 633 dmg -= p.Armour().DamageReduction(dmg, src) 634 if res, ok := p.Effect(effect.Resistance{}); ok { 635 dmg *= effect.Resistance{}.Multiplier(src, res.Level()) 636 } 637 return dmg 638 } 639 640 // Explode ... 641 func (p *Player) Explode(explosionPos mgl64.Vec3, impact float64, c block.ExplosionConfig) { 642 diff := p.Position().Sub(explosionPos) 643 p.Hurt(math.Floor((impact*impact+impact)*3.5*c.Size+1), entity.ExplosionDamageSource{}) 644 p.knockBack(explosionPos, impact, diff[1]/diff.Len()*impact) 645 } 646 647 // SetAbsorption sets the absorption health of a player. This extra health shows as golden hearts and do not 648 // actually increase the maximum health. Once the hearts are lost, they will not regenerate. 649 // Nothing happens if a negative number is passed. 650 func (p *Player) SetAbsorption(health float64) { 651 health = math.Max(health, 0) 652 p.absorptionHealth.Store(health) 653 p.session().SendAbsorption(health) 654 } 655 656 // Absorption returns the absorption health that the player has. 657 func (p *Player) Absorption() float64 { 658 return p.absorptionHealth.Load() 659 } 660 661 // KnockBack knocks the player back with a given force and height. A source is passed which indicates the 662 // source of the velocity, typically the position of an attacking entity. The source is used to calculate the 663 // direction which the entity should be knocked back in. 664 func (p *Player) KnockBack(src mgl64.Vec3, force, height float64) { 665 if p.Dead() || !p.GameMode().AllowsTakingDamage() { 666 return 667 } 668 p.knockBack(src, force, height) 669 } 670 671 // knockBack is an unexported function that is used to knock the player back. This function does not check if the player 672 // can take damage or not. 673 func (p *Player) knockBack(src mgl64.Vec3, force, height float64) { 674 velocity := p.Position().Sub(src) 675 velocity[1] = 0 676 677 if velocity.Len() != 0 { 678 velocity = velocity.Normalize().Mul(force) 679 } 680 velocity[1] = height 681 682 p.SetVelocity(velocity.Mul(1 - p.Armour().KnockBackResistance())) 683 } 684 685 // AttackImmune checks if the player is currently immune to entity attacks, meaning it was recently attacked. 686 func (p *Player) AttackImmune() bool { 687 return p.immunityTicks.Load() > 0 688 } 689 690 // AttackImmunity returns the duration the player is immune to entity attacks. 691 func (p *Player) AttackImmunity() time.Duration { 692 return time.Duration(p.immunityTicks.Load()) * time.Second / 20 693 } 694 695 // SetAttackImmunity sets the duration the player is immune to entity attacks. 696 func (p *Player) SetAttackImmunity(d time.Duration) { 697 p.immunityTicks.Store(d.Milliseconds() / 50) 698 } 699 700 // Food returns the current food level of a player. The level returned is guaranteed to always be between 0 701 // and 20. Every half drumstick is one level. 702 func (p *Player) Food() int { 703 return p.hunger.Food() 704 } 705 706 // SetFood sets the food level of a player. The level passed must be in a range of 0-20. If the level passed 707 // is negative, the food level will be set to 0. If the level exceeds 20, the food level will be set to 20. 708 func (p *Player) SetFood(level int) { 709 p.hunger.SetFood(level) 710 p.sendFood() 711 } 712 713 // AddFood adds a number of points to the food level of the player. If the new food level is negative or if 714 // it exceeds 20, it will be set to 0 or 20 respectively. 715 func (p *Player) AddFood(points int) { 716 p.hunger.AddFood(points) 717 p.sendFood() 718 } 719 720 // Saturate saturates the player's food bar with the amount of food points and saturation points passed. The 721 // total saturation of the player will never exceed its total food level. 722 func (p *Player) Saturate(food int, saturation float64) { 723 p.hunger.saturate(food, saturation) 724 p.sendFood() 725 } 726 727 // sendFood sends the current food properties to the client. 728 func (p *Player) sendFood() { 729 p.hunger.mu.RLock() 730 defer p.hunger.mu.RUnlock() 731 p.session().SendFood(p.hunger.foodLevel, p.hunger.saturationLevel, p.hunger.exhaustionLevel) 732 } 733 734 // AddEffect adds an entity.Effect to the Player. If the effect is instant, it is applied to the Player 735 // immediately. If not, the effect is applied to the player every time the Tick method is called. 736 // AddEffect will overwrite any effects present if the level of the effect is higher than the existing one, or 737 // if the effects' levels are equal and the new effect has a longer duration. 738 func (p *Player) AddEffect(e effect.Effect) { 739 p.session().SendEffect(p.effects.Add(e, p)) 740 p.updateState() 741 } 742 743 // RemoveEffect removes any effect that might currently be active on the Player. 744 func (p *Player) RemoveEffect(e effect.Type) { 745 p.effects.Remove(e, p) 746 p.session().SendEffectRemoval(e) 747 p.updateState() 748 } 749 750 // Effect returns the effect instance and true if the Player has the effect. If not found, it will return an empty 751 // effect instance and false. 752 func (p *Player) Effect(e effect.Type) (effect.Effect, bool) { 753 return p.effects.Effect(e) 754 } 755 756 // Effects returns any effect currently applied to the entity. The returned effects are guaranteed not to have 757 // expired when returned. 758 func (p *Player) Effects() []effect.Effect { 759 return p.effects.Effects() 760 } 761 762 // BeaconAffected ... 763 func (*Player) BeaconAffected() bool { 764 return true 765 } 766 767 // Exhaust exhausts the player by the amount of points passed if the player is in survival mode. If the total 768 // exhaustion level exceeds 4, a saturation point, or food point, if saturation is 0, will be subtracted. 769 func (p *Player) Exhaust(points float64) { 770 if !p.GameMode().AllowsTakingDamage() || p.World().Difficulty().FoodRegenerates() { 771 return 772 } 773 before := p.hunger.Food() 774 p.hunger.exhaust(points) 775 if after := p.hunger.Food(); before != after { 776 // Temporarily set the food level back so that it hasn't yet changed once the event is handled. 777 p.hunger.SetFood(before) 778 779 ctx := event.C() 780 if p.Handler().HandleFoodLoss(ctx, before, &after); ctx.Cancelled() { 781 return 782 } 783 p.hunger.SetFood(after) 784 if before >= 7 && after <= 6 { 785 // The client will stop sprinting by itself too, but we force it just to be sure. 786 p.StopSprinting() 787 } 788 } 789 p.sendFood() 790 } 791 792 // Dead checks if the player is considered dead. True is returned if the health of the player is equal to or 793 // lower than 0. 794 func (p *Player) Dead() bool { 795 return p.Health() <= mgl64.Epsilon 796 } 797 798 // DeathPosition returns the last position the player was at when they died. If the player has never died, the third 799 // return value will be false. 800 func (p *Player) DeathPosition() (mgl64.Vec3, world.Dimension, bool) { 801 p.deathMu.Lock() 802 defer p.deathMu.Unlock() 803 if p.deathPos == nil { 804 return mgl64.Vec3{}, nil, false 805 } 806 return *p.deathPos, p.deathDimension, true 807 } 808 809 // kill kills the player, clearing its inventories and resetting it to its base state. 810 func (p *Player) kill(src world.DamageSource) { 811 for _, viewer := range p.viewers() { 812 viewer.ViewEntityAction(p, entity.DeathAction{}) 813 } 814 815 p.addHealth(-p.MaxHealth()) 816 817 keepInv := false 818 p.Handler().HandleDeath(src, &keepInv) 819 p.StopSneaking() 820 p.StopSprinting() 821 822 w, pos := p.World(), p.Position() 823 if !keepInv { 824 p.dropContents() 825 } 826 for _, e := range p.Effects() { 827 p.RemoveEffect(e.Type()) 828 } 829 830 p.deathMu.Lock() 831 defer p.deathMu.Unlock() 832 p.deathPos, p.deathDimension = &pos, w.Dimension() 833 834 // Wait a little before removing the entity. The client displays a death animation while the player is dying. 835 time.AfterFunc(time.Millisecond*1100, func() { 836 if p.session() == session.Nop { 837 _ = p.Close() 838 return 839 } 840 if p.Dead() { 841 p.SetInvisible() 842 // We have an actual client connected to this player: We change its position server side so that in 843 // the future, the client won't respawn on the death location when disconnecting. The client should 844 // not see the movement itself yet, though. 845 p.pos.Store(w.Spawn().Vec3()) 846 } 847 }) 848 } 849 850 // dropContents drops all items and experience of the Player on the ground in random directions. 851 func (p *Player) dropContents() { 852 w, pos := p.World(), p.Position() 853 for _, orb := range entity.NewExperienceOrbs(pos, int(math.Min(float64(p.experience.Level()*7), 100))) { 854 orb.SetVelocity(mgl64.Vec3{(rand.Float64()*0.2 - 0.1) * 2, rand.Float64() * 0.4, (rand.Float64()*0.2 - 0.1) * 2}) 855 w.AddEntity(orb) 856 } 857 p.experience.Reset() 858 p.session().SendExperience(p.experience) 859 860 p.session().EmptyUIInventory() 861 for _, it := range append(p.inv.Clear(), append(p.armour.Clear(), p.offHand.Clear()...)...) { 862 if _, ok := it.Enchantment(enchantment.CurseOfVanishing{}); ok { 863 continue 864 } 865 ent := entity.NewItem(it, pos) 866 ent.SetVelocity(mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1}) 867 w.AddEntity(ent) 868 } 869 } 870 871 // Respawn spawns the player after it dies, so that its health is replenished and it is spawned in the world 872 // again. Nothing will happen if the player does not have a session connected to it. 873 func (p *Player) Respawn() { 874 w := p.World() 875 if !p.Dead() || w == nil || p.session() == session.Nop { 876 return 877 } 878 p.addHealth(p.MaxHealth()) 879 p.hunger.Reset() 880 p.sendFood() 881 p.Extinguish() 882 p.ResetFallDistance() 883 884 // We can use the principle here that returning through a portal of a specific dimension inside that dimension will 885 // always bring us back to the overworld. 886 w = w.PortalDestination(w.Dimension()) 887 pos := w.PlayerSpawn(p.UUID()).Vec3Middle() 888 889 p.Handler().HandleRespawn(&pos, &w) 890 891 w.AddEntity(p) 892 p.Teleport(pos) 893 p.session().SendRespawn(pos) 894 895 p.SetVisible() 896 } 897 898 // StartSprinting makes a player start sprinting, increasing the speed of the player by 30% and making 899 // particles show up under the feet. The player will only start sprinting if its food level is high enough. 900 // If the player is sneaking when calling StartSprinting, it is stopped from sneaking. 901 func (p *Player) StartSprinting() { 902 if !p.hunger.canSprint() && p.GameMode().AllowsTakingDamage() { 903 return 904 } 905 ctx := event.C() 906 if p.Handler().HandleToggleSprint(ctx, true); ctx.Cancelled() { 907 return 908 } 909 if !p.sprinting.CAS(false, true) { 910 return 911 } 912 p.StopSneaking() 913 p.SetSpeed(p.Speed() * 1.3) 914 915 p.updateState() 916 } 917 918 // Sprinting checks if the player is currently sprinting. 919 func (p *Player) Sprinting() bool { 920 return p.sprinting.Load() 921 } 922 923 // StopSprinting makes a player stop sprinting, setting back the speed of the player to its original value. 924 func (p *Player) StopSprinting() { 925 ctx := event.C() 926 if p.Handler().HandleToggleSprint(ctx, false); ctx.Cancelled() { 927 return 928 } 929 if !p.sprinting.CAS(true, false) { 930 return 931 } 932 p.SetSpeed(p.Speed() / 1.3) 933 934 p.updateState() 935 } 936 937 // StartSneaking makes a player start sneaking. If the player is already sneaking, StartSneaking will not do 938 // anything. 939 // If the player is sprinting while StartSneaking is called, the sprinting is stopped. 940 func (p *Player) StartSneaking() { 941 ctx := event.C() 942 if p.Handler().HandleToggleSneak(ctx, true); ctx.Cancelled() { 943 return 944 } 945 if !p.sneaking.CAS(false, true) { 946 return 947 } 948 if !p.Flying() { 949 p.StopSprinting() 950 } 951 p.updateState() 952 } 953 954 // Sneaking checks if the player is currently sneaking. 955 func (p *Player) Sneaking() bool { 956 return p.sneaking.Load() 957 } 958 959 // StopSneaking makes a player stop sneaking if it currently is. If the player is not sneaking, StopSneaking 960 // will not do anything. 961 func (p *Player) StopSneaking() { 962 ctx := event.C() 963 if p.Handler().HandleToggleSneak(ctx, false); ctx.Cancelled() { 964 return 965 } 966 if !p.sneaking.CAS(true, false) { 967 return 968 } 969 p.updateState() 970 } 971 972 // StartSwimming makes the player start swimming if it is not currently doing so. If the player is sneaking 973 // while StartSwimming is called, the sneaking is stopped. 974 func (p *Player) StartSwimming() { 975 if !p.swimming.CAS(false, true) { 976 return 977 } 978 p.StopSneaking() 979 p.updateState() 980 } 981 982 // Swimming checks if the player is currently swimming. 983 func (p *Player) Swimming() bool { 984 return p.swimming.Load() 985 } 986 987 // StopSwimming makes the player stop swimming if it is currently doing so. 988 func (p *Player) StopSwimming() { 989 if !p.swimming.CAS(true, false) { 990 return 991 } 992 p.updateState() 993 } 994 995 // StartGliding makes the player start gliding if it is not currently doing so. 996 func (p *Player) StartGliding() { 997 if !p.gliding.CAS(false, true) { 998 return 999 } 1000 chest := p.Armour().Chestplate() 1001 if _, ok := chest.Item().(item.Elytra); !ok || chest.Durability() < 2 { 1002 return 1003 } 1004 p.updateState() 1005 } 1006 1007 // Gliding checks if the player is currently gliding. 1008 func (p *Player) Gliding() bool { 1009 return p.gliding.Load() 1010 } 1011 1012 // StopGliding makes the player stop gliding if it is currently doing so. 1013 func (p *Player) StopGliding() { 1014 if !p.gliding.CAS(true, false) { 1015 return 1016 } 1017 p.glideTicks.Store(0) 1018 p.updateState() 1019 } 1020 1021 // StartFlying makes the player start flying if they aren't already. It requires the player to be in a gamemode which 1022 // allows flying. 1023 func (p *Player) StartFlying() { 1024 if !p.GameMode().AllowsFlying() || !p.flying.CAS(false, true) { 1025 return 1026 } 1027 p.session().SendGameMode(p.GameMode()) 1028 } 1029 1030 // Flying checks if the player is currently flying. 1031 func (p *Player) Flying() bool { 1032 return p.flying.Load() 1033 } 1034 1035 // StopFlying makes the player stop flying if it currently is. 1036 func (p *Player) StopFlying() { 1037 if !p.flying.CAS(true, false) { 1038 return 1039 } 1040 p.session().SendGameMode(p.GameMode()) 1041 } 1042 1043 // Jump makes the player jump if they are on ground. It exhausts the player by 0.05 food points, an additional 0.15 1044 // is exhausted if the player is sprint jumping. 1045 func (p *Player) Jump() { 1046 if p.Dead() { 1047 return 1048 } 1049 1050 p.Handler().HandleJump() 1051 if p.OnGround() { 1052 jumpVel := 0.42 1053 if e, ok := p.Effect(effect.JumpBoost{}); ok { 1054 jumpVel = float64(e.Level()) / 10 1055 } 1056 p.vel.Store(mgl64.Vec3{0, jumpVel}) 1057 } 1058 if p.Sprinting() { 1059 p.Exhaust(0.2) 1060 } else { 1061 p.Exhaust(0.05) 1062 } 1063 } 1064 1065 // SetInvisible sets the player invisible, so that other players will not be able to see it. 1066 func (p *Player) SetInvisible() { 1067 if !p.invisible.CAS(false, true) { 1068 return 1069 } 1070 p.updateState() 1071 } 1072 1073 // SetVisible sets the player visible again, so that other players can see it again. If the player was already 1074 // visible, or if the player is in spectator mode, nothing happens. 1075 func (p *Player) SetVisible() { 1076 if !p.GameMode().Visible() { 1077 return 1078 } 1079 if _, ok := p.Effect(effect.Invisibility{}); ok { 1080 return 1081 } 1082 if !p.invisible.CAS(true, false) { 1083 return 1084 } 1085 p.updateState() 1086 } 1087 1088 // Invisible checks if the Player is currently invisible. 1089 func (p *Player) Invisible() bool { 1090 return p.invisible.Load() 1091 } 1092 1093 // SetImmobile prevents the player from moving around, but still allows them to look around. 1094 func (p *Player) SetImmobile() { 1095 if !p.immobile.CAS(false, true) { 1096 return 1097 } 1098 p.updateState() 1099 } 1100 1101 // SetMobile allows the player to freely move around again after being immobile. 1102 func (p *Player) SetMobile() { 1103 if !p.immobile.CAS(true, false) { 1104 return 1105 } 1106 p.updateState() 1107 } 1108 1109 // Immobile checks if the Player is currently immobile. 1110 func (p *Player) Immobile() bool { 1111 return p.immobile.Load() 1112 } 1113 1114 // FireProof checks if the Player is currently fireproof. True is returned if the player has a FireResistance effect or 1115 // if it is in creative mode. 1116 func (p *Player) FireProof() bool { 1117 if _, ok := p.Effect(effect.FireResistance{}); ok { 1118 return true 1119 } 1120 return !p.GameMode().AllowsTakingDamage() 1121 } 1122 1123 // OnFireDuration ... 1124 func (p *Player) OnFireDuration() time.Duration { 1125 return time.Duration(p.fireTicks.Load()) * time.Second / 20 1126 } 1127 1128 // SetOnFire ... 1129 func (p *Player) SetOnFire(duration time.Duration) { 1130 ticks := int64(duration.Seconds() * 20) 1131 if level := p.Armour().HighestEnchantmentLevel(enchantment.FireProtection{}); level > 0 { 1132 ticks -= int64(math.Floor(float64(ticks) * float64(level) * 0.15)) 1133 } 1134 p.fireTicks.Store(ticks) 1135 p.updateState() 1136 } 1137 1138 // Extinguish ... 1139 func (p *Player) Extinguish() { 1140 p.SetOnFire(0) 1141 } 1142 1143 // Inventory returns the inventory of the player. This inventory holds the items stored in the normal part of 1144 // the inventory and the hotbar. It also includes the item in the main hand as returned by Player.HeldItems(). 1145 func (p *Player) Inventory() *inventory.Inventory { 1146 return p.inv 1147 } 1148 1149 // Armour returns the armour inventory of the player. This inventory yields 4 slots, for the helmet, 1150 // chestplate, leggings and boots respectively. 1151 func (p *Player) Armour() *inventory.Armour { 1152 return p.armour 1153 } 1154 1155 // HeldItems returns the items currently held in the hands of the player. The first item stack returned is the 1156 // one held in the main hand, the second is held in the off-hand. 1157 // If no item was held in a hand, the stack returned has a count of 0. Stack.Empty() may be used to check if 1158 // the hand held anything. 1159 func (p *Player) HeldItems() (mainHand, offHand item.Stack) { 1160 offHand, _ = p.offHand.Item(0) 1161 mainHand, _ = p.inv.Item(int(p.heldSlot.Load())) 1162 return mainHand, offHand 1163 } 1164 1165 // SetHeldItems sets items to the main hand and the off-hand of the player. The Stacks passed may be empty 1166 // (Stack.Empty()) to clear the held item. 1167 func (p *Player) SetHeldItems(mainHand, offHand item.Stack) { 1168 _ = p.inv.SetItem(int(p.heldSlot.Load()), mainHand) 1169 _ = p.offHand.SetItem(0, offHand) 1170 } 1171 1172 // EnderChestInventory returns the player's ender chest inventory. Its accessed by the player when opening 1173 // ender chests anywhere. 1174 func (p *Player) EnderChestInventory() *inventory.Inventory { 1175 return p.enderChest 1176 } 1177 1178 // SetGameMode sets the game mode of a player. The game mode specifies the way that the player can interact 1179 // with the world that it is in. 1180 func (p *Player) SetGameMode(mode world.GameMode) { 1181 previous := p.gameMode.Swap(mode) 1182 p.session().SendGameMode(mode) 1183 for _, v := range p.viewers() { 1184 v.ViewEntityGameMode(p) 1185 } 1186 1187 if !mode.AllowsFlying() { 1188 p.StopFlying() 1189 } 1190 if !mode.Visible() { 1191 p.SetInvisible() 1192 } else if !previous.Visible() { 1193 p.SetVisible() 1194 } 1195 } 1196 1197 // GameMode returns the current game mode assigned to the player. If not changed, the game mode returned will 1198 // be the same as that of the world that the player spawns in. 1199 // The game mode may be changed using Player.SetGameMode(). 1200 func (p *Player) GameMode() world.GameMode { 1201 return p.gameMode.Load() 1202 } 1203 1204 // HasCooldown returns true if the item passed has an active cooldown, meaning it currently cannot be used again. If the 1205 // world.Item passed is nil, HasCooldown always returns false. 1206 func (p *Player) HasCooldown(item world.Item) bool { 1207 if item == nil { 1208 return false 1209 } 1210 p.cooldownMu.Lock() 1211 defer p.cooldownMu.Unlock() 1212 1213 name, _ := item.EncodeItem() 1214 otherTime, ok := p.cooldowns[name] 1215 if !ok { 1216 return false 1217 } 1218 if time.Now().After(otherTime) { 1219 delete(p.cooldowns, name) 1220 return false 1221 } 1222 return true 1223 } 1224 1225 // SetCooldown sets a cooldown for an item. If the world.Item passed is nil, nothing happens. 1226 func (p *Player) SetCooldown(item world.Item, cooldown time.Duration) { 1227 if item == nil { 1228 return 1229 } 1230 p.cooldownMu.Lock() 1231 defer p.cooldownMu.Unlock() 1232 1233 name, _ := item.EncodeItem() 1234 p.cooldowns[name] = time.Now().Add(cooldown) 1235 p.session().ViewItemCooldown(item, cooldown) 1236 } 1237 1238 // UseItem uses the item currently held in the player's main hand in the air. Generally, nothing happens, 1239 // unless the held item implements the item.Usable interface, in which case it will be activated. 1240 // This generally happens for items such as throwable items like snowballs. 1241 func (p *Player) UseItem() { 1242 var ( 1243 i, left = p.HeldItems() 1244 w = p.World() 1245 ctx = event.C() 1246 ) 1247 if p.HasCooldown(i.Item()) { 1248 return 1249 } 1250 if p.Handler().HandleItemUse(ctx); ctx.Cancelled() { 1251 return 1252 } 1253 i, left = p.HeldItems() 1254 it := i.Item() 1255 1256 if cd, ok := it.(item.Cooldown); ok { 1257 p.SetCooldown(it, cd.Cooldown()) 1258 } 1259 1260 if _, ok := it.(item.Releasable); ok { 1261 if !p.canRelease() { 1262 return 1263 } 1264 p.usingSince.Store(time.Now().UnixNano()) 1265 p.usingItem.Store(true) 1266 p.updateState() 1267 } 1268 1269 switch usable := it.(type) { 1270 case item.Usable: 1271 useCtx := p.useContext() 1272 if !usable.Use(w, p, useCtx) { 1273 return 1274 } 1275 // We only swing the player's arm if the item held actually does something. If it doesn't, there is no 1276 // reason to swing the arm. 1277 p.SwingArm() 1278 p.SetHeldItems(p.subtractItem(p.damageItem(i, useCtx.Damage), useCtx.CountSub), left) 1279 p.addNewItem(useCtx) 1280 case item.Consumable: 1281 if c, ok := usable.(interface{ CanConsume() bool }); ok && !c.CanConsume() { 1282 p.ReleaseItem() 1283 return 1284 } 1285 if !usable.AlwaysConsumable() && p.GameMode().AllowsTakingDamage() && p.Food() >= 20 { 1286 // The item.Consumable is not always consumable, the player is not in creative mode and the 1287 // food bar is filled: The item cannot be consumed. 1288 p.ReleaseItem() 1289 return 1290 } 1291 if p.usingItem.CAS(false, true) { 1292 // Consumable starts being consumed: Set the start timestamp and update the using state to viewers. 1293 p.usingSince.Store(time.Now().UnixNano()) 1294 p.updateState() 1295 return 1296 } 1297 // The player is currently using the item held. This is a signal the item was consumed, so we 1298 // consume it and start using it again. 1299 p.ReleaseItem() 1300 if duration := p.useDuration(); duration < usable.ConsumeDuration() { 1301 // The required duration for consuming this item was not met, so we don't consume it. 1302 return 1303 } 1304 1305 ctx = event.C() 1306 if p.Handler().HandleItemConsume(ctx, i); ctx.Cancelled() { 1307 // Consuming was cancelled, but the client will continue consuming the next item. 1308 p.usingSince.Store(time.Now().UnixNano()) 1309 return 1310 } 1311 p.SetHeldItems(p.subtractItem(i, 1), left) 1312 1313 useCtx := p.useContext() 1314 useCtx.NewItem = usable.Consume(w, p) 1315 p.addNewItem(useCtx) 1316 w.PlaySound(p.Position().Add(mgl64.Vec3{0, 1.5}), sound.Burp{}) 1317 } 1318 } 1319 1320 // ReleaseItem makes the Player release the item it is currently using. This is only applicable for items that 1321 // implement the item.Releasable interface. 1322 // If the Player is not currently using any item, ReleaseItem returns immediately. 1323 // ReleaseItem either aborts the using of the item or finished it, depending on the time that elapsed since 1324 // the item started being used. 1325 func (p *Player) ReleaseItem() { 1326 if !p.usingItem.CAS(true, false) || !p.canRelease() || !p.GameMode().AllowsInteraction() { 1327 return 1328 } 1329 ctx := p.useContext() 1330 i, _ := p.HeldItems() 1331 i.Item().(item.Releasable).Release(p, p.useDuration(), ctx) 1332 1333 p.handleUseContext(ctx) 1334 p.updateState() 1335 } 1336 1337 // canRelease returns whether the player can release the item currently held in the main hand. 1338 func (p *Player) canRelease() bool { 1339 held, _ := p.HeldItems() 1340 releasable, ok := held.Item().(item.Releasable) 1341 if !ok { 1342 return false 1343 } 1344 if p.GameMode().CreativeInventory() { 1345 return true 1346 } 1347 for _, req := range releasable.Requirements() { 1348 _, found := p.Inventory().FirstFunc(func(stack item.Stack) bool { 1349 name, _ := stack.Item().EncodeItem() 1350 otherName, _ := req.Item().EncodeItem() 1351 return name == otherName 1352 }) 1353 if !found { 1354 return false 1355 } 1356 } 1357 return true 1358 } 1359 1360 // handleUseContext handles the item.UseContext after the item has been used. 1361 func (p *Player) handleUseContext(ctx *item.UseContext) { 1362 i, left := p.HeldItems() 1363 1364 p.SetHeldItems(p.subtractItem(p.damageItem(i, ctx.Damage), ctx.CountSub), left) 1365 p.addNewItem(ctx) 1366 for _, it := range ctx.ConsumedItems { 1367 _ = p.Inventory().RemoveItem(it) 1368 } 1369 } 1370 1371 // useDuration returns the duration the player has been using the item in the main hand. 1372 func (p *Player) useDuration() time.Duration { 1373 return time.Duration(time.Now().UnixNano()-p.usingSince.Load()) + time.Second/20 1374 } 1375 1376 // UsingItem checks if the Player is currently using an item. True is returned if the Player is currently eating an 1377 // item or using it over a longer duration such as when using a bow. 1378 func (p *Player) UsingItem() bool { 1379 return p.usingItem.Load() 1380 } 1381 1382 // UseItemOnBlock uses the item held in the main hand of the player on a block at the position passed. The 1383 // player is assumed to have clicked the face passed with the relative click position clickPos. 1384 // If the item could not be used successfully, for example when the position is out of range, the method 1385 // returns immediately. 1386 // UseItemOnBlock does nothing if the block at the cube.Pos passed is of the type block.Air. 1387 func (p *Player) UseItemOnBlock(pos cube.Pos, face cube.Face, clickPos mgl64.Vec3) { 1388 w := p.World() 1389 if _, ok := w.Block(pos).(block.Air); ok || !p.canReach(pos.Vec3Centre()) { 1390 // The client used its item on a block that does not exist server-side or one it couldn't reach. Stop trying 1391 // to use the item immediately. 1392 p.resendBlocks(pos, w, face) 1393 return 1394 } 1395 ctx := event.C() 1396 if p.Handler().HandleItemUseOnBlock(ctx, pos, face, clickPos); ctx.Cancelled() { 1397 p.resendBlocks(pos, w, face) 1398 return 1399 } 1400 i, left := p.HeldItems() 1401 b := w.Block(pos) 1402 if act, ok := b.(block.Activatable); ok { 1403 // If a player is sneaking, it will not activate the block clicked, unless it is not holding any 1404 // items, in which case the block will be activated as usual. 1405 if !p.Sneaking() || i.Empty() { 1406 p.SwingArm() 1407 1408 // The block was activated: Blocks such as doors must always have precedence over the item being 1409 // used. 1410 if useCtx := p.useContext(); act.Activate(pos, face, p.World(), p, useCtx) { 1411 p.SetHeldItems(p.subtractItem(p.damageItem(i, useCtx.Damage), useCtx.CountSub), left) 1412 p.addNewItem(useCtx) 1413 return 1414 } 1415 } 1416 } 1417 if i.Empty() { 1418 return 1419 } 1420 switch ib := i.Item().(type) { 1421 case item.UsableOnBlock: 1422 // The item does something when used on a block. 1423 useCtx := p.useContext() 1424 if !ib.UseOnBlock(pos, face, clickPos, p.World(), p, useCtx) { 1425 return 1426 } 1427 p.SwingArm() 1428 p.SetHeldItems(p.subtractItem(p.damageItem(i, useCtx.Damage), useCtx.CountSub), left) 1429 p.addNewItem(useCtx) 1430 case world.Block: 1431 // The item IS a block, meaning it is being placed. 1432 replacedPos := pos 1433 if replaceable, ok := b.(block.Replaceable); !ok || !replaceable.ReplaceableBy(ib) { 1434 // The block clicked was either not replaceable, or not replaceable using the block passed. 1435 replacedPos = pos.Side(face) 1436 } 1437 if replaceable, ok := w.Block(replacedPos).(block.Replaceable); !ok || !replaceable.ReplaceableBy(ib) || replacedPos.OutOfBounds(w.Range()) { 1438 return 1439 } 1440 if !p.placeBlock(replacedPos, ib, false) || p.GameMode().CreativeInventory() { 1441 return 1442 } 1443 p.SetHeldItems(p.subtractItem(i, 1), left) 1444 } 1445 } 1446 1447 // UseItemOnEntity uses the item held in the main hand of the player on the entity passed, provided it is 1448 // within range of the player. 1449 // If the item held in the main hand of the player does nothing when used on an entity, nothing will happen. 1450 func (p *Player) UseItemOnEntity(e world.Entity) bool { 1451 if !p.canReach(e.Position()) { 1452 return false 1453 } 1454 ctx := event.C() 1455 if p.Handler().HandleItemUseOnEntity(ctx, e); ctx.Cancelled() { 1456 return false 1457 } 1458 i, left := p.HeldItems() 1459 usable, ok := i.Item().(item.UsableOnEntity) 1460 if !ok { 1461 return true 1462 } 1463 useCtx := p.useContext() 1464 if !usable.UseOnEntity(e, e.World(), p, useCtx) { 1465 return true 1466 } 1467 p.SwingArm() 1468 p.SetHeldItems(p.subtractItem(p.damageItem(i, useCtx.Damage), useCtx.CountSub), left) 1469 p.addNewItem(useCtx) 1470 return true 1471 } 1472 1473 // AttackEntity uses the item held in the main hand of the player to attack the entity passed, provided it is 1474 // within range of the player. 1475 // The damage dealt to the entity will depend on the item held by the player and any effects the player may 1476 // have. 1477 // If the player cannot reach the entity at its position, the method returns immediately. 1478 func (p *Player) AttackEntity(e world.Entity) bool { 1479 if !p.canReach(e.Position()) { 1480 return false 1481 } 1482 var ( 1483 force, height = 0.45, 0.3608 1484 _, slowFalling = p.Effect(effect.SlowFalling{}) 1485 _, blind = p.Effect(effect.Blindness{}) 1486 critical = !p.Sprinting() && !p.Flying() && p.FallDistance() > 0 && !slowFalling && !blind 1487 ) 1488 1489 ctx := event.C() 1490 if p.Handler().HandleAttackEntity(ctx, e, &force, &height, &critical); ctx.Cancelled() { 1491 return false 1492 } 1493 p.SwingArm() 1494 1495 i, _ := p.HeldItems() 1496 living, ok := e.(entity.Living) 1497 if !ok { 1498 return false 1499 } 1500 if living.AttackImmune() { 1501 return true 1502 } 1503 1504 dmg := i.AttackDamage() 1505 if strength, ok := p.Effect(effect.Strength{}); ok { 1506 dmg += dmg * effect.Strength{}.Multiplier(strength.Level()) 1507 } 1508 if weakness, ok := p.Effect(effect.Weakness{}); ok { 1509 dmg -= dmg * effect.Weakness{}.Multiplier(weakness.Level()) 1510 } 1511 if s, ok := i.Enchantment(enchantment.Sharpness{}); ok { 1512 dmg += (enchantment.Sharpness{}).Addend(s.Level()) 1513 } 1514 if critical { 1515 dmg *= 1.5 1516 } 1517 1518 n, vulnerable := living.Hurt(dmg, entity.AttackDamageSource{Attacker: p}) 1519 i, left := p.HeldItems() 1520 1521 p.World().PlaySound(entity.EyePosition(e), sound.Attack{Damage: !mgl64.FloatEqual(n, 0)}) 1522 if !vulnerable { 1523 return true 1524 } 1525 if critical { 1526 for _, v := range p.World().Viewers(living.Position()) { 1527 v.ViewEntityAction(living, entity.CriticalHitAction{}) 1528 } 1529 } 1530 1531 p.Exhaust(0.1) 1532 1533 if k, ok := i.Enchantment(enchantment.KnockBack{}); ok { 1534 inc := (enchantment.KnockBack{}).Force(k.Level()) 1535 force += inc 1536 height += inc 1537 } 1538 living.KnockBack(p.Position(), force, height) 1539 1540 if f, ok := i.Enchantment(enchantment.FireAspect{}); ok { 1541 if flammable, ok := living.(entity.Flammable); ok { 1542 flammable.SetOnFire((enchantment.FireAspect{}).Duration(f.Level())) 1543 } 1544 } 1545 1546 if durable, ok := i.Item().(item.Durable); ok { 1547 p.SetHeldItems(p.damageItem(i, durable.DurabilityInfo().AttackDurability), left) 1548 } 1549 return true 1550 } 1551 1552 // StartBreaking makes the player start breaking the block at the position passed using the item currently 1553 // held in its main hand. 1554 // If no block is present at the position, or if the block is out of range, StartBreaking will return 1555 // immediately and the block will not be broken. StartBreaking will stop the breaking of any block that the 1556 // player might be breaking before this method is called. 1557 func (p *Player) StartBreaking(pos cube.Pos, face cube.Face) { 1558 p.AbortBreaking() 1559 w := p.World() 1560 if _, air := w.Block(pos).(block.Air); air || !p.canReach(pos.Vec3Centre()) { 1561 // The block was either out of range or air, so it can't be broken by the player. 1562 return 1563 } 1564 if _, ok := w.Block(pos.Side(face)).(block.Fire); ok { 1565 // TODO: Add a way to cancel fire extinguishing. This is currently not possible to handle. 1566 w.SetBlock(pos.Side(face), nil, nil) 1567 w.PlaySound(pos.Vec3(), sound.FireExtinguish{}) 1568 return 1569 } 1570 1571 held, _ := p.HeldItems() 1572 if _, ok := held.Item().(item.Sword); ok && p.GameMode().CreativeInventory() { 1573 // Can't break blocks with a sword in creative mode. 1574 return 1575 } 1576 // Note: We intentionally store this regardless of whether the breaking proceeds, so that we 1577 // can resend the block to the client when it tries to break the block regardless. 1578 p.breakingPos.Store(pos) 1579 1580 ctx := event.C() 1581 if p.Handler().HandleStartBreak(ctx, pos); ctx.Cancelled() { 1582 return 1583 } 1584 if punchable, ok := w.Block(pos).(block.Punchable); ok { 1585 punchable.Punch(pos, face, w, p) 1586 } 1587 1588 p.breaking.Store(true) 1589 p.SwingArm() 1590 1591 if p.GameMode().CreativeInventory() { 1592 return 1593 } 1594 p.lastBreakDuration = p.breakTime(pos) 1595 for _, viewer := range p.viewers() { 1596 viewer.ViewBlockAction(pos, block.StartCrackAction{BreakTime: p.lastBreakDuration}) 1597 } 1598 } 1599 1600 // breakTime returns the time needed to break a block at the position passed, taking into account the item 1601 // held, if the player is on the ground/underwater and if the player has any effects. 1602 func (p *Player) breakTime(pos cube.Pos) time.Duration { 1603 held, _ := p.HeldItems() 1604 w := p.World() 1605 breakTime := block.BreakDuration(w.Block(pos), held) 1606 if !p.OnGround() { 1607 breakTime *= 5 1608 } 1609 if _, ok := p.Armour().Helmet().Enchantment(enchantment.AquaAffinity{}); p.insideOfWater(w) && !ok { 1610 breakTime *= 5 1611 } 1612 for _, e := range p.Effects() { 1613 lvl := e.Level() 1614 switch v := e.Type().(type) { 1615 case effect.Haste: 1616 breakTime = time.Duration(float64(breakTime) * v.Multiplier(lvl)) 1617 case effect.MiningFatigue: 1618 breakTime = time.Duration(float64(breakTime) * v.Multiplier(lvl)) 1619 case effect.ConduitPower: 1620 breakTime = time.Duration(float64(breakTime) * v.Multiplier(lvl)) 1621 } 1622 } 1623 return breakTime 1624 } 1625 1626 // FinishBreaking makes the player finish breaking the block it is currently breaking, or returns immediately 1627 // if the player isn't breaking anything. 1628 // FinishBreaking will stop the animation and break the block. 1629 func (p *Player) FinishBreaking() { 1630 pos := p.breakingPos.Load() 1631 if !p.breaking.Load() { 1632 p.resendBlock(pos, p.World()) 1633 return 1634 } 1635 p.AbortBreaking() 1636 p.BreakBlock(pos) 1637 } 1638 1639 // AbortBreaking makes the player stop breaking the block it is currently breaking, or returns immediately 1640 // if the player isn't breaking anything. 1641 // Unlike FinishBreaking, AbortBreaking does not stop the animation. 1642 func (p *Player) AbortBreaking() { 1643 if !p.breaking.CAS(true, false) { 1644 return 1645 } 1646 p.breakParticleCounter.Store(0) 1647 pos := p.breakingPos.Load() 1648 for _, viewer := range p.viewers() { 1649 viewer.ViewBlockAction(pos, block.StopCrackAction{}) 1650 } 1651 } 1652 1653 // ContinueBreaking makes the player continue breaking the block it started breaking after a call to 1654 // Player.StartBreaking(). 1655 // The face passed is used to display particles on the side of the block broken. 1656 func (p *Player) ContinueBreaking(face cube.Face) { 1657 if !p.breaking.Load() { 1658 return 1659 } 1660 pos := p.breakingPos.Load() 1661 1662 p.SwingArm() 1663 1664 w := p.World() 1665 b := w.Block(pos) 1666 w.AddParticle(pos.Vec3(), particle.PunchBlock{Block: b, Face: face}) 1667 1668 if p.breakParticleCounter.Add(1)%5 == 0 { 1669 // We send this sound only every so often. Vanilla doesn't send it every tick while breaking 1670 // either. Every 5 ticks seems accurate. 1671 w.PlaySound(pos.Vec3(), sound.BlockBreaking{Block: w.Block(pos)}) 1672 } 1673 breakTime := p.breakTime(pos) 1674 if breakTime != p.lastBreakDuration { 1675 for _, viewer := range p.viewers() { 1676 viewer.ViewBlockAction(pos, block.ContinueCrackAction{BreakTime: breakTime}) 1677 } 1678 p.lastBreakDuration = breakTime 1679 } 1680 } 1681 1682 // PlaceBlock makes the player place the block passed at the position passed, granted it is within the range 1683 // of the player. 1684 // An item.UseContext may be passed to obtain information on if the block placement was successful. (SubCount will 1685 // be incremented). Nil may also be passed for the context parameter. 1686 func (p *Player) PlaceBlock(pos cube.Pos, b world.Block, ctx *item.UseContext) { 1687 if !p.placeBlock(pos, b, ctx.IgnoreBBox) { 1688 return 1689 } 1690 if ctx != nil { 1691 ctx.CountSub++ 1692 } 1693 } 1694 1695 // placeBlock makes the player place the block passed at the position passed, granted it is within the range 1696 // of the player. A bool is returned indicating if a block was placed successfully. 1697 func (p *Player) placeBlock(pos cube.Pos, b world.Block, ignoreBBox bool) bool { 1698 w := p.World() 1699 if !p.canReach(pos.Vec3Centre()) || !p.GameMode().AllowsEditing() { 1700 p.resendBlocks(pos, w, cube.Faces()...) 1701 return false 1702 } 1703 if !ignoreBBox && p.obstructedPos(pos, b) { 1704 p.resendBlocks(pos, w, cube.Faces()...) 1705 return false 1706 } 1707 1708 ctx := event.C() 1709 if p.Handler().HandleBlockPlace(ctx, pos, b); ctx.Cancelled() { 1710 p.resendBlocks(pos, w, cube.Faces()...) 1711 return false 1712 } 1713 w.SetBlock(pos, b, nil) 1714 w.PlaySound(pos.Vec3(), sound.BlockPlace{Block: b}) 1715 p.SwingArm() 1716 return true 1717 } 1718 1719 // obstructedPos checks if the position passed is obstructed if the block passed is attempted to be placed. 1720 // The function returns true if there is an entity in the way that could prevent the block from being placed. 1721 func (p *Player) obstructedPos(pos cube.Pos, b world.Block) bool { 1722 w := p.World() 1723 blockBoxes := b.Model().BBox(pos, w) 1724 for i, box := range blockBoxes { 1725 blockBoxes[i] = box.Translate(pos.Vec3()) 1726 } 1727 1728 around := w.EntitiesWithin(cube.Box(-3, -3, -3, 3, 3, 3).Translate(pos.Vec3()), nil) 1729 for _, e := range around { 1730 switch e.Type().(type) { 1731 case entity.ItemType, entity.ArrowType: 1732 continue 1733 default: 1734 if cube.AnyIntersections(blockBoxes, e.Type().BBox(e).Translate(e.Position()).Grow(-1e-6)) { 1735 return true 1736 } 1737 } 1738 } 1739 return false 1740 } 1741 1742 // BreakBlock makes the player break a block in the world at a position passed. If the player is unable to 1743 // reach the block passed, the method returns immediately. 1744 func (p *Player) BreakBlock(pos cube.Pos) { 1745 w := p.World() 1746 b := w.Block(pos) 1747 if _, air := b.(block.Air); air { 1748 // Don't do anything if the position broken is already air. 1749 return 1750 } 1751 if !p.canReach(pos.Vec3Centre()) || !p.GameMode().AllowsEditing() { 1752 p.resendBlocks(pos, w) 1753 return 1754 } 1755 if _, breakable := b.(block.Breakable); !breakable && !p.GameMode().CreativeInventory() { 1756 p.resendBlocks(pos, w) 1757 return 1758 } 1759 held, _ := p.HeldItems() 1760 drops := p.drops(held, b) 1761 1762 xp := 0 1763 if breakable, ok := b.(block.Breakable); ok && !p.GameMode().CreativeInventory() { 1764 xp = breakable.BreakInfo().XPDrops.RandomValue() 1765 } 1766 1767 ctx := event.C() 1768 if p.Handler().HandleBlockBreak(ctx, pos, &drops, &xp); ctx.Cancelled() { 1769 p.resendBlocks(pos, w) 1770 return 1771 } 1772 held, left := p.HeldItems() 1773 1774 p.SwingArm() 1775 w.SetBlock(pos, nil, nil) 1776 w.AddParticle(pos.Vec3Centre(), particle.BlockBreak{Block: b}) 1777 1778 if breakable, ok := b.(block.Breakable); ok { 1779 info := breakable.BreakInfo() 1780 if info.BreakHandler != nil { 1781 info.BreakHandler(pos, w, p) 1782 } 1783 for _, orb := range entity.NewExperienceOrbs(pos.Vec3Centre(), xp) { 1784 orb.SetVelocity(mgl64.Vec3{(rand.Float64()*0.2 - 0.1) * 2, rand.Float64() * 0.4, (rand.Float64()*0.2 - 0.1) * 2}) 1785 w.AddEntity(orb) 1786 } 1787 } 1788 for _, drop := range drops { 1789 ent := entity.NewItem(drop, pos.Vec3Centre()) 1790 ent.SetVelocity(mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1}) 1791 w.AddEntity(ent) 1792 } 1793 1794 p.Exhaust(0.005) 1795 if block.BreaksInstantly(b, held) { 1796 return 1797 } 1798 if durable, ok := held.Item().(item.Durable); ok { 1799 p.SetHeldItems(p.damageItem(held, durable.DurabilityInfo().BreakDurability), left) 1800 } 1801 } 1802 1803 // drops returns the drops that the player can get from the block passed using the item held. 1804 func (p *Player) drops(held item.Stack, b world.Block) []item.Stack { 1805 t, ok := held.Item().(item.Tool) 1806 if !ok { 1807 t = item.ToolNone{} 1808 } 1809 var drops []item.Stack 1810 if container, ok := b.(block.Container); ok { 1811 // If the block is a container, it should drop its inventory contents regardless whether the 1812 // player is in creative mode or not. 1813 drops = container.Inventory().Items() 1814 if breakable, ok := b.(block.Breakable); ok && !p.GameMode().CreativeInventory() { 1815 if breakable.BreakInfo().Harvestable(t) { 1816 drops = append(drops, breakable.BreakInfo().Drops(t, held.Enchantments())...) 1817 } 1818 } 1819 container.Inventory().Clear() 1820 } else if breakable, ok := b.(block.Breakable); ok && !p.GameMode().CreativeInventory() { 1821 if breakable.BreakInfo().Harvestable(t) { 1822 drops = breakable.BreakInfo().Drops(t, held.Enchantments()) 1823 } 1824 } else if it, ok := b.(world.Item); ok && !p.GameMode().CreativeInventory() { 1825 drops = []item.Stack{item.NewStack(it, 1)} 1826 } 1827 return drops 1828 } 1829 1830 // PickBlock makes the player pick a block in the world at a position passed. If the player is unable to 1831 // pick the block, the method returns immediately. 1832 func (p *Player) PickBlock(pos cube.Pos) { 1833 if !p.canReach(pos.Vec3()) { 1834 return 1835 } 1836 1837 b := p.World().Block(pos) 1838 1839 var pickedItem item.Stack 1840 if pi, ok := b.(block.Pickable); ok { 1841 pickedItem = pi.Pick() 1842 } else if i, ok := b.(world.Item); ok { 1843 it, _ := world.ItemByName(i.EncodeItem()) 1844 pickedItem = item.NewStack(it, 1) 1845 } else { 1846 return 1847 } 1848 1849 slot, found := p.Inventory().First(pickedItem) 1850 if !found && !p.GameMode().CreativeInventory() { 1851 return 1852 } 1853 1854 ctx := event.C() 1855 if p.Handler().HandleBlockPick(ctx, pos, b); ctx.Cancelled() { 1856 return 1857 } 1858 _, offhand := p.HeldItems() 1859 1860 if found { 1861 if slot < 9 { 1862 _ = p.session().SetHeldSlot(slot) 1863 return 1864 } 1865 _ = p.Inventory().Swap(slot, int(p.heldSlot.Load())) 1866 return 1867 } 1868 1869 firstEmpty, emptyFound := p.Inventory().FirstEmpty() 1870 if !emptyFound { 1871 p.SetHeldItems(pickedItem, offhand) 1872 return 1873 } 1874 if firstEmpty < 8 { 1875 _ = p.session().SetHeldSlot(firstEmpty) 1876 _ = p.Inventory().SetItem(firstEmpty, pickedItem) 1877 return 1878 } 1879 _ = p.Inventory().Swap(firstEmpty, int(p.heldSlot.Load())) 1880 p.SetHeldItems(pickedItem, offhand) 1881 } 1882 1883 // Teleport teleports the player to a target position in the world. Unlike Move, it immediately changes the 1884 // position of the player, rather than showing an animation. 1885 func (p *Player) Teleport(pos mgl64.Vec3) { 1886 ctx := event.C() 1887 if p.Handler().HandleTeleport(ctx, pos); ctx.Cancelled() { 1888 return 1889 } 1890 p.teleport(pos) 1891 } 1892 1893 // teleport teleports the player to a target position in the world. It does not call the Handler of the 1894 // player. 1895 func (p *Player) teleport(pos mgl64.Vec3) { 1896 for _, v := range p.viewers() { 1897 v.ViewEntityTeleport(p, pos) 1898 } 1899 p.pos.Store(pos) 1900 p.vel.Store(mgl64.Vec3{}) 1901 p.ResetFallDistance() 1902 } 1903 1904 // Move moves the player from one position to another in the world, by adding the delta passed to the current 1905 // position of the player. 1906 // Move also rotates the player, adding deltaYaw and deltaPitch to the respective values. 1907 func (p *Player) Move(deltaPos mgl64.Vec3, deltaYaw, deltaPitch float64) { 1908 if p.Dead() || (deltaPos.ApproxEqual(mgl64.Vec3{}) && mgl64.FloatEqual(deltaYaw, 0) && mgl64.FloatEqual(deltaPitch, 0)) { 1909 return 1910 } 1911 if p.immobile.Load() { 1912 if mgl64.FloatEqual(deltaYaw, 0) && mgl64.FloatEqual(deltaPitch, 0) { 1913 // If only the position was changed, don't continue with the movement when immobile. 1914 return 1915 } 1916 // Still update rotation if it was changed. 1917 deltaPos = mgl64.Vec3{} 1918 } 1919 var ( 1920 w = p.World() 1921 pos = p.Position() 1922 yaw, pitch = p.Rotation().Elem() 1923 res, resYaw, resPitch = pos.Add(deltaPos), yaw + deltaYaw, pitch + deltaPitch 1924 ) 1925 ctx := event.C() 1926 if p.Handler().HandleMove(ctx, res, resYaw, resPitch); ctx.Cancelled() { 1927 if p.session() != session.Nop && pos.ApproxEqual(p.Position()) { 1928 // The position of the player was changed and the event cancelled. This means we still need to notify the 1929 // player of this movement change. 1930 p.teleport(pos) 1931 } 1932 return 1933 } 1934 for _, v := range p.viewers() { 1935 v.ViewEntityMovement(p, res, cube.Rotation{resYaw, resPitch}, p.OnGround()) 1936 } 1937 1938 p.pos.Store(res) 1939 p.yaw.Store(resYaw) 1940 p.pitch.Store(resPitch) 1941 if deltaPos.Len() <= 3 { 1942 // Only update velocity if the player is not moving too fast to prevent potential OOMs. 1943 p.vel.Store(deltaPos) 1944 p.checkBlockCollisions(deltaPos, w) 1945 } 1946 1947 horizontalVel := deltaPos 1948 horizontalVel[1] = 0 1949 if p.Gliding() { 1950 if deltaPos.Y() >= -0.5 { 1951 p.fallDistance.Store(1.0) 1952 } 1953 if p.collidedHorizontally.Load() { 1954 if force := horizontalVel.Len()*10.0 - 3.0; force > 0.0 && !p.AttackImmune() { 1955 w.PlaySound(p.Position(), sound.Fall{Distance: force}) 1956 p.Hurt(force, entity.GlideDamageSource{}) 1957 } 1958 } 1959 } 1960 1961 _, submergedBefore := w.Liquid(cube.PosFromVec3(pos.Add(mgl64.Vec3{0, p.EyeHeight()}))) 1962 _, submergedAfter := w.Liquid(cube.PosFromVec3(res.Add(mgl64.Vec3{0, p.EyeHeight()}))) 1963 if submergedBefore != submergedAfter { 1964 // Player wasn't either breathing before and no longer isn't, or wasn't breathing before and now is, 1965 // so send the updated metadata. 1966 p.session().ViewEntityState(p) 1967 } 1968 1969 p.onGround.Store(p.checkOnGround(w)) 1970 p.updateFallState(deltaPos[1]) 1971 1972 if p.Swimming() { 1973 p.Exhaust(0.01 * horizontalVel.Len()) 1974 } else if p.Sprinting() { 1975 p.Exhaust(0.1 * horizontalVel.Len()) 1976 } 1977 } 1978 1979 // World returns the world that the player is currently in. 1980 func (p *Player) World() *world.World { 1981 w, _ := world.OfEntity(p) 1982 return w 1983 } 1984 1985 // Position returns the current position of the player. It may be changed as the player moves or is moved 1986 // around the world. 1987 func (p *Player) Position() mgl64.Vec3 { 1988 return p.pos.Load() 1989 } 1990 1991 // Velocity returns the players current velocity. If there is an attached session, this will be empty. 1992 func (p *Player) Velocity() mgl64.Vec3 { 1993 return p.vel.Load() 1994 } 1995 1996 // SetVelocity updates the player's velocity. If there is an attached session, this will just send 1997 // the velocity to the player session for the player to update. 1998 func (p *Player) SetVelocity(velocity mgl64.Vec3) { 1999 if p.session() == session.Nop { 2000 p.vel.Store(velocity) 2001 return 2002 } 2003 for _, v := range p.viewers() { 2004 v.ViewEntityVelocity(p, velocity) 2005 } 2006 } 2007 2008 // Rotation returns the yaw and pitch of the player in degrees. Yaw is horizontal rotation (rotation around the 2009 // vertical axis, 0 when facing forward), pitch is vertical rotation (rotation around the horizontal axis, also 0 2010 // when facing forward). 2011 func (p *Player) Rotation() cube.Rotation { 2012 return cube.Rotation{p.yaw.Load(), p.pitch.Load()} 2013 } 2014 2015 // Collect makes the player collect the item stack passed, adding it to the inventory. The amount of items that could 2016 // be added is returned. 2017 func (p *Player) Collect(s item.Stack) int { 2018 if p.Dead() { 2019 return 0 2020 } 2021 if !p.GameMode().AllowsInteraction() { 2022 return 0 2023 } 2024 ctx := event.C() 2025 if p.Handler().HandleItemPickup(ctx, &s); ctx.Cancelled() { 2026 return 0 2027 } 2028 n, _ := p.Inventory().AddItem(s) 2029 return n 2030 } 2031 2032 // Experience returns the amount of experience the player has. 2033 func (p *Player) Experience() int { 2034 return p.experience.Experience() 2035 } 2036 2037 // EnchantmentSeed is a seed used to calculate random enchantments with enchantment tables. 2038 func (p *Player) EnchantmentSeed() int64 { 2039 return p.enchantSeed.Load() 2040 } 2041 2042 // ResetEnchantmentSeed resets the enchantment seed to a new random value. 2043 func (p *Player) ResetEnchantmentSeed() { 2044 p.enchantSeed.Store(rand.Int63()) 2045 } 2046 2047 // AddExperience adds experience to the player. 2048 func (p *Player) AddExperience(amount int) int { 2049 ctx := event.C() 2050 if p.Handler().HandleExperienceGain(ctx, &amount); ctx.Cancelled() { 2051 return 0 2052 } 2053 before := p.experience.Level() 2054 level, _ := p.experience.Add(amount) 2055 if level/5 > before/5 { 2056 p.PlaySound(sound.LevelUp{}) 2057 } else if amount > 0 { 2058 p.PlaySound(sound.Experience{}) 2059 } 2060 p.session().SendExperience(p.experience) 2061 return amount 2062 } 2063 2064 // RemoveExperience removes experience from the player. 2065 func (p *Player) RemoveExperience(amount int) { 2066 p.experience.Add(-amount) 2067 p.session().SendExperience(p.experience) 2068 } 2069 2070 // ExperienceLevel returns the experience level of the player. 2071 func (p *Player) ExperienceLevel() int { 2072 return p.experience.Level() 2073 } 2074 2075 // SetExperienceLevel sets the experience level of the player. The level must have a value between 0 and 2,147,483,647, 2076 // otherwise the method panics. 2077 func (p *Player) SetExperienceLevel(level int) { 2078 p.experience.SetLevel(level) 2079 p.session().SendExperience(p.experience) 2080 } 2081 2082 // ExperienceProgress returns the experience progress of the player. 2083 func (p *Player) ExperienceProgress() float64 { 2084 return p.experience.Progress() 2085 } 2086 2087 // SetExperienceProgress sets the experience progress of the player. The progress must have a value between 0.0 and 1.0, otherwise 2088 // the method panics. 2089 func (p *Player) SetExperienceProgress(progress float64) { 2090 p.experience.SetProgress(progress) 2091 p.session().SendExperience(p.experience) 2092 } 2093 2094 // CollectExperience makes the player collect the experience points passed, adding it to the experience manager. A bool 2095 // is returned indicating whether the player was able to collect the experience or not, due to the 100ms delay between 2096 // experience collection or if the player was dead or in a game mode that doesn't allow collection. 2097 func (p *Player) CollectExperience(value int) bool { 2098 if p.Dead() || !p.GameMode().AllowsInteraction() { 2099 return false 2100 } 2101 if time.Since(p.lastXPPickup.Load()) < time.Millisecond*100 { 2102 return false 2103 } 2104 value = p.mendItems(value) 2105 p.lastXPPickup.Store(time.Now()) 2106 if value > 0 { 2107 return p.AddExperience(value) > 0 2108 } 2109 2110 p.PlaySound(sound.Experience{}) 2111 return true 2112 } 2113 2114 // mendItems handles the mending enchantment when collecting experience, it then returns the leftover experience. 2115 func (p *Player) mendItems(xp int) int { 2116 mendingItems := make([]item.Stack, 0, 6) 2117 held, offHand := p.HeldItems() 2118 if _, ok := offHand.Enchantment(enchantment.Mending{}); ok && offHand.Durability() < offHand.MaxDurability() { 2119 mendingItems = append(mendingItems, offHand) 2120 } 2121 if _, ok := held.Enchantment(enchantment.Mending{}); ok && held.Durability() < held.MaxDurability() { 2122 mendingItems = append(mendingItems, held) 2123 } 2124 for _, i := range p.Armour().Items() { 2125 if i.Durability() == i.MaxDurability() { 2126 continue 2127 } 2128 if _, ok := i.Enchantment(enchantment.Mending{}); ok { 2129 mendingItems = append(mendingItems, i) 2130 } 2131 } 2132 length := len(mendingItems) 2133 if length == 0 { 2134 return xp 2135 } 2136 foundItem := mendingItems[rand.Intn(length)] 2137 repairAmount := math.Min(float64(foundItem.MaxDurability()-foundItem.Durability()), float64(xp*2)) 2138 repairedItem := foundItem.WithDurability(foundItem.Durability() + int(repairAmount)) 2139 if repairAmount >= 2 { 2140 // Mending removes 1 experience point for every 2 durability points. If the repaired durability is less than 2, 2141 // then no experience is removed. 2142 xp -= int(math.Ceil(repairAmount / 2)) 2143 } 2144 if offHand.Equal(foundItem) { 2145 p.SetHeldItems(held, repairedItem) 2146 } else if held.Equal(foundItem) { 2147 p.SetHeldItems(repairedItem, offHand) 2148 } else if slot, ok := p.Armour().Inventory().First(foundItem); ok { 2149 _ = p.Armour().Inventory().SetItem(slot, repairedItem) 2150 } 2151 return xp 2152 } 2153 2154 // Drop makes the player drop the item.Stack passed as an entity.Item, so that it may be picked up from the 2155 // ground. 2156 // The dropped item entity has a pickup delay of 2 seconds. 2157 // The number of items that was dropped in the end is returned. It is generally the count of the stack passed 2158 // or 0 if dropping the item.Stack was cancelled. 2159 func (p *Player) Drop(s item.Stack) int { 2160 e := entity.NewItemPickupDelay(s, p.Position().Add(mgl64.Vec3{0, 1.4}), time.Second*2) 2161 e.SetVelocity(p.Rotation().Vec3().Mul(0.4)) 2162 2163 ctx := event.C() 2164 if p.Handler().HandleItemDrop(ctx, e); ctx.Cancelled() { 2165 return 0 2166 } 2167 p.World().AddEntity(e) 2168 return s.Count() 2169 } 2170 2171 // OpenBlockContainer opens a block container, such as a chest, at the position passed. If no container was 2172 // present at that location, OpenBlockContainer does nothing. 2173 // OpenBlockContainer will also do nothing if the player has no session connected to it. 2174 func (p *Player) OpenBlockContainer(pos cube.Pos) { 2175 if p.session() != session.Nop { 2176 p.session().OpenBlockContainer(pos) 2177 } 2178 } 2179 2180 // HideEntity hides a world.Entity from the Player so that it can under no circumstance see it. Hidden entities can be 2181 // made visible again through a call to ShowEntity. 2182 func (p *Player) HideEntity(e world.Entity) { 2183 if p.session() != session.Nop && p != e { 2184 p.session().StopShowingEntity(e) 2185 } 2186 } 2187 2188 // ShowEntity shows a world.Entity previously hidden from the Player using HideEntity. It does nothing if the entity 2189 // wasn't currently hidden. 2190 func (p *Player) ShowEntity(e world.Entity) { 2191 if p.session() != session.Nop { 2192 p.session().StartShowingEntity(e) 2193 } 2194 } 2195 2196 // Latency returns a rolling average of latency between the sending and the receiving end of the connection of 2197 // the player. 2198 // The latency returned is updated continuously and is half the round trip time (RTT). 2199 // If the Player does not have a session associated with it, Latency returns 0. 2200 func (p *Player) Latency() time.Duration { 2201 if p.session() == session.Nop { 2202 return 0 2203 } 2204 return p.session().Latency() 2205 } 2206 2207 // Tick ticks the entity, performing actions such as checking if the player is still breaking a block. 2208 func (p *Player) Tick(w *world.World, current int64) { 2209 if p.Dead() { 2210 return 2211 } 2212 if p.lastTickedWorld != w { 2213 p.Handler().HandleChangeWorld(p.lastTickedWorld, w) 2214 } 2215 p.lastTickedWorld = w 2216 if _, ok := w.Liquid(cube.PosFromVec3(p.Position())); !ok { 2217 p.StopSwimming() 2218 if _, ok := p.Armour().Helmet().Item().(item.TurtleShell); ok { 2219 p.AddEffect(effect.New(effect.WaterBreathing{}, 1, time.Second*10).WithoutParticles()) 2220 } 2221 } 2222 2223 if _, ok := p.Armour().Chestplate().Item().(item.Elytra); ok && p.Gliding() { 2224 if t := p.glideTicks.Inc(); t%20 == 0 { 2225 d := p.damageItem(p.Armour().Chestplate(), 1) 2226 p.armour.SetChestplate(d) 2227 if d.Durability() < 2 { 2228 p.StopGliding() 2229 } 2230 } 2231 } 2232 2233 p.checkBlockCollisions(p.vel.Load(), w) 2234 p.onGround.Store(p.checkOnGround(w)) 2235 2236 p.effects.Tick(p) 2237 2238 p.tickFood(w) 2239 p.tickAirSupply(w) 2240 if p.immunityTicks.Load() > 0 { 2241 p.immunityTicks.Dec() 2242 } 2243 if p.Position()[1] < float64(w.Range()[0]) && p.GameMode().AllowsTakingDamage() && current%10 == 0 { 2244 p.Hurt(4, entity.VoidDamageSource{}) 2245 } 2246 if !p.AttackImmune() && p.insideOfSolid(w) { 2247 p.Hurt(1, entity.SuffocationDamageSource{}) 2248 } 2249 2250 if p.OnFireDuration() > 0 { 2251 p.fireTicks.Sub(1) 2252 if !p.GameMode().AllowsTakingDamage() || p.OnFireDuration() <= 0 || w.RainingAt(cube.PosFromVec3(p.Position())) { 2253 p.Extinguish() 2254 } 2255 if p.OnFireDuration()%time.Second == 0 && !p.AttackImmune() { 2256 p.Hurt(1, block.FireDamageSource{}) 2257 } 2258 } 2259 2260 if current%4 == 0 && p.usingItem.Load() { 2261 held, _ := p.HeldItems() 2262 if _, ok := held.Item().(item.Consumable); ok { 2263 // Eating particles seem to happen roughly every 4 ticks. 2264 for _, v := range p.viewers() { 2265 v.ViewEntityAction(p, entity.EatAction{}) 2266 } 2267 } 2268 } 2269 2270 p.cooldownMu.Lock() 2271 for it, ti := range p.cooldowns { 2272 if time.Now().After(ti) { 2273 delete(p.cooldowns, it) 2274 } 2275 } 2276 p.cooldownMu.Unlock() 2277 2278 if p.session() == session.Nop && !p.Immobile() { 2279 m := p.mc.TickMovement(p, p.Position(), p.Velocity(), cube.Rotation{p.yaw.Load(), p.pitch.Load()}) 2280 m.Send() 2281 2282 p.vel.Store(m.Velocity()) 2283 p.Move(m.Position().Sub(p.Position()), 0, 0) 2284 } else { 2285 p.vel.Store(mgl64.Vec3{}) 2286 } 2287 } 2288 2289 // tickAirSupply tick's the player's air supply, consuming it when underwater, and replenishing it when out of water. 2290 func (p *Player) tickAirSupply(w *world.World) { 2291 if !p.canBreathe(w) { 2292 if r, ok := p.Armour().Helmet().Enchantment(enchantment.Respiration{}); ok && rand.Float64() <= (enchantment.Respiration{}).Chance(r.Level()) { 2293 // Respiration grants a chance to avoid drowning damage every tick. 2294 return 2295 } 2296 2297 if ticks := p.airSupplyTicks.Dec(); ticks <= -20 { 2298 p.airSupplyTicks.Store(0) 2299 if !p.AttackImmune() { 2300 p.Hurt(2, entity.DrowningDamageSource{}) 2301 } 2302 } 2303 p.breathing = false 2304 p.updateState() 2305 } else if max := p.maxAirSupplyTicks.Load(); !p.breathing && p.airSupplyTicks.Load() < max { 2306 p.airSupplyTicks.Add(5) 2307 if p.airSupplyTicks.Load() >= max { 2308 p.breathing = true 2309 p.airSupplyTicks.Store(max) 2310 } 2311 p.updateState() 2312 } 2313 } 2314 2315 // tickFood ticks food related functionality, such as the depletion of the food bar and regeneration if it 2316 // is full enough. 2317 func (p *Player) tickFood(w *world.World) { 2318 p.hunger.foodTick++ 2319 if p.hunger.foodTick >= 80 { 2320 p.hunger.foodTick = 0 2321 } 2322 2323 if p.hunger.foodTick%10 == 0 && (p.hunger.canQuicklyRegenerate() || w.Difficulty().FoodRegenerates()) { 2324 if w.Difficulty().FoodRegenerates() { 2325 p.AddFood(1) 2326 } 2327 if p.hunger.foodTick%20 == 0 { 2328 p.regenerate(false) 2329 } 2330 } 2331 if p.hunger.foodTick == 0 { 2332 if p.hunger.canRegenerate() { 2333 p.regenerate(true) 2334 } else if p.hunger.starving() { 2335 p.starve(w) 2336 } 2337 } 2338 2339 if !p.hunger.canSprint() { 2340 p.StopSprinting() 2341 } 2342 } 2343 2344 // regenerate attempts to regenerate half a heart of health, typically caused by a full food bar. 2345 func (p *Player) regenerate(exhaust bool) { 2346 if p.Health() == p.MaxHealth() { 2347 return 2348 } 2349 p.Heal(1, entity.FoodHealingSource{}) 2350 if exhaust { 2351 p.Exhaust(6) 2352 } 2353 } 2354 2355 // starve deals starvation damage to the player if the difficult allows it. In peaceful mode, no damage will 2356 // ever be dealt. In easy mode, damage will only be dealt if the player has more than 10 health. In normal 2357 // mode, damage will only be dealt if the player has more than 2 health and in hard mode, damage will always 2358 // be dealt. 2359 func (p *Player) starve(w *world.World) { 2360 if p.Health() > w.Difficulty().StarvationHealthLimit() { 2361 p.Hurt(1, StarvationDamageSource{}) 2362 } 2363 } 2364 2365 // AirSupply returns the player's remaining air supply. 2366 func (p *Player) AirSupply() time.Duration { 2367 return time.Duration(p.airSupplyTicks.Load()) * time.Second / 20 2368 } 2369 2370 // SetAirSupply sets the player's remaining air supply. 2371 func (p *Player) SetAirSupply(duration time.Duration) { 2372 p.airSupplyTicks.Store(duration.Milliseconds() / 50) 2373 p.updateState() 2374 } 2375 2376 // MaxAirSupply returns the player's maximum air supply. 2377 func (p *Player) MaxAirSupply() time.Duration { 2378 return time.Duration(p.maxAirSupplyTicks.Load()) * time.Second / 20 2379 } 2380 2381 // SetMaxAirSupply sets the player's maximum air supply. 2382 func (p *Player) SetMaxAirSupply(duration time.Duration) { 2383 p.maxAirSupplyTicks.Store(duration.Milliseconds() / 50) 2384 p.updateState() 2385 } 2386 2387 // canBreathe returns true if the player can currently breathe. 2388 func (p *Player) canBreathe(w *world.World) bool { 2389 canTakeDamage := p.GameMode().AllowsTakingDamage() 2390 _, waterBreathing := p.effects.Effect(effect.WaterBreathing{}) 2391 _, conduitPower := p.effects.Effect(effect.ConduitPower{}) 2392 return !canTakeDamage || waterBreathing || conduitPower || (!p.insideOfWater(w) && !p.insideOfSolid(w)) 2393 } 2394 2395 // breathingDistanceBelowEyes is the lowest distance the player can be in water and still be able to breathe based on 2396 // the player's eye height. 2397 const breathingDistanceBelowEyes = 0.11111111 2398 2399 // insideOfWater returns true if the player is currently underwater. 2400 func (p *Player) insideOfWater(w *world.World) bool { 2401 pos := cube.PosFromVec3(entity.EyePosition(p)) 2402 if l, ok := w.Liquid(pos); ok { 2403 if _, ok := l.(block.Water); ok { 2404 d := float64(l.SpreadDecay()) + 1 2405 if l.LiquidFalling() { 2406 d = 1 2407 } 2408 return p.Position().Y() < (pos.Side(cube.FaceUp).Vec3().Y())-(d/9-breathingDistanceBelowEyes) 2409 } 2410 } 2411 return false 2412 } 2413 2414 // insideOfSolid returns true if the player is inside a solid block. 2415 func (p *Player) insideOfSolid(w *world.World) bool { 2416 pos := cube.PosFromVec3(entity.EyePosition(p)) 2417 b, box := w.Block(pos), p.Type().BBox(p).Translate(p.Position()) 2418 2419 _, solid := b.Model().(model.Solid) 2420 if !solid { 2421 // Not solid. 2422 return false 2423 } 2424 d, diffuses := b.(block.LightDiffuser) 2425 if diffuses && d.LightDiffusionLevel() == 0 { 2426 // Transparent. 2427 return false 2428 } 2429 for _, blockBox := range b.Model().BBox(pos, w) { 2430 if blockBox.Translate(pos.Vec3()).IntersectsWith(box) { 2431 return true 2432 } 2433 } 2434 return false 2435 } 2436 2437 // checkCollisions checks the player's block collisions. 2438 func (p *Player) checkBlockCollisions(vel mgl64.Vec3, w *world.World) { 2439 entityBBox := p.Type().BBox(p).Translate(p.Position()) 2440 deltaX, deltaY, deltaZ := vel[0], vel[1], vel[2] 2441 2442 p.checkEntityInsiders(w, entityBBox) 2443 2444 grown := entityBBox.Extend(vel).Grow(0.25) 2445 min, max := grown.Min(), grown.Max() 2446 minX, minY, minZ := int(math.Floor(min[0])), int(math.Floor(min[1])), int(math.Floor(min[2])) 2447 maxX, maxY, maxZ := int(math.Ceil(max[0])), int(math.Ceil(max[1])), int(math.Ceil(max[2])) 2448 2449 // A prediction of one BBox per block, plus an additional 2, in case 2450 blocks := make([]cube.BBox, 0, (maxX-minX)*(maxY-minY)*(maxZ-minZ)+2) 2451 for y := minY; y <= maxY; y++ { 2452 for x := minX; x <= maxX; x++ { 2453 for z := minZ; z <= maxZ; z++ { 2454 pos := cube.Pos{x, y, z} 2455 boxes := w.Block(pos).Model().BBox(pos, w) 2456 for _, box := range boxes { 2457 blocks = append(blocks, box.Translate(pos.Vec3())) 2458 } 2459 } 2460 } 2461 } 2462 2463 // epsilon is the epsilon used for thresholds for change used for change in position and velocity. 2464 const epsilon = 0.001 2465 2466 if !mgl64.FloatEqualThreshold(deltaY, 0, epsilon) { 2467 // First we move the entity BBox on the Y axis. 2468 for _, blockBBox := range blocks { 2469 deltaY = entityBBox.YOffset(blockBBox, deltaY) 2470 } 2471 entityBBox = entityBBox.Translate(mgl64.Vec3{0, deltaY}) 2472 } 2473 if !mgl64.FloatEqualThreshold(deltaX, 0, epsilon) { 2474 // Then on the X axis. 2475 for _, blockBBox := range blocks { 2476 deltaX = entityBBox.XOffset(blockBBox, deltaX) 2477 } 2478 entityBBox = entityBBox.Translate(mgl64.Vec3{deltaX}) 2479 } 2480 if !mgl64.FloatEqualThreshold(deltaZ, 0, epsilon) { 2481 // And finally on the Z axis. 2482 for _, blockBBox := range blocks { 2483 deltaZ = entityBBox.ZOffset(blockBBox, deltaZ) 2484 } 2485 } 2486 2487 p.collidedHorizontally.Store(!mgl64.FloatEqual(deltaX, vel[0]) || !mgl64.FloatEqual(deltaZ, vel[2])) 2488 p.collidedVertically.Store(!mgl64.FloatEqual(deltaY, vel[1])) 2489 } 2490 2491 // checkEntityInsiders checks if the player is colliding with any EntityInsider blocks. 2492 func (p *Player) checkEntityInsiders(w *world.World, entityBBox cube.BBox) { 2493 box := entityBBox.Grow(-0.0001) 2494 min, max := cube.PosFromVec3(box.Min()), cube.PosFromVec3(box.Max()) 2495 2496 for y := min[1]; y <= max[1]; y++ { 2497 for x := min[0]; x <= max[0]; x++ { 2498 for z := min[2]; z <= max[2]; z++ { 2499 blockPos := cube.Pos{x, y, z} 2500 b := w.Block(blockPos) 2501 if collide, ok := b.(block.EntityInsider); ok { 2502 collide.EntityInside(blockPos, w, p) 2503 if _, liquid := b.(world.Liquid); liquid { 2504 continue 2505 } 2506 } 2507 2508 if l, ok := w.Liquid(blockPos); ok { 2509 if collide, ok := l.(block.EntityInsider); ok { 2510 collide.EntityInside(blockPos, w, p) 2511 } 2512 } 2513 } 2514 } 2515 } 2516 } 2517 2518 // checkOnGround checks if the player is currently considered to be on the ground. 2519 func (p *Player) checkOnGround(w *world.World) bool { 2520 box := p.Type().BBox(p).Translate(p.Position()) 2521 2522 b := box.Grow(1) 2523 2524 min, max := cube.PosFromVec3(b.Min()), cube.PosFromVec3(b.Max()) 2525 for x := min[0]; x <= max[0]; x++ { 2526 for z := min[2]; z <= max[2]; z++ { 2527 for y := min[1]; y < max[1]; y++ { 2528 pos := cube.Pos{x, y, z} 2529 boxList := w.Block(pos).Model().BBox(pos, w) 2530 for _, bb := range boxList { 2531 if bb.GrowVec3(mgl64.Vec3{0, 0.05}).Translate(pos.Vec3()).IntersectsWith(box) { 2532 return true 2533 } 2534 } 2535 } 2536 } 2537 } 2538 return false 2539 } 2540 2541 // Scale returns the scale modifier of the Player. The default value for a normal scale is 1. A scale of 0 2542 // will make the Player completely invisible. 2543 func (p *Player) Scale() float64 { 2544 return p.scale.Load() 2545 } 2546 2547 // SetScale changes the scale modifier of the Player. The default value for a normal scale is 1. A scale of 0 2548 // will make the Player completely invisible. 2549 func (p *Player) SetScale(s float64) { 2550 p.scale.Store(s) 2551 p.updateState() 2552 } 2553 2554 // OnGround checks if the player is considered to be on the ground. 2555 func (p *Player) OnGround() bool { 2556 if p.session() == session.Nop { 2557 return p.mc.OnGround() 2558 } 2559 return p.onGround.Load() 2560 } 2561 2562 // EyeHeight returns the eye height of the player: 1.62, or 0.52 if the player is swimming. 2563 func (p *Player) EyeHeight() float64 { 2564 if p.swimming.Load() { 2565 return 0.52 2566 } 2567 return 1.62 2568 } 2569 2570 // PlaySound plays a world.Sound that only this Player can hear. Unlike World.PlaySound, it is not broadcast 2571 // to players around it. 2572 func (p *Player) PlaySound(sound world.Sound) { 2573 p.session().PlaySound(sound) 2574 } 2575 2576 // ShowParticle shows a particle that only this Player can see. Unlike World.AddParticle, it is not broadcast 2577 // to players around it. 2578 func (p *Player) ShowParticle(pos mgl64.Vec3, particle world.Particle) { 2579 p.session().ViewParticle(pos, particle) 2580 } 2581 2582 // OpenSign makes the player open the sign at the cube.Pos passed, with the specific side provided. The client will not 2583 // show the interface if it is not aware of a sign at the position. 2584 func (p *Player) OpenSign(pos cube.Pos, frontSide bool) { 2585 p.session().OpenSign(pos, frontSide) 2586 } 2587 2588 // EditSign edits the sign at the cube.Pos passed and writes the text passed to a sign at that position. If no sign is 2589 // present, an error is returned. 2590 func (p *Player) EditSign(pos cube.Pos, frontText, backText string) error { 2591 w := p.World() 2592 sign, ok := w.Block(pos).(block.Sign) 2593 if !ok { 2594 return fmt.Errorf("edit sign: no sign at position %v", pos) 2595 } 2596 2597 if sign.Waxed { 2598 return nil 2599 } else if frontText == sign.Front.Text && backText == sign.Back.Text { 2600 return nil 2601 } 2602 2603 ctx := event.C() 2604 if frontText != sign.Front.Text { 2605 if p.Handler().HandleSignEdit(ctx, true, sign.Front.Text, frontText); ctx.Cancelled() { 2606 return nil 2607 } 2608 sign.Front.Text = frontText 2609 sign.Front.Owner = p.XUID() 2610 } else { 2611 if p.Handler().HandleSignEdit(ctx, false, sign.Back.Text, backText); ctx.Cancelled() { 2612 return nil 2613 } 2614 sign.Back.Text = backText 2615 sign.Back.Owner = p.XUID() 2616 } 2617 w.SetBlock(pos, sign, nil) 2618 return nil 2619 } 2620 2621 // TurnLecternPage edits the lectern at the cube.Pos passed by turning the page to the page passed. If no lectern is 2622 // present, an error is returned. 2623 func (p *Player) TurnLecternPage(pos cube.Pos, page int) error { 2624 w := p.World() 2625 lectern, ok := w.Block(pos).(block.Lectern) 2626 if !ok { 2627 return fmt.Errorf("edit lectern: no lectern at position %v", pos) 2628 } 2629 2630 ctx := event.C() 2631 if p.Handler().HandleLecternPageTurn(ctx, pos, lectern.Page, &page); ctx.Cancelled() { 2632 return nil 2633 } 2634 2635 lectern.Page = page 2636 w.SetBlock(pos, lectern, nil) 2637 return nil 2638 } 2639 2640 // updateState updates the state of the player to all viewers of the player. 2641 func (p *Player) updateState() { 2642 for _, v := range p.viewers() { 2643 v.ViewEntityState(p) 2644 } 2645 } 2646 2647 // Breathing checks if the player is currently able to breathe. If it's underwater and the player does not 2648 // have the water breathing or conduit power effect, this returns false. 2649 // If the player is in creative or spectator mode, Breathing always returns true. 2650 func (p *Player) Breathing() bool { 2651 _, breathing := p.Effect(effect.WaterBreathing{}) 2652 _, conduitPower := p.Effect(effect.ConduitPower{}) 2653 _, submerged := p.World().Liquid(cube.PosFromVec3(entity.EyePosition(p))) 2654 return !p.GameMode().AllowsTakingDamage() || !submerged || breathing || conduitPower 2655 } 2656 2657 // SwingArm makes the player swing its arm. 2658 func (p *Player) SwingArm() { 2659 if p.Dead() { 2660 return 2661 } 2662 for _, v := range p.viewers() { 2663 v.ViewEntityAction(p, entity.SwingArmAction{}) 2664 } 2665 } 2666 2667 // PunchAir makes the player punch the air and plays the sound for attacking with no damage. 2668 func (p *Player) PunchAir() { 2669 if p.Dead() { 2670 return 2671 } 2672 ctx := event.C() 2673 if p.Handler().HandlePunchAir(ctx); ctx.Cancelled() { 2674 return 2675 } 2676 p.SwingArm() 2677 p.World().PlaySound(p.Position(), sound.Attack{}) 2678 } 2679 2680 // damageItem damages the item stack passed with the damage passed and returns the new stack. If the item 2681 // broke, a breaking sound is played. 2682 // If the player is not survival, the original stack is returned. 2683 func (p *Player) damageItem(s item.Stack, d int) item.Stack { 2684 if p.GameMode().CreativeInventory() || d == 0 || s.MaxDurability() == -1 { 2685 return s 2686 } 2687 ctx := event.C() 2688 if p.Handler().HandleItemDamage(ctx, s, d); ctx.Cancelled() { 2689 return s 2690 } 2691 if e, ok := s.Enchantment(enchantment.Unbreaking{}); ok { 2692 d = (enchantment.Unbreaking{}).Reduce(s.Item(), e.Level(), d) 2693 } 2694 if s = s.Damage(d); s.Empty() { 2695 p.World().PlaySound(p.Position(), sound.ItemBreak{}) 2696 } 2697 return s 2698 } 2699 2700 // subtractItem subtracts d from the count of the item stack passed and returns it, if the player is in 2701 // survival or adventure mode. 2702 func (p *Player) subtractItem(s item.Stack, d int) item.Stack { 2703 if !p.GameMode().CreativeInventory() && d != 0 { 2704 return s.Grow(-d) 2705 } 2706 return s 2707 } 2708 2709 // addNewItem adds the new item of the context passed to the inventory. 2710 func (p *Player) addNewItem(ctx *item.UseContext) { 2711 if (ctx.NewItemSurvivalOnly && p.GameMode().CreativeInventory()) || ctx.NewItem.Empty() { 2712 return 2713 } 2714 held, left := p.HeldItems() 2715 if held.Empty() { 2716 p.SetHeldItems(ctx.NewItem, left) 2717 return 2718 } 2719 n, err := p.Inventory().AddItem(ctx.NewItem) 2720 if err != nil { 2721 // Not all items could be added to the inventory, so drop the rest. 2722 p.Drop(ctx.NewItem.Grow(ctx.NewItem.Count() - n)) 2723 } 2724 if p.Dead() { 2725 p.dropContents() 2726 } 2727 } 2728 2729 // canReach checks if a player can reach a position with its current range. The range depends on if the player 2730 // is either survival or creative mode. 2731 func (p *Player) canReach(pos mgl64.Vec3) bool { 2732 const ( 2733 creativeRange = 14.0 2734 survivalRange = 8.0 2735 ) 2736 if !p.GameMode().AllowsInteraction() { 2737 return false 2738 } 2739 eyes := entity.EyePosition(p) 2740 2741 if p.GameMode().CreativeInventory() { 2742 return eyes.Sub(pos).Len() <= creativeRange && !p.Dead() 2743 } 2744 return eyes.Sub(pos).Len() <= survivalRange && !p.Dead() 2745 } 2746 2747 // Disconnect closes the player and removes it from the world. 2748 // Disconnect, unlike Close, allows a custom message to be passed to show to the player when it is 2749 // disconnected. The message is formatted following the rules of fmt.Sprintln without a newline at the end. 2750 func (p *Player) Disconnect(msg ...any) { 2751 p.once.Do(func() { 2752 p.close(format(msg)) 2753 }) 2754 } 2755 2756 // Close closes the player and removes it from the world. 2757 // Close disconnects the player with a 'Connection closed.' message. Disconnect should be used to disconnect a 2758 // player with a custom message. 2759 func (p *Player) Close() error { 2760 p.once.Do(func() { 2761 p.close("Connection closed.") 2762 }) 2763 return nil 2764 } 2765 2766 // close closes the player without disconnecting it. It executes code shared by both the closing and the 2767 // disconnecting of players. 2768 func (p *Player) close(msg string) { 2769 // If the player is being disconnected while they are dead, we respawn the player 2770 // so that the player logic works correctly the next time they join. 2771 if p.Dead() && p.session() != nil { 2772 p.Respawn() 2773 } 2774 p.h.Swap(NopHandler{}).HandleQuit() 2775 2776 if s := p.s.Swap(nil); s != nil { 2777 s.Disconnect(msg) 2778 s.CloseConnection() 2779 return 2780 } 2781 // Only remove the player from the world if it's not attached to a session. If it is attached to a session, the 2782 // session will remove the player once ready. 2783 p.World().RemoveEntity(p) 2784 } 2785 2786 // load reads the player data from the provider. It uses the default values if the provider 2787 // returns false. 2788 func (p *Player) load(data Data) { 2789 p.yaw.Store(data.Yaw) 2790 p.pitch.Store(data.Pitch) 2791 2792 p.health.SetMaxHealth(data.MaxHealth) 2793 p.health.AddHealth(data.Health - p.Health()) 2794 p.session().SendHealth(p.health) 2795 2796 p.absorptionHealth.Store(data.AbsorptionLevel) 2797 p.session().SendAbsorption(p.absorptionHealth.Load()) 2798 2799 p.hunger.SetFood(data.Hunger) 2800 p.hunger.foodTick = data.FoodTick 2801 p.hunger.exhaustionLevel, p.hunger.saturationLevel = data.ExhaustionLevel, data.SaturationLevel 2802 p.sendFood() 2803 2804 p.airSupplyTicks.Store(data.AirSupply) 2805 p.maxAirSupplyTicks.Store(data.MaxAirSupply) 2806 2807 p.experience.Add(data.Experience) 2808 p.session().SendExperience(p.experience) 2809 2810 p.enchantSeed.Store(data.EnchantmentSeed) 2811 2812 p.gameMode.Store(data.GameMode) 2813 for _, potion := range data.Effects { 2814 p.AddEffect(potion) 2815 } 2816 p.fireTicks.Store(data.FireTicks) 2817 p.fallDistance.Store(data.FallDistance) 2818 2819 p.loadInventory(data.Inventory) 2820 for slot, stack := range data.EnderChestInventory { 2821 _ = p.enderChest.SetItem(slot, stack) 2822 } 2823 } 2824 2825 // loadInventory loads all the data associated with the player inventory. 2826 func (p *Player) loadInventory(data InventoryData) { 2827 for slot, stack := range data.Items { 2828 _ = p.Inventory().SetItem(slot, stack) 2829 } 2830 _ = p.offHand.SetItem(0, data.OffHand) 2831 p.Armour().Set(data.Helmet, data.Chestplate, data.Leggings, data.Boots) 2832 } 2833 2834 // Data returns the player data that needs to be saved. This is used when the player 2835 // gets disconnected and the player provider needs to save the data. 2836 func (p *Player) Data() Data { 2837 yaw, pitch := p.Rotation().Elem() 2838 offHand, _ := p.offHand.Item(0) 2839 2840 p.hunger.mu.RLock() 2841 defer p.hunger.mu.RUnlock() 2842 2843 return Data{ 2844 UUID: p.UUID(), 2845 Username: p.Name(), 2846 Position: p.Position(), 2847 Velocity: mgl64.Vec3{}, 2848 Yaw: yaw, 2849 Pitch: pitch, 2850 Health: p.Health(), 2851 MaxHealth: p.MaxHealth(), 2852 Hunger: p.hunger.foodLevel, 2853 Experience: p.Experience(), 2854 EnchantmentSeed: p.EnchantmentSeed(), 2855 FoodTick: p.hunger.foodTick, 2856 AirSupply: p.airSupplyTicks.Load(), 2857 MaxAirSupply: p.maxAirSupplyTicks.Load(), 2858 ExhaustionLevel: p.hunger.exhaustionLevel, 2859 SaturationLevel: p.hunger.saturationLevel, 2860 AbsorptionLevel: p.Absorption(), 2861 GameMode: p.GameMode(), 2862 Inventory: InventoryData{ 2863 Items: p.Inventory().Slots(), 2864 Boots: p.armour.Boots(), 2865 Leggings: p.armour.Leggings(), 2866 Chestplate: p.armour.Chestplate(), 2867 Helmet: p.armour.Helmet(), 2868 OffHand: offHand, 2869 MainHandSlot: p.heldSlot.Load(), 2870 }, 2871 EnderChestInventory: p.enderChest.Slots(), 2872 Effects: p.Effects(), 2873 FireTicks: p.fireTicks.Load(), 2874 FallDistance: p.fallDistance.Load(), 2875 World: p.World(), 2876 } 2877 } 2878 2879 // session returns the network session of the player. If it has one, it is returned. If not, a no-op session 2880 // is returned. 2881 func (p *Player) session() *session.Session { 2882 if s := p.s.Load(); s != nil { 2883 return s 2884 } 2885 return session.Nop 2886 } 2887 2888 // useContext returns an item.UseContext initialised for a Player. 2889 func (p *Player) useContext() *item.UseContext { 2890 call := func(ctx *event.Context, slot int, it item.Stack, f func(ctx *event.Context, slot int, it item.Stack)) error { 2891 if ctx.Cancelled() { 2892 return fmt.Errorf("action was cancelled") 2893 } 2894 f(ctx, slot, it) 2895 if ctx.Cancelled() { 2896 return fmt.Errorf("action was cancelled") 2897 } 2898 return nil 2899 } 2900 return &item.UseContext{ 2901 SwapHeldWithArmour: func(i int) { 2902 src, dst, srcInv, dstInv := int(p.heldSlot.Load()), i, p.inv, p.armour.Inventory() 2903 srcIt, _ := srcInv.Item(src) 2904 dstIt, _ := dstInv.Item(dst) 2905 2906 ctx := event.C() 2907 _ = call(ctx, src, srcIt, srcInv.Handler().HandleTake) 2908 _ = call(ctx, src, dstIt, srcInv.Handler().HandlePlace) 2909 _ = call(ctx, dst, dstIt, dstInv.Handler().HandleTake) 2910 if err := call(ctx, dst, srcIt, dstInv.Handler().HandlePlace); err == nil { 2911 _ = srcInv.SetItem(src, dstIt) 2912 _ = dstInv.SetItem(dst, srcIt) 2913 p.PlaySound(sound.EquipItem{Item: srcIt.Item()}) 2914 } 2915 }, 2916 FirstFunc: func(comparable func(item.Stack) bool) (item.Stack, bool) { 2917 inv := p.Inventory() 2918 s, ok := inv.FirstFunc(comparable) 2919 if !ok { 2920 return item.Stack{}, false 2921 } 2922 it, _ := inv.Item(s) 2923 return it, ok 2924 }, 2925 } 2926 } 2927 2928 // Handler returns the Handler of the player. 2929 func (p *Player) Handler() Handler { 2930 return p.h.Load() 2931 } 2932 2933 // broadcastItems broadcasts the items held to viewers. 2934 func (p *Player) broadcastItems(int, item.Stack, item.Stack) { 2935 for _, viewer := range p.viewers() { 2936 viewer.ViewEntityItems(p) 2937 } 2938 } 2939 2940 // broadcastArmour broadcasts the armour equipped to viewers. 2941 func (p *Player) broadcastArmour(_ int, before, after item.Stack) { 2942 if before.Comparable(after) && before.Empty() == after.Empty() { 2943 // Only send armour if the type of the armour changed. 2944 return 2945 } 2946 for _, viewer := range p.viewers() { 2947 viewer.ViewEntityArmour(p) 2948 } 2949 } 2950 2951 // viewers returns a list of all viewers of the Player. 2952 func (p *Player) viewers() []world.Viewer { 2953 viewers := p.World().Viewers(p.Position()) 2954 var s world.Viewer = p.session() 2955 2956 if sliceutil.Index(viewers, s) == -1 { 2957 return append(viewers, s) 2958 } 2959 return viewers 2960 } 2961 2962 // resendBlocks resends blocks in a world.World at the cube.Pos passed and the block next to it at the cube.Face passed. 2963 func (p *Player) resendBlocks(pos cube.Pos, w *world.World, faces ...cube.Face) { 2964 if p.session() == session.Nop { 2965 return 2966 } 2967 p.resendBlock(pos, w) 2968 for _, f := range faces { 2969 p.resendBlock(pos.Side(f), w) 2970 } 2971 } 2972 2973 // resendBlock resends the block at a cube.Pos in the world.World passed. 2974 func (p *Player) resendBlock(pos cube.Pos, w *world.World) { 2975 b := w.Block(pos) 2976 p.session().ViewBlockUpdate(pos, b, 0) 2977 if _, ok := b.(world.Liquid); !ok { 2978 if liq, ok := w.Liquid(pos); ok { 2979 p.session().ViewBlockUpdate(pos, liq, 1) 2980 } 2981 } 2982 } 2983 2984 // format is a utility function to format a list of values to have spaces between them, but no newline at the 2985 // end, which is typically used for sending messages, popups and tips. 2986 func format(a []any) string { 2987 return strings.TrimSuffix(strings.TrimSuffix(fmt.Sprintln(a...), "\n"), "\n") 2988 }