From 41970feb573bb27a0372b9b13451ac6e97ad479b Mon Sep 17 00:00:00 2001 From: Dylan T Date: Thu, 29 Sep 2022 22:30:12 +0100 Subject: [PATCH] Entity: Fire EntitySpawnEvent/ItemSpawnEvent on the first entity tick, instead of in the constructor (#5314) This allows plugins to modify the entity via setters in EntitySpawnEvent without their changes getting overwritten by setter calls directly after the 'new YourEntity' statement. As well as benefiting plugins, this also clears a path for a BC-breaking change in PM5 (to have the programmer use addEntity() to spawn entities, instead of the constructor doing it, which will improve on a number of data handling aspects). fixes #4973 This targets next-minor because it has some side effects on plugins that depended on the old behaviour, such as VanillaHopper, so it's not suitable for a patch release. --- src/entity/Entity.php | 14 ++++++++++++-- src/entity/object/ItemEntity.php | 5 ++++- src/player/Player.php | 4 ++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/entity/Entity.php b/src/entity/Entity.php index 54873f9f1..3e34d5c26 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -249,10 +249,8 @@ abstract class Entity{ $this->getWorld()->addEntity($this); $this->lastUpdate = $this->server->getTick(); - (new EntitySpawnEvent($this))->call(); $this->scheduleUpdate(); - } abstract protected function getInitialSizeInfo() : EntitySizeInfo; @@ -937,6 +935,14 @@ abstract class Entity{ return (new Vector2(-cos(deg2rad($this->location->yaw) - M_PI_2), -sin(deg2rad($this->location->yaw) - M_PI_2)))->normalize(); } + /** + * Called from onUpdate() on the first tick of a new entity. This is called before any movement processing or + * main ticking logic. Use this to fire any events related to spawning the entity. + */ + protected function onFirstUpdate(int $currentTick) : void{ + (new EntitySpawnEvent($this))->call(); + } + public function onUpdate(int $currentTick) : bool{ if($this->closed){ return false; @@ -953,6 +959,10 @@ abstract class Entity{ $this->lastUpdate = $currentTick; + if($this->justCreated){ + $this->onFirstUpdate($currentTick); + } + if(!$this->isAlive()){ if($this->onDeathUpdate($tickDiff)){ $this->flagForDespawn(); diff --git a/src/entity/object/ItemEntity.php b/src/entity/object/ItemEntity.php index 5c39f3bec..4fe844f7e 100644 --- a/src/entity/object/ItemEntity.php +++ b/src/entity/object/ItemEntity.php @@ -92,8 +92,11 @@ class ItemEntity extends Entity{ $this->pickupDelay = $nbt->getShort("PickupDelay", $this->pickupDelay); $this->owner = $nbt->getString("Owner", $this->owner); $this->thrower = $nbt->getString("Thrower", $this->thrower); + } - (new ItemSpawnEvent($this))->call(); + protected function onFirstUpdate(int $currentTick) : void{ + (new ItemSpawnEvent($this))->call(); //this must be called before EntitySpawnEvent, to maintain backwards compatibility + parent::onFirstUpdate($currentTick); } protected function entityBaseTick(int $tickDiff = 1) : bool{ diff --git a/src/player/Player.php b/src/player/Player.php index 340452b31..157038beb 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -1343,6 +1343,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->lastUpdate = $currentTick; + if($this->justCreated){ + $this->onFirstUpdate($currentTick); + } + if(!$this->isAlive() && $this->spawned){ $this->onDeathUpdate($tickDiff); return true;