New way to spawn entities/tiles using a global register table, allow overriding default entity/tile classes via classes

This commit is contained in:
Shoghi Cervantes 2014-10-28 13:09:27 +01:00
parent a5b85c549a
commit 34ae760def
18 changed files with 154 additions and 94 deletions

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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){

View File

@ -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);
}

View File

@ -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;
}
}
}

View File

@ -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),

View File

@ -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),

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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),

View File

@ -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),

View File

@ -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;
}
}
}

View File

@ -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");