github.com/df-mc/dragonfly@v0.9.13/server/session/world.go (about) 1 package session 2 3 import ( 4 "github.com/df-mc/dragonfly/server/entity/effect" 5 "image/color" 6 "math/rand" 7 "strings" 8 "time" 9 10 "github.com/df-mc/dragonfly/server/block" 11 "github.com/df-mc/dragonfly/server/block/cube" 12 "github.com/df-mc/dragonfly/server/entity" 13 "github.com/df-mc/dragonfly/server/internal/nbtconv" 14 "github.com/df-mc/dragonfly/server/item" 15 "github.com/df-mc/dragonfly/server/item/inventory" 16 "github.com/df-mc/dragonfly/server/world" 17 "github.com/df-mc/dragonfly/server/world/particle" 18 "github.com/df-mc/dragonfly/server/world/sound" 19 "github.com/go-gl/mathgl/mgl32" 20 "github.com/go-gl/mathgl/mgl64" 21 "github.com/google/uuid" 22 "github.com/sandertv/gophertunnel/minecraft/protocol" 23 "github.com/sandertv/gophertunnel/minecraft/protocol/packet" 24 ) 25 26 // NetworkEncodeableEntity is a world.EntityType where the save ID and network 27 // ID are not the same. 28 type NetworkEncodeableEntity interface { 29 // NetworkEncodeEntity returns the network type ID of the entity. This is 30 // NOT the save ID. 31 NetworkEncodeEntity() string 32 } 33 34 // OffsetEntity is a world.EntityType that has an additional offset when sent 35 // over network. This is mostly the case for older entities such as players and 36 // TNT. 37 type OffsetEntity interface { 38 NetworkOffset() float64 39 } 40 41 // entityHidden checks if a world.Entity is being explicitly hidden from the Session. 42 func (s *Session) entityHidden(e world.Entity) bool { 43 s.entityMutex.RLock() 44 _, ok := s.hiddenEntities[e] 45 s.entityMutex.RUnlock() 46 return ok 47 } 48 49 // ViewEntity ... 50 func (s *Session) ViewEntity(e world.Entity) { 51 if s.entityRuntimeID(e) == selfEntityRuntimeID { 52 s.ViewEntityState(e) 53 return 54 } 55 if s.entityHidden(e) { 56 return 57 } 58 var runtimeID uint64 59 60 _, controllable := e.(Controllable) 61 62 s.entityMutex.Lock() 63 if id, ok := s.entityRuntimeIDs[e]; ok && controllable { 64 runtimeID = id 65 } else { 66 s.currentEntityRuntimeID += 1 67 runtimeID = s.currentEntityRuntimeID 68 s.entityRuntimeIDs[e] = runtimeID 69 s.entities[runtimeID] = e 70 } 71 s.entityMutex.Unlock() 72 73 yaw, pitch := e.Rotation().Elem() 74 metadata := s.parseEntityMetadata(e) 75 76 id := e.Type().EncodeEntity() 77 switch v := e.(type) { 78 case Controllable: 79 actualPlayer := false 80 81 sessionMu.Lock() 82 for _, s := range sessions { 83 if s.c.UUID() == v.UUID() { 84 actualPlayer = true 85 break 86 } 87 } 88 sessionMu.Unlock() 89 if !actualPlayer { 90 s.writePacket(&packet.PlayerList{ActionType: packet.PlayerListActionAdd, Entries: []protocol.PlayerListEntry{{ 91 UUID: v.UUID(), 92 EntityUniqueID: int64(runtimeID), 93 Username: v.Name(), 94 Skin: skinToProtocol(v.Skin()), 95 }}}) 96 } 97 98 s.writePacket(&packet.AddPlayer{ 99 EntityMetadata: metadata, 100 EntityRuntimeID: runtimeID, 101 GameType: gameTypeFromMode(v.GameMode()), 102 HeadYaw: float32(yaw), 103 Pitch: float32(pitch), 104 Position: vec64To32(e.Position()), 105 UUID: v.UUID(), 106 Username: v.Name(), 107 Yaw: float32(yaw), 108 AbilityData: protocol.AbilityData{ 109 EntityUniqueID: int64(runtimeID), 110 Layers: []protocol.AbilityLayer{{ 111 Type: protocol.AbilityLayerTypeBase, 112 Abilities: protocol.AbilityCount - 1, 113 }}, 114 }, 115 }) 116 if !actualPlayer { 117 s.writePacket(&packet.PlayerList{ActionType: packet.PlayerListActionRemove, Entries: []protocol.PlayerListEntry{{ 118 UUID: v.UUID(), 119 }}}) 120 } 121 return 122 case *entity.Ent: 123 switch e.Type().(type) { 124 case entity.ItemType: 125 s.writePacket(&packet.AddItemActor{ 126 EntityUniqueID: int64(runtimeID), 127 EntityRuntimeID: runtimeID, 128 Item: instanceFromItem(v.Behaviour().(*entity.ItemBehaviour).Item()), 129 Position: vec64To32(v.Position()), 130 Velocity: vec64To32(v.Velocity()), 131 EntityMetadata: metadata, 132 }) 133 return 134 case entity.TextType: 135 metadata[protocol.EntityDataKeyVariant] = int32(world.BlockRuntimeID(block.Air{})) 136 case entity.FallingBlockType: 137 metadata[protocol.EntityDataKeyVariant] = int32(world.BlockRuntimeID(v.Behaviour().(*entity.FallingBlockBehaviour).Block())) 138 } 139 } 140 if v, ok := e.Type().(NetworkEncodeableEntity); ok { 141 id = v.NetworkEncodeEntity() 142 } 143 144 var vel mgl64.Vec3 145 if v, ok := e.(interface{ Velocity() mgl64.Vec3 }); ok { 146 vel = v.Velocity() 147 } 148 149 s.writePacket(&packet.AddActor{ 150 EntityUniqueID: int64(runtimeID), 151 EntityRuntimeID: runtimeID, 152 EntityType: id, 153 EntityMetadata: metadata, 154 Position: vec64To32(e.Position()), 155 Velocity: vec64To32(vel), 156 Pitch: float32(pitch), 157 Yaw: float32(yaw), 158 HeadYaw: float32(yaw), 159 }) 160 } 161 162 // ViewEntityGameMode ... 163 func (s *Session) ViewEntityGameMode(e world.Entity) { 164 if s.entityHidden(e) { 165 return 166 } 167 c, ok := e.(Controllable) 168 if !ok { 169 return 170 } 171 s.writePacket(&packet.UpdatePlayerGameType{ 172 GameType: gameTypeFromMode(c.GameMode()), 173 PlayerUniqueID: int64(s.entityRuntimeID(c)), 174 }) 175 } 176 177 // HideEntity ... 178 func (s *Session) HideEntity(e world.Entity) { 179 if s.entityRuntimeID(e) == selfEntityRuntimeID { 180 return 181 } 182 183 s.entityMutex.Lock() 184 id, ok := s.entityRuntimeIDs[e] 185 if _, controllable := e.(Controllable); !controllable { 186 delete(s.entityRuntimeIDs, e) 187 delete(s.entities, id) 188 } 189 s.entityMutex.Unlock() 190 if !ok { 191 // The entity was already removed some other way. We don't need to send a packet. 192 return 193 } 194 s.writePacket(&packet.RemoveActor{EntityUniqueID: int64(id)}) 195 } 196 197 // ViewEntityMovement ... 198 func (s *Session) ViewEntityMovement(e world.Entity, pos mgl64.Vec3, rot cube.Rotation, onGround bool) { 199 id := s.entityRuntimeID(e) 200 if id == selfEntityRuntimeID || s.entityHidden(e) { 201 return 202 } 203 204 flags := byte(0) 205 if onGround { 206 flags |= packet.MoveFlagOnGround 207 } 208 s.writePacket(&packet.MoveActorAbsolute{ 209 EntityRuntimeID: id, 210 Position: vec64To32(pos.Add(entityOffset(e))), 211 Rotation: vec64To32(mgl64.Vec3{rot.Pitch(), rot.Yaw(), rot.Yaw()}), 212 Flags: flags, 213 }) 214 } 215 216 // ViewEntityVelocity ... 217 func (s *Session) ViewEntityVelocity(e world.Entity, velocity mgl64.Vec3) { 218 if s.entityHidden(e) { 219 return 220 } 221 s.writePacket(&packet.SetActorMotion{ 222 EntityRuntimeID: s.entityRuntimeID(e), 223 Velocity: vec64To32(velocity), 224 }) 225 } 226 227 // entityOffset returns the offset that entities have client-side. 228 func entityOffset(e world.Entity) mgl64.Vec3 { 229 if offset, ok := e.Type().(OffsetEntity); ok { 230 return mgl64.Vec3{0, offset.NetworkOffset()} 231 } 232 return mgl64.Vec3{} 233 } 234 235 // ViewTime ... 236 func (s *Session) ViewTime(time int) { 237 s.writePacket(&packet.SetTime{Time: int32(time)}) 238 } 239 240 // ViewEntityTeleport ... 241 func (s *Session) ViewEntityTeleport(e world.Entity, position mgl64.Vec3) { 242 id := s.entityRuntimeID(e) 243 if s.entityHidden(e) { 244 return 245 } 246 247 yaw, pitch := e.Rotation().Elem() 248 if id == selfEntityRuntimeID { 249 s.chunkLoader.Move(position) 250 s.teleportPos.Store(&position) 251 } 252 253 s.writePacket(&packet.SetActorMotion{EntityRuntimeID: id}) 254 if _, ok := e.(Controllable); ok { 255 s.writePacket(&packet.MovePlayer{ 256 EntityRuntimeID: id, 257 Position: vec64To32(position.Add(entityOffset(e))), 258 Pitch: float32(pitch), 259 Yaw: float32(yaw), 260 HeadYaw: float32(yaw), 261 Mode: packet.MoveModeTeleport, 262 }) 263 return 264 } 265 s.writePacket(&packet.MoveActorAbsolute{ 266 EntityRuntimeID: id, 267 Position: vec64To32(position.Add(entityOffset(e))), 268 Rotation: vec64To32(mgl64.Vec3{pitch, yaw, yaw}), 269 Flags: packet.MoveFlagTeleport, 270 }) 271 } 272 273 // ViewEntityItems ... 274 func (s *Session) ViewEntityItems(e world.Entity) { 275 runtimeID := s.entityRuntimeID(e) 276 if runtimeID == selfEntityRuntimeID || s.entityHidden(e) { 277 // Don't view the items of the entity if the entity is the Controllable entity of the session. 278 return 279 } 280 c, ok := e.(item.Carrier) 281 if !ok { 282 return 283 } 284 285 mainHand, offHand := c.HeldItems() 286 287 // Show the main hand item. 288 s.writePacket(&packet.MobEquipment{ 289 EntityRuntimeID: runtimeID, 290 NewItem: instanceFromItem(mainHand), 291 }) 292 // Show the off-hand item. 293 s.writePacket(&packet.MobEquipment{ 294 EntityRuntimeID: runtimeID, 295 NewItem: instanceFromItem(offHand), 296 WindowID: protocol.WindowIDOffHand, 297 }) 298 } 299 300 // ViewEntityArmour ... 301 func (s *Session) ViewEntityArmour(e world.Entity) { 302 runtimeID := s.entityRuntimeID(e) 303 if runtimeID == selfEntityRuntimeID || s.entityHidden(e) { 304 // Don't view the items of the entity if the entity is the Controllable entity of the session. 305 return 306 } 307 armoured, ok := e.(interface { 308 Armour() *inventory.Armour 309 }) 310 if !ok { 311 return 312 } 313 314 inv := armoured.Armour() 315 316 // Show the main hand item. 317 s.writePacket(&packet.MobArmourEquipment{ 318 EntityRuntimeID: runtimeID, 319 Helmet: instanceFromItem(inv.Helmet()), 320 Chestplate: instanceFromItem(inv.Chestplate()), 321 Leggings: instanceFromItem(inv.Leggings()), 322 Boots: instanceFromItem(inv.Boots()), 323 }) 324 } 325 326 // ViewItemCooldown ... 327 func (s *Session) ViewItemCooldown(item world.Item, duration time.Duration) { 328 name, _ := item.EncodeItem() 329 s.writePacket(&packet.ClientStartItemCooldown{ 330 Category: strings.Split(name, ":")[1], 331 Duration: int32(duration.Milliseconds() / 50), 332 }) 333 } 334 335 // ViewParticle ... 336 func (s *Session) ViewParticle(pos mgl64.Vec3, p world.Particle) { 337 switch pa := p.(type) { 338 case particle.DragonEggTeleport: 339 xSign, ySign, zSign := 0, 0, 0 340 if pa.Diff.X() < 0 { 341 xSign = 1 << 24 342 } 343 if pa.Diff.Y() < 0 { 344 ySign = 1 << 25 345 } 346 if pa.Diff.Z() < 0 { 347 zSign = 1 << 26 348 } 349 350 s.writePacket(&packet.LevelEvent{ 351 EventType: packet.LevelEventParticlesDragonEgg, 352 Position: vec64To32(pos), 353 EventData: int32((((((abs(pa.Diff.X()) << 16) | (abs(pa.Diff.Y()) << 8)) | abs(pa.Diff.Z())) | xSign) | ySign) | zSign), 354 }) 355 case particle.Note: 356 s.writePacket(&packet.BlockEvent{ 357 EventType: pa.Instrument.Int32(), 358 EventData: int32(pa.Pitch), 359 Position: protocol.BlockPos{int32(pos.X()), int32(pos.Y()), int32(pos.Z())}, 360 }) 361 case particle.HugeExplosion: 362 s.writePacket(&packet.LevelEvent{ 363 EventType: packet.LevelEventParticlesExplosion, 364 Position: vec64To32(pos), 365 }) 366 case particle.BoneMeal: 367 s.writePacket(&packet.LevelEvent{ 368 EventType: packet.LevelEventParticleCropGrowth, 369 Position: vec64To32(pos), 370 }) 371 case particle.BlockForceField: 372 s.writePacket(&packet.LevelEvent{ 373 EventType: packet.LevelEventParticleDenyBlock, 374 Position: vec64To32(pos), 375 }) 376 case particle.BlockBreak: 377 s.writePacket(&packet.LevelEvent{ 378 EventType: packet.LevelEventParticlesDestroyBlock, 379 Position: vec64To32(pos), 380 EventData: int32(world.BlockRuntimeID(pa.Block)), 381 }) 382 case particle.PunchBlock: 383 s.writePacket(&packet.LevelEvent{ 384 EventType: packet.LevelEventParticlesCrackBlock, 385 Position: vec64To32(pos), 386 EventData: int32(world.BlockRuntimeID(pa.Block)) | (int32(pa.Face) << 24), 387 }) 388 case particle.EndermanTeleport: 389 s.writePacket(&packet.LevelEvent{ 390 EventType: packet.LevelEventParticlesTeleport, 391 Position: vec64To32(pos), 392 }) 393 case particle.Flame: 394 if pa.Colour != (color.RGBA{}) { 395 s.writePacket(&packet.LevelEvent{ 396 EventType: packet.LevelEventParticleLegacyEvent | 56, 397 Position: vec64To32(pos), 398 EventData: nbtconv.Int32FromRGBA(pa.Colour), 399 }) 400 return 401 } 402 s.writePacket(&packet.LevelEvent{ 403 EventType: packet.LevelEventParticleLegacyEvent | 8, 404 Position: vec64To32(pos), 405 }) 406 case particle.Evaporate: 407 s.writePacket(&packet.LevelEvent{ 408 EventType: packet.LevelEventParticlesEvaporateWater, 409 Position: vec64To32(pos), 410 }) 411 case particle.SnowballPoof: 412 s.writePacket(&packet.LevelEvent{ 413 EventType: packet.LevelEventParticleLegacyEvent | 15, 414 Position: vec64To32(pos), 415 }) 416 case particle.EggSmash: 417 rid, meta, _ := world.ItemRuntimeID(item.Egg{}) 418 s.writePacket(&packet.LevelEvent{ 419 EventType: packet.LevelEventParticleLegacyEvent | 14, 420 EventData: (rid << 16) | int32(meta), 421 Position: vec64To32(pos), 422 }) 423 case particle.Splash: 424 if (pa.Colour == color.RGBA{}) { 425 pa.Colour, _ = effect.ResultingColour(nil) 426 } 427 s.writePacket(&packet.LevelEvent{ 428 EventType: packet.LevelEventParticlesPotionSplash, 429 EventData: (int32(pa.Colour.A) << 24) | (int32(pa.Colour.R) << 16) | (int32(pa.Colour.G) << 8) | int32(pa.Colour.B), 430 Position: vec64To32(pos), 431 }) 432 case particle.Effect: 433 s.writePacket(&packet.LevelEvent{ 434 EventType: packet.LevelEventParticleLegacyEvent | 33, 435 EventData: (int32(pa.Colour.A) << 24) | (int32(pa.Colour.R) << 16) | (int32(pa.Colour.G) << 8) | int32(pa.Colour.B), 436 Position: vec64To32(pos), 437 }) 438 case particle.EntityFlame: 439 s.writePacket(&packet.LevelEvent{ 440 EventType: packet.LevelEventParticleLegacyEvent | 18, 441 Position: vec64To32(pos), 442 }) 443 case particle.Dust: 444 s.writePacket(&packet.LevelEvent{ 445 EventType: packet.LevelEventParticleLegacyEvent | 32, 446 Position: vec64To32(pos), 447 EventData: nbtconv.Int32FromRGBA(pa.Colour), 448 }) 449 case particle.WaterDrip: 450 s.writePacket(&packet.LevelEvent{ 451 EventType: packet.LevelEventParticleLegacyEvent | 27, 452 Position: vec64To32(pos), 453 }) 454 case particle.LavaDrip: 455 s.writePacket(&packet.LevelEvent{ 456 EventType: packet.LevelEventParticleLegacyEvent | 28, 457 Position: vec64To32(pos), 458 }) 459 case particle.Lava: 460 s.writePacket(&packet.LevelEvent{ 461 EventType: packet.LevelEventParticleLegacyEvent | 10, 462 Position: vec64To32(pos), 463 }) 464 } 465 } 466 467 // tierToSoundEvent converts an item.ArmourTier to a sound event associated with equipping it. 468 func tierToSoundEvent(tier item.ArmourTier) uint32 { 469 switch tier.(type) { 470 case item.ArmourTierLeather: 471 return packet.SoundEventEquipLeather 472 case item.ArmourTierGold: 473 return packet.SoundEventEquipGold 474 case item.ArmourTierChain: 475 return packet.SoundEventEquipChain 476 case item.ArmourTierIron: 477 return packet.SoundEventEquipIron 478 case item.ArmourTierDiamond: 479 return packet.SoundEventEquipDiamond 480 case item.ArmourTierNetherite: 481 return packet.SoundEventEquipNetherite 482 } 483 return packet.SoundEventEquipGeneric 484 } 485 486 // playSound plays a world.Sound at a position, disabling relative volume if set to true. 487 func (s *Session) playSound(pos mgl64.Vec3, t world.Sound, disableRelative bool) { 488 pk := &packet.LevelSoundEvent{ 489 Position: vec64To32(pos), 490 EntityType: ":", 491 ExtraData: -1, 492 DisableRelativeVolume: disableRelative, 493 } 494 switch so := t.(type) { 495 case sound.EquipItem: 496 switch i := so.Item.(type) { 497 case item.Helmet: 498 pk.SoundType = tierToSoundEvent(i.Tier) 499 case item.Chestplate: 500 pk.SoundType = tierToSoundEvent(i.Tier) 501 case item.Leggings: 502 pk.SoundType = tierToSoundEvent(i.Tier) 503 case item.Boots: 504 pk.SoundType = tierToSoundEvent(i.Tier) 505 case item.Elytra: 506 pk.SoundType = packet.SoundEventEquipElytra 507 default: 508 pk.SoundType = packet.SoundEventEquipGeneric 509 } 510 case sound.Note: 511 pk.SoundType = packet.SoundEventNote 512 pk.ExtraData = (so.Instrument.Int32() << 8) | int32(so.Pitch) 513 case sound.DoorCrash: 514 s.writePacket(&packet.LevelEvent{ 515 EventType: packet.LevelEventSoundZombieDoorCrash, 516 Position: vec64To32(pos), 517 }) 518 return 519 case sound.Explosion: 520 pk.SoundType = packet.SoundEventExplode 521 case sound.Thunder: 522 pk.SoundType, pk.EntityType = packet.SoundEventThunder, "minecraft:lightning_bolt" 523 case sound.Click: 524 s.writePacket(&packet.LevelEvent{ 525 EventType: packet.LevelEventSoundClick, 526 Position: vec64To32(pos), 527 }) 528 return 529 case sound.Pop: 530 s.writePacket(&packet.LevelEvent{ 531 EventType: packet.LevelEventSoundInfinityArrowPickup, 532 Position: vec64To32(pos), 533 }) 534 return 535 case sound.Teleport: 536 pk.SoundType = packet.SoundEventTeleport 537 case sound.ItemFrameAdd: 538 s.writePacket(&packet.LevelEvent{ 539 EventType: packet.LevelEventSoundAddItem, 540 Position: vec64To32(pos), 541 }) 542 return 543 case sound.ItemFrameRemove: 544 s.writePacket(&packet.LevelEvent{ 545 EventType: packet.LevelEventSoundItemFrameRemoveItem, 546 Position: vec64To32(pos), 547 }) 548 return 549 case sound.ItemFrameRotate: 550 s.writePacket(&packet.LevelEvent{ 551 EventType: packet.LevelEventSoundItemFrameRotateItem, 552 Position: vec64To32(pos), 553 }) 554 return 555 case sound.GhastWarning: 556 s.writePacket(&packet.LevelEvent{ 557 EventType: packet.LevelEventSoundGhastWarning, 558 Position: vec64To32(pos), 559 }) 560 return 561 case sound.GhastShoot: 562 s.writePacket(&packet.LevelEvent{ 563 EventType: packet.LevelEventSoundGhastFireball, 564 Position: vec64To32(pos), 565 }) 566 return 567 case sound.TNT: 568 s.writePacket(&packet.LevelEvent{ 569 EventType: packet.LevelEventSoundFuse, 570 Position: vec64To32(pos), 571 }) 572 return 573 case sound.FireworkLaunch: 574 pk.SoundType = packet.SoundEventLaunch 575 case sound.FireworkHugeBlast: 576 pk.SoundType = packet.SoundEventLargeBlast 577 case sound.FireworkBlast: 578 pk.SoundType = packet.SoundEventBlast 579 case sound.FireworkTwinkle: 580 pk.SoundType = packet.SoundEventTwinkle 581 case sound.FurnaceCrackle: 582 pk.SoundType = packet.SoundEventFurnaceUse 583 case sound.BlastFurnaceCrackle: 584 pk.SoundType = packet.SoundEventBlastFurnaceUse 585 case sound.SmokerCrackle: 586 pk.SoundType = packet.SoundEventSmokerUse 587 case sound.UseSpyglass: 588 pk.SoundType = packet.SoundEventUseSpyglass 589 case sound.StopUsingSpyglass: 590 pk.SoundType = packet.SoundEventStopUsingSpyglass 591 case sound.GoatHorn: 592 switch so.Horn { 593 case sound.Ponder(): 594 pk.SoundType = packet.SoundEventGoatCall0 595 case sound.Sing(): 596 pk.SoundType = packet.SoundEventGoatCall1 597 case sound.Seek(): 598 pk.SoundType = packet.SoundEventGoatCall2 599 case sound.Feel(): 600 pk.SoundType = packet.SoundEventGoatCall3 601 case sound.Admire(): 602 pk.SoundType = packet.SoundEventGoatCall4 603 case sound.Call(): 604 pk.SoundType = packet.SoundEventGoatCall5 605 case sound.Yearn(): 606 pk.SoundType = packet.SoundEventGoatCall6 607 case sound.Dream(): 608 pk.SoundType = packet.SoundEventGoatCall7 609 } 610 case sound.FireExtinguish: 611 pk.SoundType = packet.SoundEventExtinguishFire 612 case sound.Ignite: 613 pk.SoundType = packet.SoundEventIgnite 614 case sound.Burning: 615 pk.SoundType = packet.SoundEventPlayerHurtOnFire 616 case sound.Drowning: 617 pk.SoundType = packet.SoundEventPlayerHurtDrown 618 case sound.Fall: 619 pk.EntityType = "minecraft:player" 620 if so.Distance > 4 { 621 pk.SoundType = packet.SoundEventFallBig 622 break 623 } 624 pk.SoundType = packet.SoundEventFallSmall 625 case sound.Burp: 626 pk.SoundType = packet.SoundEventBurp 627 case sound.DoorOpen: 628 pk.SoundType, pk.ExtraData = packet.SoundEventDoorOpen, int32(world.BlockRuntimeID(so.Block)) 629 case sound.DoorClose: 630 pk.SoundType, pk.ExtraData = packet.SoundEventDoorClose, int32(world.BlockRuntimeID(so.Block)) 631 case sound.TrapdoorOpen: 632 pk.SoundType, pk.ExtraData = packet.SoundEventTrapdoorOpen, int32(world.BlockRuntimeID(so.Block)) 633 case sound.TrapdoorClose: 634 pk.SoundType, pk.ExtraData = packet.SoundEventTrapdoorClose, int32(world.BlockRuntimeID(so.Block)) 635 case sound.FenceGateOpen: 636 pk.SoundType, pk.ExtraData = packet.SoundEventFenceGateOpen, int32(world.BlockRuntimeID(so.Block)) 637 case sound.FenceGateClose: 638 pk.SoundType, pk.ExtraData = packet.SoundEventFenceGateClose, int32(world.BlockRuntimeID(so.Block)) 639 case sound.Deny: 640 pk.SoundType = packet.SoundEventDeny 641 case sound.BlockPlace: 642 pk.SoundType, pk.ExtraData = packet.SoundEventPlace, int32(world.BlockRuntimeID(so.Block)) 643 case sound.AnvilLand: 644 s.writePacket(&packet.LevelEvent{ 645 EventType: packet.LevelEventSoundAnvilLand, 646 Position: vec64To32(pos), 647 }) 648 return 649 case sound.AnvilUse: 650 s.writePacket(&packet.LevelEvent{ 651 EventType: packet.LevelEventSoundAnvilUsed, 652 Position: vec64To32(pos), 653 }) 654 return 655 case sound.AnvilBreak: 656 s.writePacket(&packet.LevelEvent{ 657 EventType: packet.LevelEventSoundAnvilBroken, 658 Position: vec64To32(pos), 659 }) 660 return 661 case sound.ChestClose: 662 pk.SoundType = packet.SoundEventChestClosed 663 case sound.ChestOpen: 664 pk.SoundType = packet.SoundEventChestOpen 665 case sound.BarrelClose: 666 pk.SoundType = packet.SoundEventBarrelClose 667 case sound.BarrelOpen: 668 pk.SoundType = packet.SoundEventBarrelOpen 669 case sound.BlockBreaking: 670 pk.SoundType, pk.ExtraData = packet.SoundEventHit, int32(world.BlockRuntimeID(so.Block)) 671 case sound.ItemBreak: 672 pk.SoundType = packet.SoundEventBreak 673 case sound.ItemUseOn: 674 pk.SoundType, pk.ExtraData = packet.SoundEventItemUseOn, int32(world.BlockRuntimeID(so.Block)) 675 case sound.Fizz: 676 pk.SoundType = packet.SoundEventFizz 677 case sound.GlassBreak: 678 pk.SoundType = packet.SoundEventGlass 679 case sound.Attack: 680 pk.SoundType, pk.EntityType = packet.SoundEventAttackStrong, "minecraft:player" 681 if !so.Damage { 682 pk.SoundType = packet.SoundEventAttackNoDamage 683 } 684 case sound.BucketFill: 685 if _, water := so.Liquid.(block.Water); water { 686 pk.SoundType = packet.SoundEventBucketFillWater 687 break 688 } 689 pk.SoundType = packet.SoundEventBucketFillLava 690 case sound.BucketEmpty: 691 if _, water := so.Liquid.(block.Water); water { 692 pk.SoundType = packet.SoundEventBucketEmptyWater 693 break 694 } 695 pk.SoundType = packet.SoundEventBucketEmptyLava 696 case sound.BowShoot: 697 pk.SoundType = packet.SoundEventBow 698 case sound.ArrowHit: 699 pk.SoundType = packet.SoundEventBowHit 700 case sound.ItemThrow: 701 pk.SoundType, pk.EntityType = packet.SoundEventThrow, "minecraft:player" 702 case sound.LevelUp: 703 pk.SoundType, pk.ExtraData = packet.SoundEventLevelUp, 0x10000000 704 case sound.Experience: 705 s.writePacket(&packet.LevelEvent{ 706 EventType: packet.LevelEventSoundExperienceOrbPickup, 707 Position: vec64To32(pos), 708 }) 709 return 710 case sound.MusicDiscPlay: 711 switch so.DiscType { 712 case sound.Disc13(): 713 pk.SoundType = packet.SoundEventRecord13 714 case sound.DiscCat(): 715 pk.SoundType = packet.SoundEventRecordCat 716 case sound.DiscBlocks(): 717 pk.SoundType = packet.SoundEventRecordBlocks 718 case sound.DiscChirp(): 719 pk.SoundType = packet.SoundEventRecordChirp 720 case sound.DiscFar(): 721 pk.SoundType = packet.SoundEventRecordFar 722 case sound.DiscMall(): 723 pk.SoundType = packet.SoundEventRecordMall 724 case sound.DiscMellohi(): 725 pk.SoundType = packet.SoundEventRecordMellohi 726 case sound.DiscStal(): 727 pk.SoundType = packet.SoundEventRecordStal 728 case sound.DiscStrad(): 729 pk.SoundType = packet.SoundEventRecordStrad 730 case sound.DiscWard(): 731 pk.SoundType = packet.SoundEventRecordWard 732 case sound.Disc11(): 733 pk.SoundType = packet.SoundEventRecord11 734 case sound.DiscWait(): 735 pk.SoundType = packet.SoundEventRecordWait 736 case sound.DiscOtherside(): 737 pk.SoundType = packet.SoundEventRecordOtherside 738 case sound.DiscPigstep(): 739 pk.SoundType = packet.SoundEventRecordPigstep 740 case sound.Disc5(): 741 pk.SoundType = packet.SoundEventRecord5 742 case sound.DiscRelic(): 743 pk.SoundType = packet.SoundEventRecordRelic 744 } 745 case sound.MusicDiscEnd: 746 pk.SoundType = packet.SoundEventRecordNull 747 case sound.FireCharge: 748 s.writePacket(&packet.LevelEvent{ 749 EventType: packet.LevelEventSoundBlazeFireball, 750 Position: vec64To32(pos), 751 }) 752 return 753 case sound.ComposterEmpty: 754 pk.SoundType = packet.SoundEventComposterEmpty 755 case sound.ComposterFill: 756 pk.SoundType = packet.SoundEventComposterFill 757 case sound.ComposterFillLayer: 758 pk.SoundType = packet.SoundEventComposterFillLayer 759 case sound.ComposterReady: 760 pk.SoundType = packet.SoundEventComposterReady 761 case sound.LecternBookPlace: 762 pk.SoundType = packet.SoundEventLecternBookPlace 763 } 764 s.writePacket(pk) 765 } 766 767 // PlaySound plays a world.Sound to the client. The volume is not dependent on the distance to the source if it is a 768 // sound of the LevelSoundEvent packet. 769 func (s *Session) PlaySound(t world.Sound) { 770 if s == Nop { 771 return 772 } 773 s.playSound(entity.EyePosition(s.c), t, true) 774 } 775 776 // ViewSound ... 777 func (s *Session) ViewSound(pos mgl64.Vec3, soundType world.Sound) { 778 s.playSound(pos, soundType, false) 779 } 780 781 // OpenSign ... 782 func (s *Session) OpenSign(pos cube.Pos, frontSide bool) { 783 blockPos := protocol.BlockPos{int32(pos[0]), int32(pos[1]), int32(pos[2])} 784 s.writePacket(&packet.OpenSign{ 785 Position: blockPos, 786 FrontSide: frontSide, 787 }) 788 } 789 790 // ViewFurnaceUpdate updates a furnace for the associated session based on previous times. 791 func (s *Session) ViewFurnaceUpdate(prevCookTime, cookTime, prevRemainingFuelTime, remainingFuelTime, prevMaxFuelTime, maxFuelTime time.Duration) { 792 if prevCookTime != cookTime { 793 s.writePacket(&packet.ContainerSetData{ 794 WindowID: byte(s.openedWindowID.Load()), 795 Key: packet.ContainerDataFurnaceTickCount, 796 Value: int32(cookTime.Milliseconds() / 50), 797 }) 798 } 799 800 if prevRemainingFuelTime != remainingFuelTime { 801 s.writePacket(&packet.ContainerSetData{ 802 WindowID: byte(s.openedWindowID.Load()), 803 Key: packet.ContainerDataFurnaceLitTime, 804 Value: int32(remainingFuelTime.Milliseconds() / 50), 805 }) 806 } 807 808 if prevMaxFuelTime != maxFuelTime { 809 s.writePacket(&packet.ContainerSetData{ 810 WindowID: byte(s.openedWindowID.Load()), 811 Key: packet.ContainerDataFurnaceLitDuration, 812 Value: int32(maxFuelTime.Milliseconds() / 50), 813 }) 814 } 815 } 816 817 // ViewBlockUpdate ... 818 func (s *Session) ViewBlockUpdate(pos cube.Pos, b world.Block, layer int) { 819 blockPos := protocol.BlockPos{int32(pos[0]), int32(pos[1]), int32(pos[2])} 820 s.writePacket(&packet.UpdateBlock{ 821 Position: blockPos, 822 NewBlockRuntimeID: world.BlockRuntimeID(b), 823 Flags: packet.BlockUpdateNetwork, 824 Layer: uint32(layer), 825 }) 826 if v, ok := b.(world.NBTer); ok { 827 NBTData := v.EncodeNBT() 828 NBTData["x"], NBTData["y"], NBTData["z"] = int32(pos.X()), int32(pos.Y()), int32(pos.Z()) 829 s.writePacket(&packet.BlockActorData{ 830 Position: blockPos, 831 NBTData: NBTData, 832 }) 833 } 834 } 835 836 // ViewEntityAction ... 837 func (s *Session) ViewEntityAction(e world.Entity, a world.EntityAction) { 838 switch act := a.(type) { 839 case entity.SwingArmAction: 840 if _, ok := e.(Controllable); ok { 841 if s.entityRuntimeID(e) == selfEntityRuntimeID && s.swingingArm.Load() { 842 return 843 } 844 s.writePacket(&packet.Animate{ 845 ActionType: packet.AnimateActionSwingArm, 846 EntityRuntimeID: s.entityRuntimeID(e), 847 }) 848 return 849 } 850 s.writePacket(&packet.ActorEvent{ 851 EntityRuntimeID: s.entityRuntimeID(e), 852 EventType: packet.ActorEventStartAttacking, 853 }) 854 case entity.HurtAction: 855 s.writePacket(&packet.ActorEvent{ 856 EntityRuntimeID: s.entityRuntimeID(e), 857 EventType: packet.ActorEventHurt, 858 }) 859 case entity.CriticalHitAction: 860 s.writePacket(&packet.Animate{ 861 ActionType: packet.AnimateActionCriticalHit, 862 EntityRuntimeID: s.entityRuntimeID(e), 863 }) 864 case entity.DeathAction: 865 s.writePacket(&packet.ActorEvent{ 866 EntityRuntimeID: s.entityRuntimeID(e), 867 EventType: packet.ActorEventDeath, 868 }) 869 case entity.PickedUpAction: 870 s.writePacket(&packet.TakeItemActor{ 871 ItemEntityRuntimeID: s.entityRuntimeID(e), 872 TakerEntityRuntimeID: s.entityRuntimeID(act.Collector), 873 }) 874 case entity.ArrowShakeAction: 875 s.writePacket(&packet.ActorEvent{ 876 EntityRuntimeID: s.entityRuntimeID(e), 877 EventType: packet.ActorEventShake, 878 EventData: int32(act.Duration.Milliseconds() / 50), 879 }) 880 case entity.FireworkExplosionAction: 881 s.writePacket(&packet.ActorEvent{ 882 EntityRuntimeID: s.entityRuntimeID(e), 883 EventType: packet.ActorEventFireworksExplode, 884 }) 885 case entity.EatAction: 886 if user, ok := e.(item.User); ok { 887 held, _ := user.HeldItems() 888 it := held.Item() 889 if held.Empty() { 890 // This can happen sometimes if the user switches between items very quickly, so just ignore the action. 891 return 892 } 893 if _, ok := it.(item.Consumable); !ok { 894 // Not consumable, refer to the comment above. 895 return 896 } 897 rid, meta, _ := world.ItemRuntimeID(it) 898 s.writePacket(&packet.ActorEvent{ 899 EntityRuntimeID: s.entityRuntimeID(e), 900 EventType: packet.ActorEventFeed, 901 // It's a little weird how the runtime ID is still shifted 16 bits to the left here, given the 902 // runtime ID already includes the meta, but it seems to work. 903 EventData: (rid << 16) | int32(meta), 904 }) 905 } 906 } 907 } 908 909 // ViewEntityState ... 910 func (s *Session) ViewEntityState(e world.Entity) { 911 s.writePacket(&packet.SetActorData{ 912 EntityRuntimeID: s.entityRuntimeID(e), 913 EntityMetadata: s.parseEntityMetadata(e), 914 }) 915 } 916 917 // ViewEntityAnimation ... 918 func (s *Session) ViewEntityAnimation(e world.Entity, animationName string) { 919 s.writePacket(&packet.AnimateEntity{ 920 Animation: animationName, 921 EntityRuntimeIDs: []uint64{ 922 s.entityRuntimeID(e), 923 }, 924 }) 925 } 926 927 // OpenBlockContainer ... 928 func (s *Session) OpenBlockContainer(pos cube.Pos) { 929 if s.containerOpened.Load() && s.openedPos.Load() == pos { 930 return 931 } 932 s.closeCurrentContainer() 933 934 w := s.c.World() 935 b := w.Block(pos) 936 if container, ok := b.(block.Container); ok { 937 s.openNormalContainer(container, pos) 938 return 939 } 940 // We hit a special kind of window like beacons, which are not actually opened server-side. 941 nextID := s.nextWindowID() 942 s.containerOpened.Store(true) 943 s.openedWindow.Store(inventory.New(1, nil)) 944 s.openedPos.Store(pos) 945 946 var containerType byte 947 switch b := b.(type) { 948 case block.CraftingTable: 949 containerType = protocol.ContainerTypeWorkbench 950 case block.EnchantingTable: 951 containerType = protocol.ContainerTypeEnchantment 952 case block.Anvil: 953 containerType = protocol.ContainerTypeAnvil 954 case block.Beacon: 955 containerType = protocol.ContainerTypeBeacon 956 case block.Loom: 957 containerType = protocol.ContainerTypeLoom 958 case block.Grindstone: 959 containerType = protocol.ContainerTypeGrindstone 960 case block.Stonecutter: 961 containerType = protocol.ContainerTypeStonecutter 962 case block.SmithingTable: 963 containerType = protocol.ContainerTypeSmithingTable 964 case block.EnderChest: 965 b.AddViewer(w, pos) 966 967 inv := s.c.EnderChestInventory() 968 s.openedWindow.Store(inv) 969 970 defer s.sendInv(inv, uint32(nextID)) 971 } 972 973 s.openedContainerID.Store(uint32(containerType)) 974 s.writePacket(&packet.ContainerOpen{ 975 WindowID: nextID, 976 ContainerType: containerType, 977 ContainerPosition: protocol.BlockPos{int32(pos[0]), int32(pos[1]), int32(pos[2])}, 978 ContainerEntityUniqueID: -1, 979 }) 980 } 981 982 // openNormalContainer opens a normal container that can hold items in it server-side. 983 func (s *Session) openNormalContainer(b block.Container, pos cube.Pos) { 984 b.AddViewer(s, s.c.World(), pos) 985 986 nextID := s.nextWindowID() 987 s.containerOpened.Store(true) 988 s.openedWindow.Store(b.Inventory()) 989 s.openedPos.Store(pos) 990 991 var containerType byte 992 switch b.(type) { 993 case block.Furnace: 994 containerType = protocol.ContainerTypeFurnace 995 case block.BlastFurnace: 996 containerType = protocol.ContainerTypeBlastFurnace 997 case block.Smoker: 998 containerType = protocol.ContainerTypeSmoker 999 } 1000 1001 s.writePacket(&packet.ContainerOpen{ 1002 WindowID: nextID, 1003 ContainerType: containerType, 1004 ContainerPosition: protocol.BlockPos{int32(pos[0]), int32(pos[1]), int32(pos[2])}, 1005 ContainerEntityUniqueID: -1, 1006 }) 1007 s.sendInv(b.Inventory(), uint32(nextID)) 1008 } 1009 1010 // ViewSlotChange ... 1011 func (s *Session) ViewSlotChange(slot int, newItem item.Stack) { 1012 if !s.containerOpened.Load() { 1013 return 1014 } 1015 if s.inTransaction.Load() { 1016 // Don't send slot changes to the player itself. 1017 return 1018 } 1019 s.writePacket(&packet.InventorySlot{ 1020 WindowID: s.openedWindowID.Load(), 1021 Slot: uint32(slot), 1022 NewItem: instanceFromItem(newItem), 1023 }) 1024 } 1025 1026 // ViewBlockAction ... 1027 func (s *Session) ViewBlockAction(pos cube.Pos, a world.BlockAction) { 1028 blockPos := protocol.BlockPos{int32(pos[0]), int32(pos[1]), int32(pos[2])} 1029 switch t := a.(type) { 1030 case block.OpenAction: 1031 s.writePacket(&packet.BlockEvent{ 1032 Position: blockPos, 1033 EventType: packet.BlockEventChangeChestState, 1034 EventData: 1, 1035 }) 1036 case block.CloseAction: 1037 s.writePacket(&packet.BlockEvent{ 1038 Position: blockPos, 1039 EventType: packet.BlockEventChangeChestState, 1040 }) 1041 case block.StartCrackAction: 1042 s.writePacket(&packet.LevelEvent{ 1043 EventType: packet.LevelEventStartBlockCracking, 1044 Position: vec64To32(pos.Vec3()), 1045 EventData: int32(65535 / (t.BreakTime.Seconds() * 20)), 1046 }) 1047 case block.StopCrackAction: 1048 s.writePacket(&packet.LevelEvent{ 1049 EventType: packet.LevelEventStopBlockCracking, 1050 Position: vec64To32(pos.Vec3()), 1051 EventData: 0, 1052 }) 1053 case block.ContinueCrackAction: 1054 s.writePacket(&packet.LevelEvent{ 1055 EventType: packet.LevelEventUpdateBlockCracking, 1056 Position: vec64To32(pos.Vec3()), 1057 EventData: int32(65535 / (t.BreakTime.Seconds() * 20)), 1058 }) 1059 } 1060 } 1061 1062 // ViewEmote ... 1063 func (s *Session) ViewEmote(player world.Entity, emote uuid.UUID) { 1064 s.writePacket(&packet.Emote{ 1065 EntityRuntimeID: s.entityRuntimeID(player), 1066 EmoteID: emote.String(), 1067 Flags: packet.EmoteFlagServerSide, 1068 }) 1069 } 1070 1071 // ViewSkin ... 1072 func (s *Session) ViewSkin(e world.Entity) { 1073 switch v := e.(type) { 1074 case Controllable: 1075 s.writePacket(&packet.PlayerSkin{ 1076 UUID: v.UUID(), 1077 Skin: skinToProtocol(v.Skin()), 1078 }) 1079 } 1080 } 1081 1082 // ViewWorldSpawn ... 1083 func (s *Session) ViewWorldSpawn(pos cube.Pos) { 1084 blockPos := protocol.BlockPos{int32(pos[0]), int32(pos[1]), int32(pos[2])} 1085 s.writePacket(&packet.SetSpawnPosition{ 1086 SpawnType: packet.SpawnTypeWorld, 1087 Position: blockPos, 1088 Dimension: packet.DimensionOverworld, 1089 SpawnPosition: blockPos, 1090 }) 1091 } 1092 1093 // ViewWeather ... 1094 func (s *Session) ViewWeather(raining, thunder bool) { 1095 pk := &packet.LevelEvent{ 1096 EventType: packet.LevelEventStopRaining, 1097 } 1098 if raining { 1099 pk.EventType, pk.EventData = packet.LevelEventStartRaining, int32(rand.Intn(50000)+10000) 1100 } 1101 s.writePacket(pk) 1102 1103 pk = &packet.LevelEvent{ 1104 EventType: packet.LevelEventStopThunderstorm, 1105 } 1106 if thunder { 1107 pk.EventType, pk.EventData = packet.LevelEventStartThunderstorm, int32(rand.Intn(50000)+10000) 1108 } 1109 s.writePacket(pk) 1110 } 1111 1112 // nextWindowID produces the next window ID for a new window. It is an int of 1-99. 1113 func (s *Session) nextWindowID() byte { 1114 if s.openedWindowID.CAS(99, 1) { 1115 return 1 1116 } 1117 return byte(s.openedWindowID.Add(1)) 1118 } 1119 1120 // closeWindow closes the container window currently opened. If no window is open, closeWindow will do 1121 // nothing. 1122 func (s *Session) closeWindow() { 1123 if !s.containerOpened.CAS(true, false) { 1124 return 1125 } 1126 s.openedContainerID.Store(0) 1127 s.openedWindow.Store(inventory.New(1, nil)) 1128 s.writePacket(&packet.ContainerClose{WindowID: byte(s.openedWindowID.Load())}) 1129 } 1130 1131 // entityRuntimeID returns the runtime ID of the entity passed. 1132 // noinspection GoCommentLeadingSpace 1133 func (s *Session) entityRuntimeID(e world.Entity) uint64 { 1134 s.entityMutex.RLock() 1135 //lint:ignore S1005 Double assignment is done explicitly to prevent panics. 1136 id, _ := s.entityRuntimeIDs[e] 1137 s.entityMutex.RUnlock() 1138 return id 1139 } 1140 1141 // entityFromRuntimeID attempts to return an entity by its runtime ID. False is returned if no entity with the 1142 // ID could be found. 1143 func (s *Session) entityFromRuntimeID(id uint64) (world.Entity, bool) { 1144 s.entityMutex.RLock() 1145 e, ok := s.entities[id] 1146 s.entityMutex.RUnlock() 1147 return e, ok 1148 } 1149 1150 // vec32To64 converts a mgl32.Vec3 to a mgl64.Vec3. 1151 func vec32To64(vec3 mgl32.Vec3) mgl64.Vec3 { 1152 return mgl64.Vec3{float64(vec3[0]), float64(vec3[1]), float64(vec3[2])} 1153 } 1154 1155 // vec64To32 converts a mgl64.Vec3 to a mgl32.Vec3. 1156 func vec64To32(vec3 mgl64.Vec3) mgl32.Vec3 { 1157 return mgl32.Vec3{float32(vec3[0]), float32(vec3[1]), float32(vec3[2])} 1158 } 1159 1160 // blockPosFromProtocol ... 1161 func blockPosFromProtocol(pos protocol.BlockPos) cube.Pos { 1162 return cube.Pos{int(pos.X()), int(pos.Y()), int(pos.Z())} 1163 } 1164 1165 // boolByte returns 1 if the bool passed is true, or 0 if it is false. 1166 func boolByte(b bool) uint8 { 1167 if b { 1168 return 1 1169 } 1170 return 0 1171 } 1172 1173 // abs ... 1174 func abs(a int) int { 1175 if a < 0 { 1176 return -a 1177 } 1178 return a 1179 }