diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 8f3f97ef2..ce6744d24 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -297,6 +297,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ /** @var int|null */ protected $lineHeight = null; + /** @var string */ + protected $locale = "en_US"; + + /** + * @var int + * Last measurement of player's latency in milliseconds. + */ + protected $lastPingMeasure = 1; + /** * @return TranslationContainer|string */ @@ -738,6 +747,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } } + /** + * Returns the player's locale, e.g. en_US. + * @return string + */ + public function getLocale() : string{ + return $this->locale; + } + /** * Called when a player changes their skin. * Plugin developers should not use this, use setSkin() and sendSkin() instead. @@ -791,6 +808,27 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return $this->port; } + /** + * Returns the last measured latency for this player, in milliseconds. This is measured automatically and reported + * back by the network interface. + * + * @return int + */ + public function getPing() : int{ + return $this->lastPingMeasure; + } + + /** + * Updates the player's last ping measurement. + * + * @internal Plugins should not use this method. + * + * @param int $pingMS + */ + public function updatePing(int $pingMS){ + $this->lastPingMeasure = $pingMS; + } + /** * @return Position */ @@ -1434,7 +1472,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->server->broadcastPacket($entity->getViewers(), $pk); $this->inventory->addItem(clone $item); - $entity->kill(); + $entity->flagForDespawn(); }elseif($entity instanceof DroppedItem){ if($entity->getPickupDelay() <= 0){ $item = $entity->getItem(); @@ -1464,7 +1502,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->server->broadcastPacket($entity->getViewers(), $pk); $this->inventory->addItem(clone $item); - $entity->kill(); + $entity->flagForDespawn(); } } } @@ -1992,6 +2030,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->displayName = $this->username; $this->iusername = strtolower($this->username); + if($packet->locale !== null){ + $this->locale = $packet->locale; + } + if(count($this->server->getOnlinePlayers()) >= $this->server->getMaxPlayers() and $this->kick("disconnectionScreen.serverFull", false)){ return true; } @@ -2497,12 +2539,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return true; } - $pk = new EntityEventPacket(); - $pk->entityRuntimeId = $this->getId(); - $pk->event = EntityEventPacket::USE_ITEM; - $this->dataPacket($pk); - $this->server->broadcastPacket($this->getViewers(), $pk); - if($this->isSurvival()){ $slot = $this->inventory->getItemInHand(); --$slot->count; @@ -2673,7 +2709,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $this->getSpawn())); - $realSpawn = $ev->getRespawnPosition()->add(0.5, 0, 0.5); + $realSpawn = Position::fromObject($ev->getRespawnPosition()->add(0.5, 0, 0.5), $ev->getRespawnPosition()->getLevel()); if($realSpawn->distanceSquared($this->getSpawn()->add(0.5, 0, 0.5)) > 0.01){ $this->teleport($realSpawn); //If the destination was modified by plugins @@ -3626,10 +3662,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ if($source->isCancelled()){ return; }elseif($this->getLastDamageCause() === $source and $this->spawned){ - $pk = new EntityEventPacket(); - $pk->entityRuntimeId = $this->id; - $pk->event = EntityEventPacket::HURT_ANIMATION; - $this->dataPacket($pk); + $this->broadcastEntityEvent(EntityEventPacket::HURT_ANIMATION); if($this->isSurvival()){ $this->exhaust(0.3, PlayerExhaustEvent::CAUSE_DAMAGE); diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index 8c10cf69f..5c28a70b1 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -167,8 +167,8 @@ namespace pocketmine { exit(1); } - if(version_compare(RakLib::VERSION, "0.8.1") < 0){ - echo "[CRITICAL] RakLib version 0.8.1 is required, while you have version " . RakLib::VERSION . "." . PHP_EOL; + if(version_compare(RakLib::VERSION, "0.8.2") < 0){ + echo "[CRITICAL] RakLib version 0.8.2 is required, while you have version " . RakLib::VERSION . "." . PHP_EOL; echo "[CRITICAL] Please update your submodules or use provided builds." . PHP_EOL; exit(1); } diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index e586c49a9..3424c63bb 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1407,6 +1407,9 @@ class Server{ * @return Server */ public static function getInstance() : Server{ + if(self::$instance === null){ + throw new \RuntimeException("Attempt to retrieve Server instance outside server thread"); + } return self::$instance; } diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index bbaecbfa6..f9631603f 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -59,6 +59,7 @@ use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ShortTag; use pocketmine\nbt\tag\StringTag; use pocketmine\network\mcpe\protocol\AddEntityPacket; +use pocketmine\network\mcpe\protocol\EntityEventPacket; use pocketmine\network\mcpe\protocol\MoveEntityPacket; use pocketmine\network\mcpe\protocol\RemoveEntityPacket; use pocketmine\network\mcpe\protocol\SetEntityDataPacket; @@ -470,6 +471,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{ /** @var bool */ protected $closed = false; + /** @var bool */ + private $needsDespawn = false; /** @var TimingsHandler */ protected $timings; @@ -1279,15 +1282,20 @@ abstract class Entity extends Location implements Metadatable, EntityIds{ $this->lastUpdate = $currentTick; + if($this->needsDespawn){ + $this->close(); + return false; + } + if(!$this->isAlive()){ $this->deadTicks += $tickDiff; if($this->deadTicks >= $this->maxDeadTicks){ $this->despawnFromAll(); if(!$this->isPlayer){ - $this->close(); + $this->flagForDespawn(); } } - return $this->deadTicks < $this->maxDeadTicks; + return true; } @@ -1929,6 +1937,13 @@ abstract class Entity extends Location implements Metadatable, EntityIds{ } } + /** + * Flags the entity to be removed from the world on the next tick. + */ + public function flagForDespawn() : void{ + $this->needsDespawn = true; + } + /** * Returns whether the entity has been "closed". * @return bool @@ -2077,6 +2092,15 @@ abstract class Entity extends Location implements Metadatable, EntityIds{ } } + public function broadcastEntityEvent(int $eventId, ?int $eventData = null, ?array $players = null) : void{ + $pk = new EntityEventPacket(); + $pk->entityRuntimeId = $this->id; + $pk->event = $eventId; + $pk->data = $eventData ?? 0; + + $this->server->broadcastPacket($players ?? $this->getViewers(), $pk); + } + public function __destruct(){ $this->close(); } diff --git a/src/pocketmine/entity/FallingSand.php b/src/pocketmine/entity/FallingSand.php index 99838f81d..2f72ee76c 100644 --- a/src/pocketmine/entity/FallingSand.php +++ b/src/pocketmine/entity/FallingSand.php @@ -104,7 +104,7 @@ class FallingSand extends Entity{ } if($this->onGround or $blockTarget !== null){ - $this->kill(); + $this->flagForDespawn(); $block = $this->level->getBlock($pos); if($block->getId() > 0 and $block->isTransparent() and !$block->canBeReplaced()){ diff --git a/src/pocketmine/entity/Item.php b/src/pocketmine/entity/Item.php index 9f772ba63..181fb922d 100644 --- a/src/pocketmine/entity/Item.php +++ b/src/pocketmine/entity/Item.php @@ -118,7 +118,7 @@ class Item extends Entity{ if($ev->isCancelled()){ $this->age = 0; }else{ - $this->kill(); + $this->flagForDespawn(); $hasUpdate = true; } } diff --git a/src/pocketmine/entity/Living.php b/src/pocketmine/entity/Living.php index 1bc23a9db..520e5f7ee 100644 --- a/src/pocketmine/entity/Living.php +++ b/src/pocketmine/entity/Living.php @@ -108,10 +108,7 @@ abstract class Living extends Entity implements Damageable{ parent::setHealth($amount); $this->attributeMap->getAttribute(Attribute::HEALTH)->setValue(ceil($this->getHealth()), true); if($this->isAlive() and !$wasAlive){ - $pk = new EntityEventPacket(); - $pk->entityRuntimeId = $this->getId(); - $pk->event = EntityEventPacket::RESPAWN; - $this->server->broadcastPacket($this->hasSpawned, $pk); + $this->broadcastEntityEvent(EntityEventPacket::RESPAWN); } } @@ -376,10 +373,7 @@ abstract class Living extends Entity implements Damageable{ $this->setAbsorption(max(0, $this->getAbsorption() + $source->getDamage(EntityDamageEvent::MODIFIER_ABSORPTION))); - $pk = new EntityEventPacket(); - $pk->entityRuntimeId = $this->getId(); - $pk->event = $this->getHealth() <= 0 ? EntityEventPacket::DEATH_ANIMATION : EntityEventPacket::HURT_ANIMATION; //Ouch! - $this->server->broadcastPacket($this->hasSpawned, $pk); + $this->broadcastEntityEvent($this->getHealth() <= 0 ? EntityEventPacket::DEATH_ANIMATION : EntityEventPacket::HURT_ANIMATION); //Ouch! $this->attackTime = 10; //0.5 seconds cooldown } diff --git a/src/pocketmine/entity/PrimedTNT.php b/src/pocketmine/entity/PrimedTNT.php index 43ffc141d..71d1023ad 100644 --- a/src/pocketmine/entity/PrimedTNT.php +++ b/src/pocketmine/entity/PrimedTNT.php @@ -91,7 +91,7 @@ class PrimedTNT extends Entity implements Explosive{ $this->fuse -= $tickDiff; if($this->fuse <= 0){ - $this->kill(); + $this->flagForDespawn(); $this->explode(); } } diff --git a/src/pocketmine/entity/Squid.php b/src/pocketmine/entity/Squid.php index eb6257523..64f44245b 100644 --- a/src/pocketmine/entity/Squid.php +++ b/src/pocketmine/entity/Squid.php @@ -64,10 +64,7 @@ class Squid extends WaterAnimal{ $this->swimDirection = (new Vector3($this->x - $e->x, $this->y - $e->y, $this->z - $e->z))->normalize(); } - $pk = new EntityEventPacket(); - $pk->entityRuntimeId = $this->getId(); - $pk->event = EntityEventPacket::SQUID_INK_CLOUD; - $this->server->broadcastPacket($this->hasSpawned, $pk); + $this->broadcastEntityEvent(EntityEventPacket::SQUID_INK_CLOUD); } } diff --git a/src/pocketmine/entity/projectile/Arrow.php b/src/pocketmine/entity/projectile/Arrow.php index 80c262943..a9f806f82 100644 --- a/src/pocketmine/entity/projectile/Arrow.php +++ b/src/pocketmine/entity/projectile/Arrow.php @@ -72,7 +72,7 @@ class Arrow extends Projectile{ } if($this->age > 1200){ - $this->close(); + $this->flagForDespawn(); $hasUpdate = true; } diff --git a/src/pocketmine/entity/projectile/Projectile.php b/src/pocketmine/entity/projectile/Projectile.php index 0d9946777..70e1bf9a2 100644 --- a/src/pocketmine/entity/projectile/Projectile.php +++ b/src/pocketmine/entity/projectile/Projectile.php @@ -102,7 +102,7 @@ abstract class Projectile extends Entity{ } } - $this->close(); + $this->flagForDespawn(); } public function saveNBT(){ diff --git a/src/pocketmine/entity/projectile/Throwable.php b/src/pocketmine/entity/projectile/Throwable.php index 10738623c..039012077 100644 --- a/src/pocketmine/entity/projectile/Throwable.php +++ b/src/pocketmine/entity/projectile/Throwable.php @@ -40,7 +40,7 @@ abstract class Throwable extends Projectile{ if($this->age > 1200 or $this->isCollided){ //TODO: hit particles - $this->kill(); + $this->flagForDespawn(); $hasUpdate = true; } diff --git a/src/pocketmine/item/Bow.php b/src/pocketmine/item/Bow.php index e18d17846..aeb0e1b00 100644 --- a/src/pocketmine/item/Bow.php +++ b/src/pocketmine/item/Bow.php @@ -75,7 +75,7 @@ class Bow extends Tool{ $entity = $ev->getProjectile(); //This might have been changed by plugins if($ev->isCancelled()){ - $entity->kill(); + $entity->flagForDespawn(); $player->getInventory()->sendContents($player); }else{ $entity->setMotion($entity->getMotion()->multiply($ev->getForce())); @@ -87,7 +87,7 @@ class Bow extends Tool{ if($entity instanceof Projectile){ $player->getServer()->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($entity)); if($projectileEv->isCancelled()){ - $ev->getProjectile()->kill(); + $ev->getProjectile()->flagForDespawn(); }else{ $ev->getProjectile()->spawnToAll(); $player->level->addSound(new LaunchSound($player), $player->getViewers()); diff --git a/src/pocketmine/item/ProjectileItem.php b/src/pocketmine/item/ProjectileItem.php index 70c31140d..bc2385781 100644 --- a/src/pocketmine/item/ProjectileItem.php +++ b/src/pocketmine/item/ProjectileItem.php @@ -49,7 +49,7 @@ abstract class ProjectileItem extends Item{ if($projectile instanceof Projectile){ $player->getServer()->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($projectile)); if($projectileEv->isCancelled()){ - $projectile->kill(); + $projectile->flagForDespawn(); }else{ $projectile->spawnToAll(); $player->getLevel()->addSound(new LaunchSound($player), $player->getViewers()); diff --git a/src/pocketmine/network/mcpe/RakLibInterface.php b/src/pocketmine/network/mcpe/RakLibInterface.php index 2d595b8aa..7d888b448 100644 --- a/src/pocketmine/network/mcpe/RakLibInterface.php +++ b/src/pocketmine/network/mcpe/RakLibInterface.php @@ -237,6 +237,12 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{ return null; } + public function updatePing(string $identifier, int $pingMS){ + if(isset($this->players[$identifier])){ + $this->players[$identifier]->updatePing($pingMS); + } + } + private function getPacket($buffer){ $pid = ord($buffer{0}); if(($data = PacketPool::getPacketById($pid)) === null){ diff --git a/src/pocketmine/network/mcpe/protocol/EntityEventPacket.php b/src/pocketmine/network/mcpe/protocol/EntityEventPacket.php index 576087ee0..9f126002e 100644 --- a/src/pocketmine/network/mcpe/protocol/EntityEventPacket.php +++ b/src/pocketmine/network/mcpe/protocol/EntityEventPacket.php @@ -33,6 +33,7 @@ class EntityEventPacket extends DataPacket{ const HURT_ANIMATION = 2; const DEATH_ANIMATION = 3; + const ARM_SWING = 4; const TAME_FAIL = 6; const TAME_SUCCESS = 7; @@ -44,12 +45,41 @@ class EntityEventPacket extends DataPacket{ const FISH_HOOK_HOOK = 13; const FISH_HOOK_TEASE = 14; const SQUID_INK_CLOUD = 15; - const AMBIENT_SOUND = 16; + const ZOMBIE_VILLAGER_CURE = 16; const RESPAWN = 18; + const IRON_GOLEM_OFFER_FLOWER = 19; + const IRON_GOLEM_WITHDRAW_FLOWER = 20; + const LOVE_PARTICLES = 21; //breeding + + const WITCH_SPELL_PARTICLES = 24; + const FIREWORK_PARTICLES = 25; + + const SILVERFISH_SPAWN_ANIMATION = 27; + + const WITCH_DRINK_POTION = 29; + const WITCH_THROW_POTION = 30; + const MINECART_TNT_PRIME_FUSE = 31; + + const PLAYER_ADD_XP_LEVELS = 34; + const ELDER_GUARDIAN_CURSE = 35; + const AGENT_ARM_SWING = 36; + const ENDER_DRAGON_DEATH = 37; + const DUST_PARTICLES = 38; //not sure what this is const EATING_ITEM = 57; + const BABY_ANIMAL_FEED = 60; //green particles, like bonemeal on crops + const DEATH_SMOKE_CLOUD = 61; + const COMPLETE_TRADE = 62; + const REMOVE_LEASH = 63; //data 1 = cut leash + + const CONSUME_TOTEM = 65; + const PLAYER_CHECK_TREASURE_HUNTER_ACHIEVEMENT = 66; //mojang... + const ENTITY_SPAWN = 67; //used for MinecraftEventing stuff, not needed + const DRAGON_PUKE = 68; //they call this puke particles + const ITEM_ENTITY_MERGE = 69; + //TODO: add more events /** @var int */ diff --git a/src/pocketmine/network/mcpe/protocol/LoginPacket.php b/src/pocketmine/network/mcpe/protocol/LoginPacket.php index 6abc1c093..a31ae5736 100644 --- a/src/pocketmine/network/mcpe/protocol/LoginPacket.php +++ b/src/pocketmine/network/mcpe/protocol/LoginPacket.php @@ -50,6 +50,8 @@ class LoginPacket extends DataPacket{ public $identityPublicKey; /** @var string */ public $serverAddress; + /** @var string */ + public $locale; /** @var array (the "chain" index contains one or more JWTs) */ public $chainData = []; @@ -119,6 +121,8 @@ class LoginPacket extends DataPacket{ $this->clientId = $this->clientData["ClientRandomId"] ?? null; $this->serverAddress = $this->clientData["ServerAddress"] ?? null; + + $this->locale = $this->clientData["LanguageCode"] ?? null; } protected function encodePayload(){ diff --git a/src/pocketmine/resources/pocketmine.yml b/src/pocketmine/resources/pocketmine.yml index d36b29d6f..e5ba291ba 100644 --- a/src/pocketmine/resources/pocketmine.yml +++ b/src/pocketmine/resources/pocketmine.yml @@ -160,7 +160,7 @@ auto-report: anonymous-statistics: #Sends anonymous statistics for data aggregation, plugin usage tracking - enabled: true + enabled: false #TODO: re-enable this when we have a new stats host host: stats.pocketmine.net auto-updater: diff --git a/src/pocketmine/utils/UUID.php b/src/pocketmine/utils/UUID.php index d95d60bcb..23c1f38eb 100644 --- a/src/pocketmine/utils/UUID.php +++ b/src/pocketmine/utils/UUID.php @@ -39,7 +39,7 @@ class UUID{ } public function equals(UUID $uuid) : bool{ - return $uuid->parts[0] === $this->parts[0] and $uuid->parts[1] === $this->parts[1] and $uuid->parts[2] === $this->parts[2] and $uuid->parts[3] === $this->parts[3]; + return $uuid->parts === $this->parts; } /** diff --git a/src/raklib b/src/raklib index a10881b00..268a93c57 160000 --- a/src/raklib +++ b/src/raklib @@ -1 +1 @@ -Subproject commit a10881b0078559f646b570a12304dba71658357c +Subproject commit 268a93c575c3ffa699e8a66b854834989245bb05 diff --git a/src/spl b/src/spl index c4d484158..cf7738721 160000 --- a/src/spl +++ b/src/spl @@ -1 +1 @@ -Subproject commit c4d4841586a19d95f25cc02b23baa00dbbc0b0d7 +Subproject commit cf7738721e7342c018a91ad300108b2dd95c7224