diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index b8dfbf512..dfac68181 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -24,7 +24,7 @@ namespace pocketmine; use pocketmine\block\Block; use pocketmine\command\CommandSender; use pocketmine\entity\Arrow; -use pocketmine\entity\DroppedItem; +use pocketmine\entity\Item as DroppedItem; use pocketmine\entity\Entity; use pocketmine\entity\Human; use pocketmine\entity\Living; @@ -1650,7 +1650,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ ]); $f = 1.5; - $snowball = new Snowball($this->chunk, $nbt, $this); + $snowball = Entity::createEntity("Snowball", $this->chunk, $nbt, $this); $snowball->setMotion($snowball->getMotion()->multiply($f)); if($this->isSurvival()){ $this->inventory->removeItem(Item::get(Item::SNOWBALL, 0, 1)); @@ -1709,7 +1709,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ ]); $f = 1.5; - $ev = EntityShootBowEvent::createEvent($this, $bow, new Arrow($this->chunk, $nbt, $this), $f); + $ev = EntityShootBowEvent::createEvent($this, $bow, Entity::createEntity("Arrow", $this->chunk, $nbt, $this), $f); $this->server->getPluginManager()->callEvent($ev); diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 8ae5dde89..049f4acec 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -31,12 +31,21 @@ use pocketmine\command\CommandSender; use pocketmine\command\ConsoleCommandSender; use pocketmine\command\PluginIdentifiableCommand; use pocketmine\command\SimpleCommandMap; +use pocketmine\entity\Arrow; +use pocketmine\entity\Entity; +use pocketmine\entity\FallingSand; +use pocketmine\entity\Human; +use pocketmine\entity\PrimedTNT; +use pocketmine\entity\Snowball; +use pocketmine\entity\Villager; +use pocketmine\entity\Zombie; use pocketmine\event\Event; use pocketmine\event\HandlerList; use pocketmine\event\level\LevelInitEvent; use pocketmine\event\level\LevelLoadEvent; use pocketmine\event\server\ServerCommandEvent; use pocketmine\event\Timings; +use pocketmine\entity\Item as DroppedItem; use pocketmine\event\TimingsHandler; use pocketmine\inventory\CraftingManager; use pocketmine\inventory\InventoryType; @@ -82,6 +91,10 @@ use pocketmine\plugin\PluginManager; use pocketmine\scheduler\CallbackTask; use pocketmine\scheduler\SendUsageTask; use pocketmine\scheduler\ServerScheduler; +use pocketmine\tile\Chest; +use pocketmine\tile\Furnace; +use pocketmine\tile\Sign; +use pocketmine\tile\Tile; use pocketmine\updater\AutoUpdater; use pocketmine\utils\Binary; use pocketmine\utils\Cache; @@ -1553,6 +1566,9 @@ class Server{ $this->consoleSender = new ConsoleCommandSender(); $this->commandMap = new SimpleCommandMap($this); + $this->registerEntities(); + $this->registerTiles(); + InventoryType::init(); Block::init(); Item::init(); @@ -2142,4 +2158,22 @@ class Server{ return true; } + private function registerEntities(){ + Entity::registerEntity(Arrow::class); + Entity::registerEntity(DroppedItem::class); + Entity::registerEntity(FallingSand::class); + Entity::registerEntity(PrimedTNT::class); + Entity::registerEntity(Snowball::class); + Entity::registerEntity(Villager::class); + Entity::registerEntity(Zombie::class); + + Entity::registerEntity(Human::class, true); + } + + private function registerTiles(){ + Tile::registerTile(Chest::class); + Tile::registerTile(Furnace::class); + Tile::registerTile(Sign::class); + } + } diff --git a/src/pocketmine/block/BurningFurnace.php b/src/pocketmine/block/BurningFurnace.php index 12a58907b..5c9b46d86 100644 --- a/src/pocketmine/block/BurningFurnace.php +++ b/src/pocketmine/block/BurningFurnace.php @@ -55,7 +55,7 @@ class BurningFurnace extends Solid{ new Int("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); - new Furnace($this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); + Tile::createTile("Furnace", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; } @@ -81,7 +81,7 @@ class BurningFurnace extends Solid{ new Int("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); - $furnace = new Furnace($this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); + $furnace = Tile::createTile("Furnace", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); } if(($player->getGamemode() & 0x01) === 0x01){ diff --git a/src/pocketmine/block/Chest.php b/src/pocketmine/block/Chest.php index 9242b4a99..2b670f315 100644 --- a/src/pocketmine/block/Chest.php +++ b/src/pocketmine/block/Chest.php @@ -90,9 +90,9 @@ class Chest extends Transparent{ new Int("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); - $tile = new TileChest($this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); + $tile = Tile::createTile("Chest", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); - if($chest instanceof TileChest){ + if($chest instanceof TileChest and $tile instanceof TileChest){ $chest->pairWith($tile); $tile->pairWith($chest); } diff --git a/src/pocketmine/block/Fallable.php b/src/pocketmine/block/Fallable.php index 8faf46acf..448878fc9 100644 --- a/src/pocketmine/block/Fallable.php +++ b/src/pocketmine/block/Fallable.php @@ -21,7 +21,8 @@ namespace pocketmine\block; -use pocketmine\entity\FallingBlock; +use pocketmine\entity\Entity; +use pocketmine\entity\FallingSand; use pocketmine\item\Item; use pocketmine\level\Level; use pocketmine\nbt\tag\Byte; @@ -46,7 +47,7 @@ abstract class Fallable extends Solid{ if($this->hasPhysics === true and $type === Level::BLOCK_UPDATE_NORMAL){ $down = $this->getSide(0); if($down->getID() === self::AIR or ($down instanceof Liquid)){ - $fall = new FallingBlock($this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), new Compound("", [ + $fall = Entity::createEntity("FallingSand", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), new Compound("", [ "Pos" => new Enum("Pos", [ new Double("", $this->x + 0.5), new Double("", $this->y), @@ -67,8 +68,6 @@ abstract class Fallable extends Solid{ $fall->spawnToAll(); } - - return false; } } } \ No newline at end of file diff --git a/src/pocketmine/block/TNT.php b/src/pocketmine/block/TNT.php index 5dc758ac9..31a68c3ff 100644 --- a/src/pocketmine/block/TNT.php +++ b/src/pocketmine/block/TNT.php @@ -21,6 +21,7 @@ namespace pocketmine\block; +use pocketmine\entity\Entity; use pocketmine\entity\PrimedTNT; use pocketmine\item\Item; use pocketmine\nbt\tag\Byte; @@ -44,7 +45,7 @@ class TNT extends Solid{ $this->getLevel()->setBlock($this, new Air(), true); $mot = (new Random())->nextSignedFloat() * M_PI * 2; - $tnt = new PrimedTNT($this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), new Compound("", [ + $tnt = Entity::createEntity("PrimedTNT", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), new Compound("", [ "Pos" => new Enum("Pos", [ new Double("", $this->x + 0.5), new Double("", $this->y), diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index 3d63b1299..c874a8b3d 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -62,9 +62,12 @@ use pocketmine\Server; abstract class Entity extends Location implements Metadatable{ + const NETWORK_ID = -1; public static $entityCount = 1; + /** @var Entity[] */ + private static $knownEntities = []; /** * @var Player[] @@ -219,6 +222,39 @@ abstract class Entity extends Location implements Metadatable{ } + /** + * @param int|string $type + * @param FullChunk $chunk + * @param Compound $nbt + * @param $args + * + * @return Entity + */ + public static function createEntity($type, FullChunk $chunk, Compound $nbt, ...$args){ + if(isset(self::$knownEntities[$type])){ + $class = self::$knownEntities[$type]; + return new $class($chunk, $nbt, ...$args); + } + + return null; + } + + public static function registerEntity($className, $force = false){ + $class = new \ReflectionClass($className); + if(is_a($className, Entity::class, true) and !$class->isAbstract()){ + if($className::NETWORK_ID !== -1){ + self::$knownEntities[$className::NETWORK_ID] = $className; + }elseif(!$force){ + return false; + } + + self::$knownEntities[$class->getShortName()] = $className; + return true; + } + + return false; + } + public function saveNBT(){ $this->namedtag->Pos = new Enum("Pos", [ new Double(0, $this->x), diff --git a/src/pocketmine/entity/FallingBlock.php b/src/pocketmine/entity/FallingSand.php similarity index 97% rename from src/pocketmine/entity/FallingBlock.php rename to src/pocketmine/entity/FallingSand.php index d9bb4d1ba..a4a0115bb 100644 --- a/src/pocketmine/entity/FallingBlock.php +++ b/src/pocketmine/entity/FallingSand.php @@ -35,8 +35,7 @@ use pocketmine\network\protocol\AddEntityPacket; use pocketmine\network\protocol\SetEntityMotionPacket; use pocketmine\Player; -class FallingBlock extends Entity{ - +class FallingSand extends Entity{ const NETWORK_ID = 66; public $width = 0.98; @@ -54,8 +53,7 @@ class FallingBlock extends Entity{ $this->namedtag->id = new String("id", "FallingSand"); if(isset($this->namedtag->TileID)){ $this->blockId = $this->namedtag["TileID"]; - } - elseif(isset($this->namedtag->Tile)){ + }elseif(isset($this->namedtag->Tile)){ $this->blockId = $this->namedtag["Tile"]; $this->namedtag["TileID"] = new Int("TileID", $this->blockId); } @@ -151,7 +149,7 @@ class FallingBlock extends Entity{ public function spawnTo(Player $player){ $pk = AddEntityPacket::getFromPool(); - $pk->type = FallingBlock::NETWORK_ID; + $pk->type = FallingSand::NETWORK_ID; $pk->eid = $this->getID(); $pk->x = $this->x; $pk->y = $this->y; diff --git a/src/pocketmine/entity/DroppedItem.php b/src/pocketmine/entity/Item.php similarity index 95% rename from src/pocketmine/entity/DroppedItem.php rename to src/pocketmine/entity/Item.php index 1ca2d8a38..9707ddd56 100644 --- a/src/pocketmine/entity/DroppedItem.php +++ b/src/pocketmine/entity/Item.php @@ -25,7 +25,7 @@ use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityRegainHealthEvent; use pocketmine\event\entity\ItemDespawnEvent; use pocketmine\event\entity\ItemSpawnEvent; -use pocketmine\item\Item; +use pocketmine\item\Item as ItemItem; use pocketmine\math\Vector3; use pocketmine\nbt\tag\Byte; use pocketmine\nbt\tag\Compound; @@ -35,12 +35,13 @@ use pocketmine\network\protocol\AddItemEntityPacket; use pocketmine\network\protocol\SetEntityMotionPacket; use pocketmine\Player; -class DroppedItem extends Entity{ +class Item extends Entity{ + const NETWORK_ID = 64; protected $owner = null; protected $thrower = null; protected $pickupDelay = 0; - /** @var Item */ + /** @var ItemItem */ protected $item; public $width = 0.25; @@ -67,7 +68,7 @@ class DroppedItem extends Entity{ if(isset($this->namedtag->Thrower)){ $this->thrower = $this->namedtag["Thrower"]; } - $this->item = Item::get($this->namedtag->Item["id"], $this->namedtag->Item["Damage"], $this->namedtag->Item["Count"]); + $this->item = ItemItem::get($this->namedtag->Item["id"], $this->namedtag->Item["Damage"], $this->namedtag->Item["Count"]); $this->server->getPluginManager()->callEvent(ItemSpawnEvent::createEvent($this)); @@ -166,7 +167,7 @@ class DroppedItem extends Entity{ } /** - * @return Item + * @return ItemItem */ public function getItem(){ return $this->item; diff --git a/src/pocketmine/event/entity/EntityDespawnEvent.php b/src/pocketmine/event/entity/EntityDespawnEvent.php index a5ff67242..d02c13c4b 100644 --- a/src/pocketmine/event/entity/EntityDespawnEvent.php +++ b/src/pocketmine/event/entity/EntityDespawnEvent.php @@ -22,7 +22,7 @@ namespace pocketmine\event\entity; use pocketmine\entity\Creature; -use pocketmine\entity\DroppedItem; +use pocketmine\entity\Item; use pocketmine\entity\Entity; use pocketmine\entity\Human; use pocketmine\entity\Projectile; @@ -85,7 +85,7 @@ class EntityDespawnEvent extends EntityEvent{ * @return bool */ public function isItem(){ - return $this->entity instanceof DroppedItem; + return $this->entity instanceof Item; } } \ No newline at end of file diff --git a/src/pocketmine/event/entity/EntitySpawnEvent.php b/src/pocketmine/event/entity/EntitySpawnEvent.php index d865c537e..8fde50ec6 100644 --- a/src/pocketmine/event/entity/EntitySpawnEvent.php +++ b/src/pocketmine/event/entity/EntitySpawnEvent.php @@ -22,7 +22,7 @@ namespace pocketmine\event\entity; use pocketmine\entity\Creature; -use pocketmine\entity\DroppedItem; +use pocketmine\entity\Item; use pocketmine\entity\Entity; use pocketmine\entity\Human; use pocketmine\entity\Projectile; @@ -92,7 +92,7 @@ class EntitySpawnEvent extends EntityEvent{ * @return bool */ public function isItem(){ - return $this->entity instanceof DroppedItem; + return $this->entity instanceof Item; } } \ No newline at end of file diff --git a/src/pocketmine/event/entity/ItemDespawnEvent.php b/src/pocketmine/event/entity/ItemDespawnEvent.php index f37b3b51e..f1998688e 100644 --- a/src/pocketmine/event/entity/ItemDespawnEvent.php +++ b/src/pocketmine/event/entity/ItemDespawnEvent.php @@ -21,7 +21,7 @@ namespace pocketmine\event\entity; -use pocketmine\entity\DroppedItem; +use pocketmine\entity\Item; use pocketmine\event\Cancellable; class ItemDespawnEvent extends EntityEvent implements Cancellable{ @@ -30,15 +30,15 @@ class ItemDespawnEvent extends EntityEvent implements Cancellable{ public static $nextEvent = 0; /** - * @param DroppedItem $item + * @param Item $item */ - public function __construct(DroppedItem $item){ + public function __construct(Item $item){ $this->entity = $item; } /** - * @return DroppedItem + * @return Item */ public function getEntity(){ return $this->entity; diff --git a/src/pocketmine/event/entity/ItemSpawnEvent.php b/src/pocketmine/event/entity/ItemSpawnEvent.php index 5e369dde9..02aa93f91 100644 --- a/src/pocketmine/event/entity/ItemSpawnEvent.php +++ b/src/pocketmine/event/entity/ItemSpawnEvent.php @@ -21,7 +21,7 @@ namespace pocketmine\event\entity; -use pocketmine\entity\DroppedItem; +use pocketmine\entity\Item; class ItemSpawnEvent extends EntityEvent{ public static $handlerList = null; @@ -29,15 +29,15 @@ class ItemSpawnEvent extends EntityEvent{ public static $nextEvent = 0; /** - * @param DroppedItem $item + * @param Item $item */ - public function __construct(DroppedItem $item){ + public function __construct(Item $item){ $this->entity = $item; } /** - * @return DroppedItem + * @return Item */ public function getEntity(){ return $this->entity; diff --git a/src/pocketmine/item/SpawnEgg.php b/src/pocketmine/item/SpawnEgg.php index 65e74157e..be8f19795 100644 --- a/src/pocketmine/item/SpawnEgg.php +++ b/src/pocketmine/item/SpawnEgg.php @@ -66,37 +66,10 @@ class SpawnEgg extends Item{ ]), ]); - switch($this->meta){ - case Villager::NETWORK_ID: - $nbt->Health = new Short("Health", 20); - $entity = new Villager($chunk, $nbt); - break; - case Zombie::NETWORK_ID: - $nbt->Health = new Short("Health", 20); - $entity = new Zombie($chunk, $nbt); - break; - /* - //TODO: use entity constants - case 10: - case 11: - case 12: - case 13: - $data = array( - "x" => $block->x + 0.5, - "y" => $block->y, - "z" => $block->z + 0.5, - ); - //$e = Server::getInstance()->api->entity->add($block->level, ENTITY_MOB, $this->meta, $data); - //Server::getInstance()->api->entity->spawnToAll($e); - if(($player->gamemode & 0x01) === 0){ - --$this->count; - } - - return true;*/ - } + $entity = Entity::createEntity($this->meta, $chunk, $nbt); if($entity instanceof Entity){ - if(($player->gamemode & 0x01) === 0){ + if($player->isSurvival()){ --$this->count; } $entity->spawnToAll(); diff --git a/src/pocketmine/level/Explosion.php b/src/pocketmine/level/Explosion.php index aa2920c82..61a59c8df 100644 --- a/src/pocketmine/level/Explosion.php +++ b/src/pocketmine/level/Explosion.php @@ -191,7 +191,7 @@ class Explosion{ if($block instanceof TNT){ $mot = (new Random())->nextSignedFloat() * M_PI * 2; - $tnt = new PrimedTNT($this->level->getChunk($block->x >> 4, $block->z >> 4), new Compound("", [ + $tnt = Entity::createEntity("PrimedTNT", $this->level->getChunk($block->x >> 4, $block->z >> 4), new Compound("", [ "Pos" => new Enum("Pos", [ new Double("", $block->x + 0.5), new Double("", $block->y), diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 0925e1fd7..c3ae3eb04 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -45,7 +45,7 @@ use pocketmine\block\SnowLayer; use pocketmine\block\Sugarcane; use pocketmine\block\Wheat; use pocketmine\entity\Arrow; -use pocketmine\entity\DroppedItem; +use pocketmine\entity\Item as DroppedItem; use pocketmine\entity\Entity; use pocketmine\event\block\BlockBreakEvent; use pocketmine\event\block\BlockPlaceEvent; @@ -1000,7 +1000,7 @@ class Level implements ChunkManager, Metadatable{ public function dropItem(Vector3 $source, Item $item, Vector3 $motion = null, $delay = 10){ $motion = $motion === null ? Vector3::createVector(lcg_value() * 0.2 - 0.1, 0.2, lcg_value() * 0.2 - 0.1) : $motion; if($item->getID() > 0 and $item->getCount() > 0){ - $itemEntity = new DroppedItem($this->getChunk($source->getX() >> 4, $source->getZ() >> 4), new Compound("", [ + $itemEntity = Entity::createEntity("Item", $this->getChunk($source->getX() >> 4, $source->getZ() >> 4), new Compound("", [ "Pos" => new Enum("Pos", [ new Double("", $source->getX()), new Double("", $source->getY()), @@ -1233,7 +1233,7 @@ class Level implements ChunkManager, Metadatable{ } if($hand->getID() === Item::SIGN_POST or $hand->getID() === Item::WALL_SIGN){ - $tile = new Sign($this->getChunk($block->x >> 4, $block->z >> 4), new Compound(false, [ + $tile = Tile::createTile("Sign", $this->getChunk($block->x >> 4, $block->z >> 4), new Compound(false, [ "id" => new String("id", Tile::SIGN), "x" => new Int("x", $block->x), "y" => new Int("y", $block->y), diff --git a/src/pocketmine/level/format/generic/BaseFullChunk.php b/src/pocketmine/level/format/generic/BaseFullChunk.php index b24daba55..b315736d6 100644 --- a/src/pocketmine/level/format/generic/BaseFullChunk.php +++ b/src/pocketmine/level/format/generic/BaseFullChunk.php @@ -21,10 +21,7 @@ namespace pocketmine\level\format\generic; -use pocketmine\entity\Arrow; -use pocketmine\entity\DroppedItem; use pocketmine\entity\Entity; -use pocketmine\entity\FallingBlock; use pocketmine\level\format\FullChunk; use pocketmine\level\format\LevelProvider; use pocketmine\nbt\tag\Compound; @@ -116,27 +113,20 @@ abstract class BaseFullChunk implements FullChunk{ foreach($this->NBTentities as $nbt){ if($nbt instanceof Compound){ if(!isset($nbt->id)){ + $this->setChanged(); continue; } if(($nbt["Pos"][0] >> 4) !== $this->x or ($nbt["Pos"][2] >> 4) !== $this->z){ + $this->setChanged(); continue; //Fixes entities allocated in wrong chunks. } - //TODO: add all entities - switch($nbt["id"]){ - case DroppedItem::NETWORK_ID: - case "Item": - (new DroppedItem($this, $nbt))->spawnToAll(); - break; - case Arrow::NETWORK_ID: - case "Arrow": - (new Arrow($this, $nbt))->spawnToAll(); - break; - case FallingBlock::NETWORK_ID: - case "FallingSand": - (new FallingBlock($this, $nbt))->spawnToAll(); - break; + if(($entity = Entity::createEntity($nbt["id"], $this, $nbt)) instanceof Entity){ + $entity->spawnToAll(); + }else{ + $this->setChanged(); + continue; } } } @@ -155,16 +145,9 @@ abstract class BaseFullChunk implements FullChunk{ continue; //Fixes tiles allocated in wrong chunks. } - switch($nbt["id"]){ - case Tile::CHEST: - new Chest($this, $nbt); - break; - case Tile::FURNACE: - new Furnace($this, $nbt); - break; - case Tile::SIGN: - new Sign($this, $nbt); - break; + if(Tile::createTile($nbt["id"], $this, $nbt) === null){ + $this->setChanged(); + continue; } } } diff --git a/src/pocketmine/tile/Tile.php b/src/pocketmine/tile/Tile.php index b87fd6a43..d22bebe38 100644 --- a/src/pocketmine/tile/Tile.php +++ b/src/pocketmine/tile/Tile.php @@ -40,6 +40,9 @@ abstract class Tile extends Position{ public static $tileCount = 1; + /** @var Tile[] */ + private static $knownTiles = []; + /** @var Chunk */ public $chunk; public $name; @@ -58,6 +61,38 @@ abstract class Tile extends Position{ /** @var \pocketmine\event\TimingsHandler */ public $tickTimer; + /** + * @param string $type + * @param FullChunk $chunk + * @param Compound $nbt + * @param $args + * + * @return Tile + */ + public static function createTile($type, FullChunk $chunk, Compound $nbt, ...$args){ + if(isset(self::$knownTiles[$type])){ + $class = self::$knownTiles[$type]; + return new $class($chunk, $nbt, ...$args); + } + + return null; + } + + /** + * @param $className + * + * @return bool + */ + public static function registerTile($className){ + $class = new \ReflectionClass($className); + if(is_a($className, Tile::class, true) and !$class->isAbstract()){ + self::$knownTiles[$class->getShortName()] = $className; + return true; + } + + return false; + } + public function __construct(FullChunk $chunk, Compound $nbt){ if($chunk === null or $chunk->getProvider() === null){ throw new \Exception("Invalid garbage Chunk given to Tile");