Items spawn, Tiles spawn, both are saved, blocks drop

This commit is contained in:
Shoghi Cervantes 2014-05-24 17:25:37 +02:00
parent f2dc9cdff7
commit ec055fd8d1
9 changed files with 293 additions and 48 deletions

View File

@ -1432,11 +1432,10 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
//TODO
//$this->heal($this->data->get("health"), "spawn", true);
$this->spawned = true;
$this->spawnToAll();
$this->sendSettings();
$this->getInventory()->sendContents($this);
$this->getInventory()->sendArmorContents($this);
$this->inventory->sendContents($this);
$this->inventory->sendArmorContents($this);
$this->tasks[] = $this->server->getScheduler()->scheduleDelayedTask(new CallbackTask(array($this, "orderChunks")), 30);
$this->blocked = false;
@ -1451,6 +1450,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $pos));
$this->teleport($ev->getRespawnPosition());
$this->spawnToAll();
$this->sendBuffer();
break;
@ -1705,9 +1705,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$item = clone $this->inventory->getItemInHand();
}
if(($drops = $this->getLevel()->useBreakOn($vector, $item)) !== true){
if($this->getLevel()->useBreakOn($vector, $item) === true){
if(($this->gamemode & 0x01) === 0){
//TODO: drop items
$this->inventory->setItemInHand($item);
}
break;

View File

@ -21,7 +21,141 @@
namespace pocketmine\entity;
use pocketmine\item\Item;
use pocketmine\nbt\tag\Byte;
use pocketmine\nbt\tag\Compound;
use pocketmine\nbt\tag\Short;
use pocketmine\nbt\tag\String;
use pocketmine\network\protocol\AddItemEntityPacket;
use pocketmine\Player;
class DroppedItem extends Entity{
protected $age = 0;
protected $owner = null;
protected $thrower = null;
protected $pickupDelay = 0;
/** @var Item */
protected $item;
protected function initEntity(){
//TODO: upgrade old numeric entity ids
$this->namedtag->id = new String("id", "Item");
$this->setMaxHealth(5);
$this->setHealth(@$this->namedtag["Health"]);
if(isset($this->namedtag->Age)){
$this->age = $this->namedtag["Age"];
}
if(isset($this->namedtag->PickupDelay)){
$this->pickupDelay = $this->namedtag["PickupDelay"];
}
if(isset($this->namedtag->Owner)){
$this->owner = $this->namedtag["Owner"];
}
if(isset($this->namedtag->Thrower)){
$this->thrower = $this->namedtag["Thrower"];
}
$this->item = Item::get($this->namedtag->Item["id"], $this->namedtag->Item["Damage"], min(64, $this->namedtag->Item["Count"]));
}
public function attack($damage, $source = "generic"){
}
public function heal($amount, $source = "generic"){
}
public function saveNBT(){
$this->namedtag->Item = new Compound("Item", [
"id" => new Short("id", $this->item->getID()),
"Damage" => new Short("Damage", $this->item->getDamage()),
"Count" => new Byte("Count", $this->item->getCount())
]);
$this->namedtag->Health = new Short("Health", $this->getHealth());
$this->namedtag->Age = new Short("Age", $this->age);
$this->namedtag->PickupDelay = new Short("PickupDelay", $this->pickupDelay);
if($this->owner !== null){
$this->namedtag->Owner = new String("Owner", $this->owner);
}
if($this->thrower !== null){
$this->namedtag->Thrower = new String("Thrower", $this->thrower);
}
}
public function getData(){
$flags = 0;
$flags |= $this->fireTicks > 0 ? 1 : 0;
return [
0 => array("type" => 0, "value" => $flags),
1 => array("type" => 1, "value" => 0),
16 => array("type" => 0, "value" => 0),
17 => array("type" => 6, "value" => array(0, 0, 0)),
];
}
/**
* @return Item
*/
public function getItem(){
return $this->item;
}
/**
* @return int
*/
public function getPickupDelay(){
return $this->pickupDelay;
}
/**
* @param int $delay
*/
public function setPickupDelay($delay){
$this->pickupDelay = $delay;
}
/**
* @return string
*/
public function getOwner(){
return $this->owner;
}
/**
* @param string $owner
*/
public function setOwner($owner){
$this->owner = $owner;
}
/**
* @return string
*/
public function getThrower(){
return $this->thrower;
}
/**
* @param string $thrower
*/
public function setThrower($thrower){
$this->thrower = $thrower;
}
public function spawnTo(Player $player){
$pk = new AddItemEntityPacket();
$pk->eid = $this->getID();
$pk->x = $this->x;
$pk->y = $this->y;
$pk->z = $this->z;
$pk->yaw = $this->yaw;
$pk->pitch = $this->pitch;
$pk->roll = 0;
$pk->item = $this->getItem();
$pk->metadata = $this->getData();
$player->dataPacket($pk);
parent::spawnTo($player);
}
}

View File

@ -36,7 +36,10 @@ use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3 as Vector3;
use pocketmine\metadata\Metadatable;
use pocketmine\metadata\MetadataValue;
use pocketmine\nbt\tag\Byte;
use pocketmine\nbt\tag\Compound;
use pocketmine\nbt\tag\Float;
use pocketmine\nbt\tag\Short;
use pocketmine\Network;
use pocketmine\network\protocol\MoveEntityPacket_PosRot;
use pocketmine\network\protocol\MovePlayerPacket;
@ -108,35 +111,67 @@ abstract class Entity extends Position implements Metadatable{
protected $fireProof;
private $invulnerable;
protected $spawnTime;
/** @var Server */
protected $server;
public $closed;
public $closed = false;
public function __construct(Level $level, Compound $nbt){
$this->id = Entity::$entityCount++;
$this->justCreated = true;
$this->closed = false;
$this->namedtag = $nbt;
$this->setLevel($level, true); //Create a hard reference
$this->server = Server::getInstance();
$this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
$this->setPositionAndRotation(new Vector3($this->namedtag["Pos"][0], $this->namedtag["Pos"][1], $this->namedtag["Pos"][2]), $this->namedtag->Rotation[0], $this->namedtag->Rotation[1]);
$this->setMotion(new Vector3($this->namedtag["Motion"][0], $this->namedtag["Motion"][1], $this->namedtag["Motion"][2]));
$this->setPositionAndRotation(new Vector3(
$this->namedtag["Pos"][0],
$this->namedtag["Pos"][1],
$this->namedtag["Pos"][2]),
$this->namedtag->Rotation[0],
$this->namedtag->Rotation[1]
);
$this->setMotion(new Vector3(
$this->namedtag["Motion"][0],
$this->namedtag["Motion"][1],
$this->namedtag["Motion"][2])
);
if(!isset($this->namedtag->FallDistance)){
$this->namedtag->FallDistance = new Float("FallDistance", 0);
}
$this->fallDistance = $this->namedtag["FallDistance"];
if(!isset($this->namedtag->Fire)){
$this->namedtag->Fire = new Short("Fire", 0);
}
$this->fireTicks = $this->namedtag["Fire"];
if(!isset($this->namedtag->Air)){
$this->namedtag->Air = new Short("Air", 300);
}
$this->airTicks = $this->namedtag["Air"];
if(!isset($this->namedtag->OnGround)){
$this->namedtag->OnGround = new Byte("OnGround", 1);
}
$this->onGround = $this->namedtag["OnGround"] > 0 ? true : false;
if(!isset($this->namedtag->Invulnerable)){
$this->namedtag->Invulnerable = new Byte("Invulnerable", 0);
}
$this->invulnerable = $this->namedtag["Invulnerable"] > 0 ? true : false;
$index = LevelFormat::getIndex($this->x >> 4, $this->z >> 4);
$this->chunkIndex = $index;
$this->getLevel()->addEntity($this);
$this->initEntity();
$this->getLevel()->chunkEntities[$this->chunkIndex][$this->id] = $this;
$this->lastUpdate = microtime(true);
$this->lastUpdate = $this->spawnTime = microtime(true);
$this->justCreated = false;
$this->server->getPluginManager()->callEvent(new EntitySpawnEvent($this));
}
@ -165,12 +200,18 @@ abstract class Entity extends Position implements Metadatable{
return $this->hasSpawned;
}
/**
* @param Player $player
*/
public function spawnTo(Player $player){
if(!isset($this->hasSpawned[$player->getID()]) and $player->chunksLoaded[$this->chunkIndex] !== 0xff){
$this->hasSpawned[$player->getID()] = $player;
}
}
/**
* @param Player $player
*/
public function despawnFrom(Player $player){
if(isset($this->hasSpawned[$player->getID()])){
$pk = new RemoveEntityPacket;
@ -246,14 +287,14 @@ abstract class Entity extends Position implements Metadatable{
}
}else{
if(($this->fireTicks % 20) === 0){
$this->attackEntity(1, "onFire");
$this->attack(1, "onFire");
}
--$this->fireTicks;
}
}
if($this->handleLavaMovement()){
$this->attackEntity(4, "lava");
$this->attack(4, "lava");
$this->setOnFire(15);
$this->fallDistance *= 0.5;
}
@ -389,7 +430,7 @@ abstract class Entity extends Position implements Metadatable{
protected function switchLevel(Level $targetLevel){
if($this->isValid()){
Server::getInstance()->getPluginManager()->callEvent($ev = new EntityLevelChangeEvent($this, $this->getLevel(), $targetLevel));
$this->server->getPluginManager()->callEvent($ev = new EntityLevelChangeEvent($this, $this->getLevel(), $targetLevel));
if($ev->isCancelled()){
return false;
}
@ -433,9 +474,6 @@ abstract class Entity extends Position implements Metadatable{
return;
}
$x = $this->x;
$y = $this->y;
$z = $this->z;
$this->scheduleUpdate();
}
@ -461,15 +499,20 @@ abstract class Entity extends Position implements Metadatable{
return false;
}
}
Server::getInstance()->getPluginManager()->callEvent($ev = new EntityMoveEvent($this, $pos));
if($ev->isCancelled()){
return false;
if(!$this->justCreated){
$this->server->getPluginManager()->callEvent($ev = new EntityMoveEvent($this, $pos));
if($ev->isCancelled()){
return false;
}
}
$this->x = $pos->x;
$this->y = $pos->y;
$this->z = $pos->z;
$radius = $this->width / 2;
$this->boundingBox->setBounds($pos->x - $radius, $pos->y, $pos->z - $radius, $pos->x + $radius, $pos->y + $this->height, $pos->z + $radius);
if(($index = LevelFormat::getIndex($this->x >> 4, $this->z >> 4)) !== $this->chunkIndex){
if($this->chunkIndex !== false){
unset($this->getLevel()->chunkEntities[$this->chunkIndex][$this->id]);
@ -477,21 +520,22 @@ abstract class Entity extends Position implements Metadatable{
$this->chunkIndex = $index;
$this->getLevel()->loadChunk($this->x >> 4, $this->z >> 4);
$newChunk = $this->getLevel()->getUsingChunk($this->x >> 4, $this->z >> 4);
foreach($this->hasSpawned as $player){
if(!isset($newChunk[$player->CID])){
$this->despawnFrom($player);
}else{
unset($newChunk[$player->CID]);
if(!$this->justCreated){
$newChunk = $this->getLevel()->getUsingChunk($this->x >> 4, $this->z >> 4);
foreach($this->hasSpawned as $player){
if(!isset($newChunk[$player->CID])){
$this->despawnFrom($player);
}else{
unset($newChunk[$player->CID]);
}
}
foreach($newChunk as $player){
$this->spawnTo($player);
}
}
foreach($newChunk as $player){
$this->spawnTo($player);
}
$this->getLevel()->chunkEntities[$this->chunkIndex][$this->id] = $this;
}
$this->boundingBox->setBounds($pos->x - $radius, $pos->y, $pos->z - $radius, $pos->x + $radius, $pos->y + $this->height, $pos->z + $radius);
$this->scheduleUpdate();
@ -503,14 +547,18 @@ abstract class Entity extends Position implements Metadatable{
}
public function setMotion(Vector3 $motion){
Server::getInstance()->getPluginManager()->callEvent($ev = new EntityMotionEvent($this, $motion));
if($ev->isCancelled()){
return false;
if(!$this->justCreated){
Server::getInstance()->getPluginManager()->callEvent($ev = new EntityMotionEvent($this, $motion));
if($ev->isCancelled()){
return false;
}
}
$this->motionX = $motion->x;
$this->motionY = $motion->y;
$this->motionZ = $motion->z;
$this->scheduleUpdate();
if(!$this->justCreated){
$this->scheduleUpdate();
}
}
public function isOnGround(){
@ -553,7 +601,7 @@ abstract class Entity extends Position implements Metadatable{
}
public function spawnToAll(){
foreach($this->getLevel()->getPlayers() as $player){
foreach($this->getLevel()->getUsingChunk($this->x >> 4, $this->z >> 4) as $player){
if(isset($player->id) and $player->spawned === true){
$this->spawnTo($player);
}
@ -573,7 +621,7 @@ abstract class Entity extends Position implements Metadatable{
$this->getLevel()->removeEntity($this);
unset($this->getLevel()->chunkEntities[$this->chunkIndex][$this->id]);
$this->despawnFromAll();
Server::getInstance()->getPluginManager()->callEvent(new EntityDespawnEvent($this));
$this->server->getPluginManager()->callEvent(new EntityDespawnEvent($this));
}
}

View File

@ -24,6 +24,7 @@ namespace pocketmine\entity;
use pocketmine\inventory\InventoryHolder;
use pocketmine\inventory\PlayerInventory;
use pocketmine\item\Item;
use pocketmine\level\Level;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\Byte;
use pocketmine\nbt\tag\Compound;
@ -46,6 +47,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
}
protected function initEntity(){
$this->inventory = new PlayerInventory($this);
if($this instanceof Player){
$this->addWindow($this->inventory, 0);
@ -68,6 +70,8 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
$this->height = 1.8; //Or 1.62?
$this->width = 0.6;
parent::initEntity();
}
public function saveNBT(){

View File

@ -23,5 +23,7 @@ namespace pocketmine\entity;
abstract class Living extends Entity implements Damageable{
protected function initEntity(){
}
}

View File

@ -26,19 +26,25 @@ namespace pocketmine\level;
use pocketmine\block\Air;
use pocketmine\block\Block;
use pocketmine\entity\DroppedItem;
use pocketmine\entity\Entity;
use pocketmine\event\block\BlockBreakEvent;
use pocketmine\event\block\BlockPlaceEvent;
use pocketmine\event\player\PlayerInteractEvent;
use pocketmine\item\Item;
use pocketmine\level\format\pmf\LevelFormat;
use pocketmine\level\generator\Flat;
use pocketmine\level\generator\Generator;
use pocketmine\math\Vector2;
use pocketmine\math\Vector3 as Vector3;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\Byte;
use pocketmine\nbt\tag\Compound;
use pocketmine\nbt\tag\Double;
use pocketmine\nbt\tag\Enum;
use pocketmine\nbt\tag\Float;
use pocketmine\nbt\tag\Int;
use pocketmine\nbt\tag\Short;
use pocketmine\nbt\tag\String;
use pocketmine\network\protocol\SetTimePacket;
use pocketmine\network\protocol\UpdateBlockPacket;
@ -297,6 +303,7 @@ class Level{
if(count($this->changedBlocks) > 0){
foreach($this->changedBlocks as $index => $mini){
foreach($mini as $blocks){
/** @var Block $b */
foreach($blocks as $b){
$pk = new UpdateBlockPacket;
$pk->x = $b->x;
@ -606,6 +613,35 @@ class Level{
return $ret;
}
public function dropItem(Vector3 $pos, Item $item){
if($item->getID() !== Item::AIR and $item->getCount() > 0){
$itemEntity = new DroppedItem($this, new Compound("", [
"Pos" => new Enum("Pos", [
new Double("", $pos->getX()),
new Double("", $pos->getY()),
new Double("", $pos->getZ())
]),
//TODO: add random motion with physics
"Motion" => new Enum("Motion", [
new Double("", 0),
new Double("", 0),
new Double("", 0)
]),
"Rotation" => new Enum("Rotation", [
new Float("", 0),
new Float("", 0)
]),
"Health" => new Short("Health", 5),
"Item" => new Compound("Item", [
"id" => new Short("id", $item->getID()),
"Damage" => new Short("Damage", $item->getDamage()),
"Count" => new Byte("Count", $item->getCount())
]),
]));
$itemEntity->spawnToAll();
}
}
/**
* Tries to break a block using a item, including Player time checks if available
*
@ -658,7 +694,12 @@ class Level{
}
}
return $target->getDrops($item);
if(!($player instanceof Player) or ($player->getGamemode() & 0x01) === 0){
foreach($target->getDrops($item) as $drop){
$this->dropItem($vector->add(0.5, 0.5, 0.5), Item::get($drop[0], $drop[1], $drop[2]));
}
}
return true;
}
/**
@ -817,6 +858,7 @@ class Level{
}
public function addEntity(Entity $entity){
//TODO: chunkIndex
$this->entities[$entity->getID()] = $entity;
}
@ -825,6 +867,7 @@ class Level{
}
public function addTile(Tile $tile){
//TODO: chunkIndex
$this->tiles[$tile->getID()] = $tile;
}
@ -962,21 +1005,35 @@ class Level{
return true;
}elseif($this->level->loadChunk($X, $Z) !== false){
$this->usedChunks[$index] = [];
$this->chunkTiles[$index] = [];
$this->chunkEntities[$index] = [];
if(!isset($this->chunkTiles[$index])){
$this->chunkTiles[$index] = [];
}
if(!isset($this->chunkEntities[$index])){
$this->chunkEntities[$index] = [];
}
$tags = $this->level->getChunkNBT($X, $Z);
if(isset($tags->Entities)){
foreach($tags->Entities as $nbt){
if(!isset($nbt["id"])){
if(!isset($nbt->id)){
continue;
}
switch($nbt["id"]){
//TODO: spawn entities
if($nbt->id instanceof String){ //New format
switch($nbt["id"]){
case "Ttem":
(new DroppedItem($this, $nbt))->spawnToAll();
break;
}
}else{ //Old format
}
}
}
if(isset($tags->TileEntities)){
foreach($tags->TileEntities as $nbt){
if(!isset($nbt->id)){
continue;
}
switch($nbt["id"]){
case Tile::CHEST:
new Chest($this, $nbt);

View File

@ -126,7 +126,7 @@ class Position extends Vector3{
}
public function __toString(){
return "Position(level=" . ($this->isValid() ? $this->getLevel()->getID() : "null") . ",x=" . $this->x . ",y=" . $this->y . ",z=" . $this->z . ")";
return "Position(level=" . ($this->isValid() ? $this->getLevel()->getName() : "null") . ",x=" . $this->x . ",y=" . $this->y . ",z=" . $this->z . ")";
}
}

View File

@ -21,11 +21,18 @@
namespace pocketmine\tile;
use pocketmine\level\Level;
use pocketmine\nbt\tag\Compound;
use pocketmine\Player;
abstract class Spawnable extends Tile{
public abstract function spawnTo(Player $player);
public function __construct(Level $level, Compound $nbt){
parent::__construct($level, $nbt);
$this->spawnToAll();
}
public function spawnToAll(){
foreach($this->getLevel()->getPlayers() as $player){
if($player->spawned === true){

View File

@ -40,11 +40,6 @@ abstract class Tile extends Position{
public static $tileCount = 1;
/**
* @var Tile[]
*/
public static $list = [];
/**
* @var Tile[]
*/
@ -76,7 +71,6 @@ abstract class Tile extends Position{
$this->name = "";
$this->lastUpdate = microtime(true);
$this->id = Tile::$tileCount++;
Tile::$list[$this->id] = $this;
$this->x = (int) $this->namedtag["x"];
$this->y = (int) $this->namedtag["y"];
$this->z = (int) $this->namedtag["z"];