diff --git a/src/pocketmine/MemoryManager.php b/src/pocketmine/MemoryManager.php index 69ca142b4..bbca6a3ca 100644 --- a/src/pocketmine/MemoryManager.php +++ b/src/pocketmine/MemoryManager.php @@ -26,6 +26,7 @@ use pocketmine\event\Timings; use pocketmine\scheduler\GarbageCollectionTask; use pocketmine\utils\Utils; + class MemoryManager{ /** @var Server */ @@ -231,7 +232,7 @@ class MemoryManager{ } $this->leakInfo[$identifier] = [ - "id" => $id = Utils::dataToUUID($identifier . ":" . $this->leakSeed++), + "id" => $id = md5($identifier . ":" . $this->leakSeed++), "class" => get_class($object), "hash" => $identifier ]; diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index ae38135f3..f1985bc04 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -37,6 +37,7 @@ use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityRegainHealthEvent; use pocketmine\event\entity\EntityShootBowEvent; use pocketmine\event\entity\ProjectileLaunchEvent; +use pocketmine\event\inventory\CraftItemEvent; use pocketmine\event\inventory\InventoryCloseEvent; use pocketmine\event\inventory\InventoryPickupArrowEvent; use pocketmine\event\inventory\InventoryPickupItemEvent; @@ -58,21 +59,24 @@ use pocketmine\event\player\PlayerMoveEvent; use pocketmine\event\player\PlayerPreLoginEvent; use pocketmine\event\player\PlayerQuitEvent; use pocketmine\event\player\PlayerRespawnEvent; +use pocketmine\event\player\PlayerToggleSneakEvent; +use pocketmine\event\player\PlayerToggleSprintEvent; use pocketmine\event\server\DataPacketReceiveEvent; use pocketmine\event\server\DataPacketSendEvent; use pocketmine\event\TextContainer; use pocketmine\event\Timings; use pocketmine\event\TranslationContainer; use pocketmine\inventory\BaseTransaction; +use pocketmine\inventory\BigShapedRecipe; use pocketmine\inventory\BigShapelessRecipe; -use pocketmine\inventory\CraftingTransactionGroup; use pocketmine\inventory\FurnaceInventory; use pocketmine\inventory\Inventory; use pocketmine\inventory\InventoryHolder; use pocketmine\inventory\PlayerInventory; +use pocketmine\inventory\ShapedRecipe; use pocketmine\inventory\ShapelessRecipe; use pocketmine\inventory\SimpleTransactionGroup; -use pocketmine\inventory\StonecutterShapelessRecipe; + use pocketmine\item\Item; use pocketmine\level\ChunkLoader; use pocketmine\level\format\FullChunk; @@ -99,16 +103,19 @@ use pocketmine\network\Network; use pocketmine\network\protocol\AdventureSettingsPacket; use pocketmine\network\protocol\AnimatePacket; use pocketmine\network\protocol\BatchPacket; +use pocketmine\network\protocol\ContainerClosePacket; use pocketmine\network\protocol\ContainerSetContentPacket; use pocketmine\network\protocol\DataPacket; use pocketmine\network\protocol\DisconnectPacket; use pocketmine\network\protocol\EntityEventPacket; use pocketmine\network\protocol\FullChunkDataPacket; use pocketmine\network\protocol\Info as ProtocolInfo; +use pocketmine\network\protocol\PlayerActionPacket; use pocketmine\network\protocol\PlayStatusPacket; use pocketmine\network\protocol\RespawnPacket; +use pocketmine\network\protocol\SetPlayerGameTypePacket; use pocketmine\network\protocol\TextPacket; -use pocketmine\network\protocol\MoveEntityPacket; + use pocketmine\network\protocol\MovePlayerPacket; use pocketmine\network\protocol\SetDifficultyPacket; use pocketmine\network\protocol\SetEntityMotionPacket; @@ -126,7 +133,9 @@ use pocketmine\tile\Sign; use pocketmine\tile\Spawnable; use pocketmine\tile\Tile; use pocketmine\utils\TextFormat; -use pocketmine\utils\Utils; + + +use raklib\Binary; /** * Main class that handles networking, recovery, and packet sending to the server part @@ -160,6 +169,8 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade protected $sendIndex = 0; + private $clientSecret; + /** @var Vector3 */ public $speed = null; @@ -181,7 +192,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade public $creationTime = 0; protected $randomClientId; - protected $uuid; protected $lastMovement = 0; /** @var Vector3 */ @@ -252,8 +262,8 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade return $this->randomClientId; } - public function getUniqueId(){ - return $this->uuid; + public function getClientSecret(){ + return $this->clientSecret; } public function isBanned(){ @@ -351,7 +361,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade * @return bool */ public function canSee(Player $player){ - return !isset($this->hiddenPlayers[$player->getUniqueId()]); + return !isset($this->hiddenPlayers[$player->getRawUniqueId()]); } /** @@ -361,7 +371,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if($player === $this){ return; } - $this->hiddenPlayers[$player->getUniqueId()] = $player; + $this->hiddenPlayers[$player->getRawUniqueId()] = $player; $player->despawnFrom($this); } @@ -372,7 +382,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if($player === $this){ return; } - unset($this->hiddenPlayers[$player->getUniqueId()]); + unset($this->hiddenPlayers[$player->getRawUniqueId()]); if($player->isOnline()){ $player->spawnTo($this); } @@ -509,7 +519,8 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->newPosition = new Vector3(0, 0, 0); $this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0); - $this->uuid = Utils::dataToUUID($ip, $port, $clientID); + $this->uuid = null; + $this->rawUUID = null; $this->creationTime = microtime(true); } @@ -554,17 +565,20 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade return $this->displayName; } - /** - * @param string $name - */ + /** + * @param string $name + */ public function setDisplayName($name){ $this->displayName = $name; + if($this->spawned){ + $this->server->updatePlayerListData($this->getUniqueId(), $this->getId(), $this->getDisplayName(), $this->getSkinName(), $this->getSkinData()); + } } - public function setSkin($str, $isSlim = false){ - parent::setSkin($str, $isSlim); + public function setSkin($str, $skinName){ + parent::setSkin($str, $skinName); if($this->spawned){ - $this->respawnToAll(); + $this->server->updatePlayerListData($this->getUniqueId(), $this->getId(), $this->getDisplayName(), $skinName, $str); } } @@ -607,7 +621,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk = new SetTimePacket(); $pk->time = $this->level->getTime(); $pk->started = $this->level->stopTime == false; - $this->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $this->dataPacket($pk); } } @@ -620,18 +634,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $entity->despawnFrom($this); } } - //TODO HACK: removes tile entities that linger whenever you teleport - // to a different world - $pk = new UpdateBlockPacket(); - foreach($level->getChunkTiles($x, $z) as $tile){ - if($tile instanceof Spawnable){ - $pk->records[] = [$tile->x, $tile->z, $tile->y, 0, 0, UpdateBlockPacket::FLAG_NONE]; - } - } - if(count($pk->records)){ - $this->dataPacket($pk); - } - //---- unset($this->usedChunks[$index]); } @@ -652,7 +654,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } } - public function sendChunk($x, $z, $payload){ + public function sendChunk($x, $z, $payload, $ordering = FullChunkDataPacket::ORDER_COLUMNS){ if($this->connected === false){ return; } @@ -666,8 +668,9 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk = new FullChunkDataPacket(); $pk->chunkX = $x; $pk->chunkZ = $z; + $pk->order = $ordering; $pk->data = $payload; - $this->batchDataPacket($pk->setChannel($this->spawned ? Network::CHANNEL_WORLD_CHUNKS : Network::CHANNEL_PRIORITY)); + $this->batchDataPacket($pk); } if($this->spawned){ @@ -710,7 +713,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } unset($this->loadQueue[$index]); - $this->level->requestChunk($X, $Z, $this, LevelProvider::ORDER_ZXY); + $this->level->requestChunk($X, $Z, $this); } if($this->chunkLoadCount >= $this->spawnThreshold and $this->spawned === false and $this->teleportPosition === null){ @@ -732,7 +735,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk = new SetTimePacket(); $pk->time = $this->level->getTime(); $pk->started = $this->level->stopTime == false; - $this->dataPacket($pk->setChannel(Network::CHANNEL_PRIORITY)); + $this->dataPacket($pk); $pos = $this->level->getSafeSpawn($this); @@ -744,11 +747,11 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk->x = $pos->x; $pk->y = $pos->y; $pk->z = $pos->z; - $this->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_CHUNKS)); + $this->dataPacket($pk); $pk = new PlayStatusPacket(); $pk->status = PlayStatusPacket::PLAYER_SPAWN; - $this->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_CHUNKS)); + $this->dataPacket($pk); $this->server->getPluginManager()->callEvent($ev = new PlayerJoinEvent($this, new TranslationContainer(TextFormat::YELLOW . "%multiplayer.player.joined", [ @@ -784,7 +787,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk->x = $pos->x; $pk->y = $pos->y; $pk->z = $pos->z; - $this->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $this->dataPacket($pk); } } @@ -1011,7 +1014,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk->x = (int) $this->spawnPosition->x; $pk->y = (int) $this->spawnPosition->y; $pk->z = (int) $this->spawnPosition->z; - $this->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $this->dataPacket($pk); } public function stopSleep(){ @@ -1028,7 +1031,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk = new AnimatePacket(); $pk->eid = 0; $pk->action = 3; //Wake up - $this->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $this->dataPacket($pk); } } @@ -1083,7 +1086,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade return false; } - $this->gamemode = $gm; $this->allowFlight = $this->isCreative(); @@ -1096,33 +1098,22 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->namedtag->playerGameType = new Int("playerGameType", $this->gamemode); - $spawnPosition = $this->getSpawn(); - - $pk = new StartGamePacket(); - $pk->seed = -1; - $pk->x = $this->x; - $pk->y = $this->y; - $pk->z = $this->z; - $pk->spawnX = (int) $spawnPosition->x; - $pk->spawnY = (int) $spawnPosition->y; - $pk->spawnZ = (int) $spawnPosition->z; - $pk->generator = 1; //0 old, 1 infinite, 2 flat + $pk = new SetPlayerGameTypePacket(); $pk->gamemode = $this->gamemode & 0x01; - $pk->eid = 0; - $this->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $this->dataPacket($pk); $this->sendSettings(); if($this->gamemode === Player::SPECTATOR){ $pk = new ContainerSetContentPacket(); $pk->windowid = ContainerSetContentPacket::SPECIAL_CREATIVE; - $this->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $this->dataPacket($pk); }else{ $pk = new ContainerSetContentPacket(); $pk->windowid = ContainerSetContentPacket::SPECIAL_CREATIVE; foreach(Item::getCreativeItems() as $item){ $pk->slots[] = clone $item; } - $this->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $this->dataPacket($pk); } $this->inventory->sendContents($this); @@ -1194,7 +1185,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk = new AdventureSettingsPacket(); $pk->flags = $flags; - $this->dataPacket($pk->setChannel(Network::CHANNEL_PRIORITY)); + $this->dataPacket($pk); } public function isSurvival(){ @@ -1286,12 +1277,12 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk = new TakeItemEntityPacket(); $pk->eid = $this->getId(); $pk->target = $entity->getId(); - Server::broadcastPacket($entity->getViewers(), $pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + Server::broadcastPacket($entity->getViewers(), $pk); $pk = new TakeItemEntityPacket(); $pk->eid = 0; $pk->target = $entity->getId(); - $this->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $this->dataPacket($pk); $this->inventory->addItem(clone $item); $entity->kill(); @@ -1321,12 +1312,12 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk = new TakeItemEntityPacket(); $pk->eid = $this->getId(); $pk->target = $entity->getId(); - Server::broadcastPacket($entity->getViewers(), $pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + Server::broadcastPacket($entity->getViewers(), $pk); $pk = new TakeItemEntityPacket(); $pk->eid = 0; $pk->target = $entity->getId(); - $this->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $this->dataPacket($pk); $this->inventory->addItem(clone $item); $entity->kill(); @@ -1466,7 +1457,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->level->addEntityMotion($this->chunk->getX(), $this->chunk->getZ(), $this->getId(), $this->motionX, $this->motionY, $this->motionZ); $pk = new SetEntityMotionPacket(); $pk->entities[] = [0, $mot->x, $mot->y, $mot->z]; - $this->dataPacket($pk->setChannel(Network::CHANNEL_MOVEMENT)); + $this->dataPacket($pk); } if($this->motionY > 0){ @@ -1578,6 +1569,193 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade return ($dot1 - $dot) >= -$maxDiff; } + public function onPlayerPreLogin(){ + //TODO: implement auth + $this->tryAuthenticate(); + } + + public function tryAuthenticate(){ + //TODO: implement authentication after it is available + $this->authenticateCallback(true); + } + + public function authenticateCallback($valid){ + + //TODO add more stuff after authentication is available + if(!$valid){ + $this->close("", "disconnectionScreen.invalidSession"); + return; + } + + $this->processLogin(); + } + + protected function processLogin(){ + if(!$this->server->isWhitelisted(strtolower($this->getName()))){ + $this->close($this->getLeaveMessage(), "Server is white-listed"); + + return; + }elseif($this->server->getNameBans()->isBanned(strtolower($this->getName())) or $this->server->getIPBans()->isBanned($this->getAddress())){ + $this->close($this->getLeaveMessage(), "You are banned"); + + return; + } + + if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){ + $this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this); + } + if($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){ + $this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this); + } + + foreach($this->server->getOnlinePlayers() as $p){ + if($p !== $this and strtolower($p->getName()) === strtolower($this->getName())){ + if($p->kick("logged in from another location") === false){ + $this->close($this->getLeaveMessage(), "Logged in from another location"); + return; + } + }elseif($p->loggedIn and $this->getUniqueId()->equals($p->getUniqueId())){ + if($p->kick("logged in from another location") === false){ + $this->close($this->getLeaveMessage(), "Logged in from another location"); + return; + } + } + } + + $nbt = $this->server->getOfflinePlayerData($this->username); + if(!isset($nbt->NameTag)){ + $nbt->NameTag = new String("NameTag", $this->username); + }else{ + $nbt["NameTag"] = $this->username; + } + $this->gamemode = $nbt["playerGameType"] & 0x03; + if($this->server->getForceGamemode()){ + $this->gamemode = $this->server->getGamemode(); + $nbt->playerGameType = new Int("playerGameType", $this->gamemode); + } + + $this->allowFlight = $this->isCreative(); + + + if(($level = $this->server->getLevelByName($nbt["Level"])) === null){ + $this->setLevel($this->server->getDefaultLevel()); + $nbt["Level"] = $this->level->getName(); + $nbt["Pos"][0] = $this->level->getSpawnLocation()->x; + $nbt["Pos"][1] = $this->level->getSpawnLocation()->y; + $nbt["Pos"][2] = $this->level->getSpawnLocation()->z; + }else{ + $this->setLevel($level); + } + + if(!($nbt instanceof Compound)){ + $this->close($this->getLeaveMessage(), "Invalid data"); + + return; + } + + $this->achievements = []; + + /** @var Byte $achievement */ + foreach($nbt->Achievements as $achievement){ + $this->achievements[$achievement->getName()] = $achievement->getValue() > 0 ? true : false; + } + + $nbt->lastPlayed = new Long("lastPlayed", floor(microtime(true) * 1000)); + if($this->server->getAutoSave()){ + $this->server->saveOfflinePlayerData($this->username, $nbt, true); + } + + parent::__construct($this->level->getChunk($nbt["Pos"][0] >> 4, $nbt["Pos"][2] >> 4, true), $nbt); + $this->loggedIn = true; + $this->server->addOnlinePlayer($this); + + $this->server->getPluginManager()->callEvent($ev = new PlayerLoginEvent($this, "Plugin reason")); + if($ev->isCancelled()){ + $this->close($this->getLeaveMessage(), $ev->getKickMessage()); + + return; + } + + if($this->isCreative()){ + $this->inventory->setHeldItemSlot(0); + }else{ + $this->inventory->setHeldItemSlot($this->inventory->getHotbarSlotIndex(0)); + } + + $pk = new PlayStatusPacket(); + $pk->status = PlayStatusPacket::LOGIN_SUCCESS; + $this->dataPacket($pk); + + if($this->spawnPosition === null and isset($this->namedtag->SpawnLevel) and ($level = $this->server->getLevelByName($this->namedtag["SpawnLevel"])) instanceof Level){ + $this->spawnPosition = new Position($this->namedtag["SpawnX"], $this->namedtag["SpawnY"], $this->namedtag["SpawnZ"], $level); + } + + $spawnPosition = $this->getSpawn(); + + $pk = new StartGamePacket(); + $pk->seed = -1; + $pk->dimension = 0; + $pk->x = $this->x; + $pk->y = $this->y; + $pk->z = $this->z; + $pk->spawnX = (int) $spawnPosition->x; + $pk->spawnY = (int) $spawnPosition->y; + $pk->spawnZ = (int) $spawnPosition->z; + $pk->generator = 1; //0 old, 1 infinite, 2 flat + $pk->gamemode = $this->gamemode & 0x01; + $pk->eid = 0; //Always use EntityID as zero for the actual player + $this->dataPacket($pk); + + $pk = new SetTimePacket(); + $pk->time = $this->level->getTime(); + $pk->started = $this->level->stopTime == false; + $this->dataPacket($pk); + + $pk = new SetSpawnPositionPacket(); + $pk->x = (int) $spawnPosition->x; + $pk->y = (int) $spawnPosition->y; + $pk->z = (int) $spawnPosition->z; + $this->dataPacket($pk); + + $pk = new SetHealthPacket(); + $pk->health = $this->getHealth(); + $this->dataPacket($pk); + + $pk = new SetDifficultyPacket(); + $pk->difficulty = $this->server->getDifficulty(); + $this->dataPacket($pk); + + $this->server->getLogger()->info($this->getServer()->getLanguage()->translateString("pocketmine.player.logIn", [ + TextFormat::AQUA . $this->username . TextFormat::WHITE, + $this->ip, + $this->port, + $this->id, + $this->level->getName(), + round($this->x, 4), + round($this->y, 4), + round($this->z, 4) + ])); + + if($this->isOp()){ + $this->setRemoveFormat(false); + } + + if($this->gamemode === Player::SPECTATOR){ + $pk = new ContainerSetContentPacket(); + $pk->windowid = ContainerSetContentPacket::SPECIAL_CREATIVE; + $this->dataPacket($pk); + }else{ + $pk = new ContainerSetContentPacket(); + $pk->windowid = ContainerSetContentPacket::SPECIAL_CREATIVE; + $pk->slots = Item::getCreativeItems(); + $this->dataPacket($pk); + } + + $this->forceMovement = $this->teleportPosition = $this->getPosition(); + + $this->server->onPlayerLogin($this); + } + /** * Handles a Minecraft packet * TODO: Separate all of this in handlers @@ -1619,12 +1797,8 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->displayName = $this->username; $this->setNameTag($this->username); $this->iusername = strtolower($this->username); - $this->randomClientId = $packet->clientId; - $this->loginData = ["clientId" => $packet->clientId, "loginData" => null]; - $this->uuid = Utils::dataToUUID($this->randomClientId, $this->iusername, $this->getAddress()); - - if(count($this->server->getOnlinePlayers()) > $this->server->getMaxPlayers() and $this->kick("disconnectionScreen.serverFull", false)){ + if(count($this->server->getOnlinePlayers()) >= $this->server->getMaxPlayers() and $this->kick("disconnectionScreen.serverFull", false)){ break; } @@ -1634,19 +1808,25 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk = new PlayStatusPacket(); $pk->status = PlayStatusPacket::LOGIN_FAILED_CLIENT; - $this->directDataPacket($pk->setChannel(Network::CHANNEL_PRIORITY)); + $this->directDataPacket($pk); }else{ $message = "disconnectionScreen.outdatedServer"; $pk = new PlayStatusPacket(); $pk->status = PlayStatusPacket::LOGIN_FAILED_SERVER; - $this->directDataPacket($pk->setChannel(Network::CHANNEL_PRIORITY)); + $this->directDataPacket($pk); } $this->close("", $message, false); break; } + $this->randomClientId = $packet->clientId; + + $this->uuid = $packet->clientUUID; + $this->rawUUID = $this->uuid->toBinary(); + $this->clientSecret = $packet->clientSecret; + $valid = true; $len = strlen($packet->username); if($len > 16 or $len < 3){ @@ -1670,13 +1850,13 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade break; } - + if(strlen($packet->skin) !== 64 * 32 * 4 and strlen($packet->skin) !== 64 * 64 * 4){ $this->close("", "disconnectionScreen.invalidSkin"); break; } - $this->setSkin($packet->skin, $packet->slim); + $this->setSkin($packet->skin, $packet->skinName); $this->server->getPluginManager()->callEvent($ev = new PlayerPreLoginEvent($this, "Plugin reason")); if($ev->isCancelled()){ @@ -1685,164 +1865,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade break; } - if(!$this->server->isWhitelisted(strtolower($this->getName()))){ - $this->close($this->getLeaveMessage(), "Server is white-listed"); - - break; - }elseif($this->server->getNameBans()->isBanned(strtolower($this->getName())) or $this->server->getIPBans()->isBanned($this->getAddress())){ - $this->close($this->getLeaveMessage(), "You are banned"); - - break; - } - - if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){ - $this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this); - } - if($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){ - $this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this); - } - - foreach($this->server->getOnlinePlayers() as $p){ - if($p !== $this and strtolower($p->getName()) === strtolower($this->getName())){ - if($p->kick("logged in from another location") === false){ - $this->close($this->getLeaveMessage(), "Logged in from another location"); - - $timings->stopTiming(); - return; - } - } - } - - $nbt = $this->server->getOfflinePlayerData($this->username); - if(!isset($nbt->NameTag)){ - $nbt->NameTag = new String("NameTag", $this->username); - }else{ - $nbt["NameTag"] = $this->username; - } - $this->gamemode = $nbt["playerGameType"] & 0x03; - if($this->server->getForceGamemode()){ - $this->gamemode = $this->server->getGamemode(); - $nbt->playerGameType = new Int("playerGameType", $this->gamemode); - } - - $this->allowFlight = $this->isCreative(); - - - if(($level = $this->server->getLevelByName($nbt["Level"])) === null){ - $this->setLevel($this->server->getDefaultLevel()); - $nbt["Level"] = $this->level->getName(); - $nbt["Pos"][0] = $this->level->getSpawnLocation()->x; - $nbt["Pos"][1] = $this->level->getSpawnLocation()->y; - $nbt["Pos"][2] = $this->level->getSpawnLocation()->z; - }else{ - $this->setLevel($level); - } - - if(!($nbt instanceof Compound)){ - $this->close($this->getLeaveMessage(), "Invalid data"); - - break; - } - - $this->achievements = []; - - /** @var Byte $achievement */ - foreach($nbt->Achievements as $achievement){ - $this->achievements[$achievement->getName()] = $achievement->getValue() > 0 ? true : false; - } - - $nbt->lastPlayed = new Long("lastPlayed", floor(microtime(true) * 1000)); - if($this->server->getAutoSave()){ - $this->server->saveOfflinePlayerData($this->username, $nbt, true); - } - - parent::__construct($this->level->getChunk($nbt["Pos"][0] >> 4, $nbt["Pos"][2] >> 4, true), $nbt); - $this->loggedIn = true; - - $this->server->getPluginManager()->callEvent($ev = new PlayerLoginEvent($this, "Plugin reason")); - if($ev->isCancelled()){ - $this->close($this->getLeaveMessage(), $ev->getKickMessage()); - - break; - } - - if($this->isCreative()){ - $this->inventory->setHeldItemSlot(0); - }else{ - $this->inventory->setHeldItemSlot($this->inventory->getHotbarSlotIndex(0)); - } - - $pk = new PlayStatusPacket(); - $pk->status = PlayStatusPacket::LOGIN_SUCCESS; - $this->dataPacket($pk->setChannel(Network::CHANNEL_PRIORITY)); - - if($this->spawnPosition === null and isset($this->namedtag->SpawnLevel) and ($level = $this->server->getLevelByName($this->namedtag["SpawnLevel"])) instanceof Level){ - $this->spawnPosition = new Position($this->namedtag["SpawnX"], $this->namedtag["SpawnY"], $this->namedtag["SpawnZ"], $level); - } - - $spawnPosition = $this->getSpawn(); - - $pk = new StartGamePacket(); - $pk->seed = -1; - $pk->x = $this->x; - $pk->y = $this->y; - $pk->z = $this->z; - $pk->spawnX = (int) $spawnPosition->x; - $pk->spawnY = (int) $spawnPosition->y; - $pk->spawnZ = (int) $spawnPosition->z; - $pk->generator = 1; //0 old, 1 infinite, 2 flat - $pk->gamemode = $this->gamemode & 0x01; - $pk->eid = 0; //Always use EntityID as zero for the actual player - $this->dataPacket($pk->setChannel(Network::CHANNEL_PRIORITY)); - - $pk = new SetTimePacket(); - $pk->time = $this->level->getTime(); - $pk->started = $this->level->stopTime == false; - $this->dataPacket($pk->setChannel(Network::CHANNEL_PRIORITY)); - - $pk = new SetSpawnPositionPacket(); - $pk->x = (int) $spawnPosition->x; - $pk->y = (int) $spawnPosition->y; - $pk->z = (int) $spawnPosition->z; - $this->dataPacket($pk->setChannel(Network::CHANNEL_PRIORITY)); - - $pk = new SetHealthPacket(); - $pk->health = $this->getHealth(); - $this->dataPacket($pk->setChannel(Network::CHANNEL_PRIORITY)); - - $pk = new SetDifficultyPacket(); - $pk->difficulty = $this->server->getDifficulty(); - $this->dataPacket($pk->setChannel(Network::CHANNEL_PRIORITY)); - - $this->server->getLogger()->info($this->getServer()->getLanguage()->translateString("pocketmine.player.logIn", [ - TextFormat::AQUA . $this->username . TextFormat::WHITE, - $this->ip, - $this->port, - $this->id, - $this->level->getName(), - round($this->x, 4), - round($this->y, 4), - round($this->z, 4) - ])); - - if($this->isOp()){ - $this->setRemoveFormat(false); - } - - if($this->gamemode === Player::SPECTATOR){ - $pk = new ContainerSetContentPacket(); - $pk->windowid = ContainerSetContentPacket::SPECIAL_CREATIVE; - $this->dataPacket($pk->setChannel(Network::CHANNEL_PRIORITY)); - }else{ - $pk = new ContainerSetContentPacket(); - $pk->windowid = ContainerSetContentPacket::SPECIAL_CREATIVE; - $pk->slots = Item::getCreativeItems(); - $this->dataPacket($pk->setChannel(Network::CHANNEL_PRIORITY)); - } - - $this->forceMovement = $this->teleportPosition = $this->getPosition(); - - $this->server->onPlayerLogin($this); + $this->onPlayerPreLogin(); break; case ProtocolInfo::MOVE_PLAYER_PACKET: @@ -1871,7 +1894,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } break; - case ProtocolInfo::PLAYER_EQUIPMENT_PACKET: + case ProtocolInfo::MOB_EQUIPMENT_PACKET: if($this->spawned === false or !$this->isAlive()){ break; } @@ -1882,8 +1905,11 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $packet->slot -= 9; //Get real block slot } + /** @var Item $item */ + $item = null; + if($this->isCreative()){ //Creative mode match - $item = Item::get($packet->item, $packet->meta, 1); + $item = $packet->item; $slot = Item::getCreativeItemIndex($item); }else{ $item = $this->inventory->getItem($packet->slot); @@ -1914,7 +1940,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade break; } } - }elseif(!isset($item) or $slot === -1 or $item->getId() !== $packet->item or $item->getDamage() !== $packet->meta){ // packet error or not implemented + }elseif($item === null or $slot === -1 or !$item->deepEquals($packet->item)){ // packet error or not implemented $this->inventory->sendContents($this); break; }elseif($this->isCreative()){ @@ -1944,8 +1970,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->craftingType = 0; - $packet->eid = $this->id; - if($packet->face >= 0 and $packet->face <= 5){ //Use Block, place $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); @@ -1956,15 +1980,15 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if($this->level->useItemOn($blockVector, $item, $packet->face, $packet->fx, $packet->fy, $packet->fz, $this) === true){ break; } - }elseif($this->inventory->getItemInHand()->getId() !== $packet->item or (($damage = $this->inventory->getItemInHand()->getDamage()) !== $packet->meta and $damage !== null)){ + }elseif(!$this->inventory->getItemInHand()->deepEquals($packet->item)){ $this->inventory->sendHeldItem($this); }else{ $item = $this->inventory->getItemInHand(); $oldItem = clone $item; //TODO: Implement adventure mode checks if($this->level->useItemOn($blockVector, $item, $packet->face, $packet->fx, $packet->fy, $packet->fz, $this)){ - if(!$item->equals($oldItem, true) or $item->getCount() !== $oldItem->getCount()){ - $this->inventory->setItemInHand($item, $this); + if(!$item->deepEquals($oldItem) or $item->getCount() !== $oldItem->getCount()){ + $this->inventory->setItemInHand($item); $this->inventory->sendHeldItem($this->hasSpawned); } break; @@ -1986,7 +2010,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if($this->isCreative()){ $item = $this->inventory->getItemInHand(); - }elseif($this->inventory->getItemInHand()->getId() !== $packet->item or (($damage = $this->inventory->getItemInHand()->getDamage()) !== $packet->meta and $damage !== null)){ + }elseif(!$this->inventory->getItemInHand()->deepEquals($packet->item)){ $this->inventory->sendHeldItem($this); break; }else{ @@ -2045,17 +2069,16 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } break; case ProtocolInfo::PLAYER_ACTION_PACKET: - if($this->spawned === false or $this->blocked === true or (!$this->isAlive() and $packet->action !== 7)){ + if($this->spawned === false or $this->blocked === true or (!$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN and $packet->action !== PlayerActionPacket::ACTION_DIMENSION_CHANGE)){ break; } - $this->craftingType = 0; $packet->eid = $this->id; $pos = new Vector3($packet->x, $packet->y, $packet->z); switch($packet->action){ - case 0: //Start break - if($pos->distanceSquared($this) > 10000){ + case PlayerActionPacket::ACTION_START_BREAK: + if($this->lastBreak !== PHP_INT_MAX or $pos->distanceSquared($this) > 10000){ break; } $target = $this->level->getBlock($pos); @@ -2067,7 +2090,10 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } $this->lastBreak = microtime(true); break; - case 5: //Shot arrow + case PlayerActionPacket::ACTION_ABORT_BREAK: + $this->lastBreak = PHP_INT_MAX; + break; + case PlayerActionPacket::ACTION_RELEASE_ITEM: if($this->startAction > -1 and $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION)){ if($this->inventory->getItemInHand()->getId() === Item::BOW) { $bow = $this->inventory->getItemInHand(); @@ -2143,7 +2169,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk = new EntityEventPacket(); $pk->eid = $this->getId(); $pk->event = EntityEventPacket::USE_ITEM; - $pk->setChannel(Network::CHANNEL_WORLD_EVENTS); $this->dataPacket($pk); Server::broadcastPacket($this->getViewers(), $pk); @@ -2159,10 +2184,10 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->inventory->sendContents($this); } break; - case 6: //get out of the bed + case PlayerActionPacket::ACTION_STOP_SLEEPING: $this->stopSleep(); break; - case 7: //Respawn + case PlayerActionPacket::ACTION_RESPAWN: if($this->spawned === false or $this->isAlive() or !$this->isOnline()){ break; } @@ -2178,6 +2203,9 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->teleport($ev->getRespawnPosition()); + $this->setSprinting(false); + $this->setSneaking(false); + $this->extinguish(); $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 300); $this->deadTicks = 0; @@ -2197,6 +2225,42 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->spawnToAll(); $this->scheduleUpdate(); break; + case PlayerActionPacket::ACTION_START_SPRINT: + $ev = new PlayerToggleSprintEvent($this, true); + $this->server->getPluginManager()->callEvent($ev); + if($ev->isCancelled()){ + $this->sendData($this); + }else{ + $this->setSprinting(true); + } + break; + case PlayerActionPacket::ACTION_STOP_SPRINT: + $ev = new PlayerToggleSprintEvent($this, false); + $this->server->getPluginManager()->callEvent($ev); + if($ev->isCancelled()){ + $this->sendData($this); + }else{ + $this->setSprinting(false); + } + break; + case PlayerActionPacket::ACTION_START_SNEAK: + $ev = new PlayerToggleSneakEvent($this, true); + $this->server->getPluginManager()->callEvent($ev); + if($ev->isCancelled()){ + $this->sendData($this); + }else{ + $this->setSneaking(true); + } + break; + case PlayerActionPacket::ACTION_STOP_SNEAK: + $ev = new PlayerToggleSneakEvent($this, false); + $this->server->getPluginManager()->callEvent($ev); + if($ev->isCancelled()){ + $this->sendData($this); + }else{ + $this->setSneaking(false); + } + break; } $this->startAction = -1; @@ -2222,8 +2286,8 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if($this->canInteract($vector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 6) and $this->level->useBreakOn($vector, $item, $this)){ if($this->isSurvival()){ - if(!$item->equals($oldItem, true) or $item->getCount() !== $oldItem->getCount()){ - $this->inventory->setItemInHand($item, $this); + if(!$item->deepEquals($oldItem) or $item->getCount() !== $oldItem->getCount()){ + $this->inventory->setItemInHand($item); $this->inventory->sendHeldItem($this->hasSpawned); } } @@ -2243,7 +2307,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } break; - case ProtocolInfo::PLAYER_ARMOR_EQUIPMENT_PACKET: + case ProtocolInfo::MOB_ARMOR_EQUIPMENT_PACKET: break; case ProtocolInfo::INTERACT_PACKET: @@ -2360,9 +2424,9 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if($item->isTool() and $this->isSurvival()){ if($item->useOn($target) and $item->getDamage() >= $item->getMaxDurability()){ - $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1), $this); + $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1)); }else{ - $this->inventory->setItemInHand($item, $this); + $this->inventory->setItemInHand($item); } } } @@ -2382,7 +2446,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk = new AnimatePacket(); $pk->eid = $this->getId(); $pk->action = $ev->getAnimationType(); - Server::broadcastPacket($this->getViewers(), $pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + Server::broadcastPacket($this->getViewers(), $pk); break; case ProtocolInfo::SET_HEALTH_PACKET: //Not used break; @@ -2436,7 +2500,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk = new EntityEventPacket(); $pk->eid = $this->getId(); $pk->event = EntityEventPacket::USE_ITEM; - $pk->setChannel(Network::CHANNEL_WORLD_EVENTS); $this->dataPacket($pk); Server::broadcastPacket($this->getViewers(), $pk); @@ -2448,7 +2511,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->heal($ev->getAmount(), $ev); --$slot->count; - $this->inventory->setItemInHand($slot, $this); + $this->inventory->setItemInHand($slot); if($slot->getId() === Item::MUSHROOM_STEW or $slot->getId() === Item::BEETROOT_SOUP){ $this->inventory->addItem(Item::get(Item::BOWL, 0, 1)); }elseif($slot->getId() === Item::RAW_FISH and $slot->getDamage() === 3){ //Pufferfish @@ -2464,7 +2527,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if($this->spawned === false or $this->blocked === true or !$this->isAlive()){ break; } - $packet->eid = $this->id; $item = $this->inventory->getItemInHand(); $ev = new PlayerDropItemEvent($this, $item); $this->server->getPluginManager()->callEvent($ev); @@ -2473,7 +2535,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade break; } - $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1), $this); + $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1)); $motion = $this->getDirectionVector()->multiply(0.4); $this->level->dropItem($this->add(0, 1.3, 0), $item, $motion, 40); @@ -2527,136 +2589,183 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } break; - case ProtocolInfo::CONTAINER_SET_CONTENT_PACKET: - if($packet->windowid === ContainerSetContentPacket::SPECIAL_CRAFTING){ - if(count($packet->slots) < 9){ - $this->inventory->sendContents($this); - break; + case ProtocolInfo::CRAFTING_EVENT_PACKET: + if($this->spawned === false or !$this->isAlive()){ + break; + }elseif(!isset($this->windowIndex[$packet->windowId])){ + $this->inventory->sendContents($this); + $pk = new ContainerClosePacket(); + $pk->windowid = $packet->windowId; + $this->dataPacket($pk); + break; + } + + $recipe = $this->server->getCraftingManager()->getRecipe($packet->id); + + if($recipe === null or (($recipe instanceof BigShapelessRecipe or $recipe instanceof BigShapedRecipe) and $this->craftingType === 0)){ + $this->inventory->sendContents($this); + break; + } + + foreach($packet->input as $i => $item){ + if($item->getDamage() === -1 or $item->getDamage() === 0xffff){ + $item->setDamage(null); } - foreach($packet->slots as $i => $item){ - /** @var Item $item */ - if($item->getDamage() === -1 or $item->getDamage() === 0xffff){ - $item->setDamage(null); - } - - if($i < 9 and $item->getId() > 0){ - $item->setCount(1); - } + if($i < 9 and $item->getId() > 0){ + $item->setCount(1); } + } - $result = $packet->slots[9]; + $canCraft = true; - if($this->craftingType === 1 or $this->craftingType === 2){ - $recipe = new BigShapelessRecipe($result); - }else{ - $recipe = new ShapelessRecipe($result); - } - /** @var Item[] $ingredients */ - $ingredients = []; - for($x = 0; $x < 3; ++$x){ + if($recipe instanceof ShapedRecipe){ + for($x = 0; $x < 3 and $canCraft; ++$x){ for($y = 0; $y < 3; ++$y){ - $item = $packet->slots[$x * 3 + $y]; - if($item->getCount() > 0 and $item->getId() > 0){ - //TODO shaped - $recipe->addIngredient($item); - $ingredients[$x * 3 + $y] = $item; + $item = $packet->input[$y * 3 + $x]; + $ingredient = $recipe->getIngredient($x, $y); + if($item->getCount() > 0){ + if($ingredient === null or !$ingredient->deepEquals($item, $ingredient->getDamage() !== null, $ingredient->getCompoundTag() !== null)){ + $canCraft = false; + break; + } } } } + }elseif($recipe instanceof ShapelessRecipe){ + $needed = $recipe->getIngredientList(); - if(!Server::getInstance()->getCraftingManager()->matchRecipe($recipe)){ - $this->server->getLogger()->debug("Unmatched recipe from player ". $this->getName() .": " . $recipe->getResult().", using: " . implode(", ", $recipe->getIngredientList())); - $this->inventory->sendContents($this); - break; - } + for($x = 0; $x < 3 and $canCraft; ++$x){ + for($y = 0; $y < 3; ++$y){ + $item = clone $packet->input[$y * 3 + $x]; - $canCraft = true; + foreach($needed as $k => $n){ + if($n->deepEquals($item, $n->getDamage() !== null, $n->getCompoundTag() !== null)){ + $remove = min($n->getCount(), $item->getCount()); + $n->setCount($n->getCount() - $remove); + $item->setCount($item->getCount() - $remove); - $used = array_fill(0, $this->inventory->getSize(), 0); + if($n->getCount() === 0){ + unset($needed[$k]); + } + } + } - foreach($ingredients as $ingredient){ - $slot = -1; - $checkDamage = $ingredient->getDamage() === null ? false : true; - foreach($this->inventory->getContents() as $index => $i){ - if($ingredient->equals($i, $checkDamage) and ($i->getCount() - $used[$index]) >= 1){ - $slot = $index; - $used[$index]++; + if($item->getCount() > 0){ + $canCraft = false; break; } } + } - if($slot === -1){ - $canCraft = false; + if(count($needed) > 0){ + $canCraft = false; + } + }else{ + $canCraft = false; + } + + /** @var Item[] $ingredients */ + $ingredients = $packet->input; + $result = $packet->output[0]; + + if(!$canCraft or !$recipe->getResult()->deepEquals($result)){ + $this->server->getLogger()->debug("Unmatched recipe ". $recipe->getId() ." from player ". $this->getName() .": expected " . $recipe->getResult() . ", got ". $result .", using: " . implode(", ", $ingredients)); + $this->inventory->sendContents($this); + break; + } + + $used = array_fill(0, $this->inventory->getSize(), 0); + + foreach($ingredients as $ingredient){ + $slot = -1; + foreach($this->inventory->getContents() as $index => $i){ + if($ingredient->getId() !== 0 and $ingredient->deepEquals($i, $i->getDamage() !== null) and ($i->getCount() - $used[$index]) >= 1){ + $slot = $index; + $used[$index]++; break; } } - if(!$canCraft){ - $this->inventory->sendContents($this); + if($ingredient->getId() !== 0 and $slot === -1){ + $canCraft = false; break; } + } - foreach($used as $slot => $count){ - if($count === 0){ - continue; - } + if(!$canCraft){ + $this->server->getLogger()->debug("Unmatched recipe ". $recipe->getId() ." from player ". $this->getName() .": client does not have enough items, using: " . implode(", ", $ingredients)); + $this->inventory->sendContents($this); + break; + } - $item = $this->inventory->getItem($slot); + $this->server->getPluginManager()->callEvent($ev = new CraftItemEvent($this, $ingredients, $recipe)); - if($item->getCount() > $count){ - $newItem = clone $item; - $newItem->setCount($item->getCount() - $count); - }else{ - $newItem = Item::get(Item::AIR, 0, 0); - } + if($ev->isCancelled()){ + $this->inventory->sendContents($this); + break; + } - $this->inventory->setItem($slot, $newItem); + foreach($used as $slot => $count){ + if($count === 0){ + continue; } - $extraItem = $this->inventory->addItem($recipe->getResult()); - if(count($extraItem) > 0){ - foreach($extraItem as $item){ - $this->level->dropItem($this, $item); - } + $item = $this->inventory->getItem($slot); + + if($item->getCount() > $count){ + $newItem = clone $item; + $newItem->setCount($item->getCount() - $count); + }else{ + $newItem = Item::get(Item::AIR, 0, 0); } - switch($recipe->getResult()->getId()){ - case Item::WORKBENCH: - $this->awardAchievement("buildWorkBench"); - break; - case Item::WOODEN_PICKAXE: - $this->awardAchievement("buildPickaxe"); - break; - case Item::FURNACE: - $this->awardAchievement("buildFurnace"); - break; - case Item::WOODEN_HOE: - $this->awardAchievement("buildHoe"); - break; - case Item::BREAD: - $this->awardAchievement("makeBread"); - break; - case Item::CAKE: - //TODO: detect complex recipes like cake that leave remains - $this->awardAchievement("bakeCake"); - $this->inventory->addItem(Item::get(Item::BUCKET, 0, 3)); - break; - case Item::STONE_PICKAXE: - case Item::GOLD_PICKAXE: - case Item::IRON_PICKAXE: - case Item::DIAMOND_PICKAXE: - $this->awardAchievement("buildBetterPickaxe"); - break; - case Item::WOODEN_SWORD: - $this->awardAchievement("buildSword"); - break; - case Item::DIAMOND: - $this->awardAchievement("diamond"); - break; + $this->inventory->setItem($slot, $newItem); + } + + $extraItem = $this->inventory->addItem($recipe->getResult()); + if(count($extraItem) > 0){ + foreach($extraItem as $item){ + $this->level->dropItem($this, $item); } } + + switch($recipe->getResult()->getId()){ + case Item::WORKBENCH: + $this->awardAchievement("buildWorkBench"); + break; + case Item::WOODEN_PICKAXE: + $this->awardAchievement("buildPickaxe"); + break; + case Item::FURNACE: + $this->awardAchievement("buildFurnace"); + break; + case Item::WOODEN_HOE: + $this->awardAchievement("buildHoe"); + break; + case Item::BREAD: + $this->awardAchievement("makeBread"); + break; + case Item::CAKE: + //TODO: detect complex recipes like cake that leave remains + $this->awardAchievement("bakeCake"); + $this->inventory->addItem(Item::get(Item::BUCKET, 0, 3)); + break; + case Item::STONE_PICKAXE: + case Item::GOLD_PICKAXE: + case Item::IRON_PICKAXE: + case Item::DIAMOND_PICKAXE: + $this->awardAchievement("buildBetterPickaxe"); + break; + case Item::WOODEN_SWORD: + $this->awardAchievement("buildSword"); + break; + case Item::DIAMOND: + $this->awardAchievement("diamond"); + break; + } + break; case ProtocolInfo::CONTAINER_SET_SLOT_PACKET: @@ -2693,7 +2802,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade break; } - if($transaction->getSourceItem()->equals($transaction->getTargetItem(), true) and $transaction->getTargetItem()->getCount() === $transaction->getSourceItem()->getCount()){ //No changes! + if($transaction->getSourceItem()->deepEquals($transaction->getTargetItem()) and $transaction->getTargetItem()->getCount() === $transaction->getSourceItem()->getCount()){ //No changes! //No changes, just a local inventory update sent by the server break; } @@ -2738,7 +2847,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } break; - case ProtocolInfo::TILE_ENTITY_DATA_PACKET: + case ProtocolInfo::BLOCK_ENTITY_DATA_PACKET: if($this->spawned === false or $this->blocked === true or !$this->isAlive()){ break; } @@ -2761,7 +2870,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade TextFormat::clean($nbt["Text1"], $this->removeFormat), TextFormat::clean($nbt["Text2"], $this->removeFormat), TextFormat::clean($nbt["Text3"], $this->removeFormat), TextFormat::clean($nbt["Text4"], $this->removeFormat) ]); - if(!isset($t->namedtag->Creator) or $t->namedtag["Creator"] !== $this->getUniqueId()){ + if(!isset($t->namedtag->Creator) or $t->namedtag["Creator"] !== $this->getRawUniqueId()){ $ev->setCancelled(); }else{ foreach($ev->getLines() as $line){ @@ -2837,7 +2946,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk = new TextPacket(); $pk->type = TextPacket::TYPE_RAW; $pk->message = $m; - $this->dataPacket($pk->setChannel(Network::CHANNEL_TEXT)); + $this->dataPacket($pk); } } } @@ -2855,21 +2964,22 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk->type = TextPacket::TYPE_RAW; $pk->message = $this->server->getLanguage()->translateString($message, $parameters); } - $this->dataPacket($pk->setChannel(Network::CHANNEL_TEXT)); + $this->dataPacket($pk); } - - public function sendPopup($message){ + + public function sendPopup($message, $subtitle = ""){ $pk = new TextPacket(); $pk->type = TextPacket::TYPE_POPUP; - $pk->message = $message; - $this->dataPacket($pk->setChannel(Network::CHANNEL_TEXT)); + $pk->source = $message; + $pk->message = $subtitle; + $this->dataPacket($pk); } public function sendTip($message){ $pk = new TextPacket(); $pk->type = TextPacket::TYPE_TIP; $pk->message = $message; - $this->dataPacket($pk->setChannel(Network::CHANNEL_TEXT)); + $this->dataPacket($pk); } /** @@ -2886,9 +2996,9 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if($notify and strlen((string) $reason) > 0){ $pk = new DisconnectPacket; $pk->message = $reason; - $this->directDataPacket($pk->setChannel(Network::CHANNEL_PRIORITY)); + $this->directDataPacket($pk); } - + $this->connected = false; if(strlen($this->getName()) > 0){ $this->server->getPluginManager()->callEvent($ev = new PlayerQuitEvent($this, $message, true)); @@ -2918,6 +3028,10 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->interface->close($this, $notify ? $reason : ""); + if($this->loggedIn){ + $this->server->removeOnlinePlayer($this); + } + $this->loggedIn = false; if(isset($ev) and $this->username != "" and $this->spawned !== false and $ev->getQuitMessage() != ""){ @@ -3008,7 +3122,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $message = "death.attack.generic"; $params = [ - $this->getName() + $this->getDisplayName() ]; $cause = $this->getLastDamageCause(); @@ -3019,11 +3133,11 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $e = $cause->getDamager(); if($e instanceof Player){ $message = "death.attack.player"; - $params[] = $e->getName(); + $params[] = $e->getDisplayName(); break; }elseif($e instanceof Living){ $message = "death.attack.mob"; - $params[] = $e->getName(); + $params[] = $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName(); break; }else{ $params[] = "Unknown"; @@ -3033,9 +3147,12 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade case EntityDamageEvent::CAUSE_PROJECTILE: if($cause instanceof EntityDamageByEntityEvent){ $e = $cause->getDamager(); - if($e instanceof Living){ + if($e instanceof Player){ $message = "death.attack.arrow"; - $params[] = $e->getName(); + $params[] = $e->getDisplayName(); + }elseif($e instanceof Living){ + $message = "death.attack.arrow"; + $params[] = $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName(); break; }else{ $params[] = "Unknown"; @@ -3090,9 +3207,13 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade case EntityDamageEvent::CAUSE_ENTITY_EXPLOSION: if($cause instanceof EntityDamageByEntityEvent){ $e = $cause->getDamager(); - if($e instanceof Living){ + if($e instanceof Player){ $message = "death.attack.explosion.player"; - $params[] = $e->getName(); + $params[] = $e->getDisplayName(); + }elseif($e instanceof Living){ + $message = "death.attack.explosion.player"; + $params[] = $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName(); + break; } }else{ $message = "death.attack.explosion"; @@ -3134,7 +3255,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk->x = $pos->x; $pk->y = $pos->y; $pk->z = $pos->z; - $this->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $this->dataPacket($pk); } public function setHealth($amount){ @@ -3142,7 +3263,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if($this->spawned === true){ $pk = new SetHealthPacket(); $pk->health = $this->getHealth(); - $this->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $this->dataPacket($pk); } } @@ -3169,11 +3290,11 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk = new EntityEventPacket(); $pk->eid = 0; $pk->event = EntityEventPacket::HURT_ANIMATION; - $this->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $this->dataPacket($pk); } } - public function sendPosition(Vector3 $pos, $yaw = null, $pitch = null, $mode = 0, $channel = Network::CHANNEL_PRIORITY, array $targets = null){ + public function sendPosition(Vector3 $pos, $yaw = null, $pitch = null, $mode = 0, array $targets = null){ $yaw = $yaw === null ? $this->yaw : $yaw; $pitch = $pitch === null ? $this->pitch : $pitch; @@ -3188,10 +3309,10 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk->mode = $mode; if($targets !== null){ - Server::broadcastPacket($targets, $pk->setChannel($channel)); + Server::broadcastPacket($targets, $pk); }else{ $pk->eid = 0; - $this->dataPacket($pk->setChannel($channel)); + $this->dataPacket($pk); } } @@ -3217,12 +3338,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } } - if($this->chunk !== null and $this->spawned){ - //TODO HACK: Minecraft: PE does not like moving a player from old chunks. - //Player entities get stuck in unloaded chunks and the client does not accept position updates. - $this->sendPosition($this, null, null, MovePlayerPacket::MODE_RESET, Network::CHANNEL_MOVEMENT, $reload); - } - foreach($newChunk as $player){ $this->spawnTo($player); } @@ -3249,7 +3364,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } } - $this->sendPosition($this, null, null, 1, Network::CHANNEL_WORLD_CHUNKS); + $this->sendPosition($this, null, null, 1); $this->spawnToAll(); $this->forceMovement = $this->teleportPosition; $this->teleportPosition = null; @@ -3309,7 +3424,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } $this->forceMovement = new Vector3($this->x, $this->y, $this->z); - $this->sendPosition($this, $this->yaw, $this->pitch, 1, Network::CHANNEL_WORLD_EVENTS); + $this->sendPosition($this, $this->yaw, $this->pitch, 1); $this->resetFallDistance(); @@ -3423,17 +3538,17 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade * * @return DataPacket */ - public static function getChunkCacheFromData($chunkX, $chunkZ, $payload){ + public static function getChunkCacheFromData($chunkX, $chunkZ, $payload, $ordering = FullChunkDataPacket::ORDER_COLUMNS){ $pk = new FullChunkDataPacket(); $pk->chunkX = $chunkX; $pk->chunkZ = $chunkZ; + $pk->order = $ordering; $pk->data = $payload; $pk->encode(); $batch = new BatchPacket(); - $batch->payload = zlib_encode($pk->getBuffer(), ZLIB_ENCODING_DEFLATE, Server::getInstance()->networkCompressionLevel); + $batch->payload = zlib_encode(Binary::writeInt(strlen($pk->getBuffer())) . $pk->getBuffer(), ZLIB_ENCODING_DEFLATE, Server::getInstance()->networkCompressionLevel); - $batch->setChannel(Network::CHANNEL_WORLD_CHUNKS); $batch->encode(); $batch->isEncoded = true; return $batch; diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index 2e260c0ee..a4facf3dc 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -72,11 +72,11 @@ namespace pocketmine { use pocketmine\utils\Utils; use pocketmine\wizard\Installer; - const VERSION = "1.5dev"; - const API_VERSION = "1.12.0"; - const CODENAME = "活発(Kappatsu)フグ(Fugu)"; - const MINECRAFT_VERSION = "v0.11.0 alpha"; - const MINECRAFT_VERSION_NETWORK = "0.11.0"; + const VERSION = "1.6dev"; + const API_VERSION = "1.13.1"; + const CODENAME = "[REDACTED]"; + const MINECRAFT_VERSION = "v0.13.1 alpha"; + const MINECRAFT_VERSION_NETWORK = "0.13.1"; /* * Startup code. Do not look at it, it may harm you. @@ -355,10 +355,10 @@ namespace pocketmine { $args = $trace[$i]["params"]; } foreach($args as $name => $value){ - $params .= (is_object($value) ? get_class($value) . " " . (method_exists($value, "__toString") ? $value->__toString() : "object") : gettype($value) . " " . (is_array($value) ? "Array()" : @strval($value))) . ", "; + $params .= (is_object($value) ? get_class($value) . " " . (method_exists($value, "__toString") ? $value->__toString() : "object") : gettype($value) . " " . (is_array($value) ? "Array()" : Utils::printable(@strval($value)))) . ", "; } } - $messages[] = "#$j " . (isset($trace[$i]["file"]) ? cleanPath($trace[$i]["file"]) : "") . "(" . (isset($trace[$i]["line"]) ? $trace[$i]["line"] : "") . "): " . (isset($trace[$i]["class"]) ? $trace[$i]["class"] . (($trace[$i]["type"] === "dynamic" or $trace[$i]["type"] === "->") ? "->" : "::") : "") . $trace[$i]["function"] . "(" . substr($params, 0, -2) . ")"; + $messages[] = "#$j " . (isset($trace[$i]["file"]) ? cleanPath($trace[$i]["file"]) : "") . "(" . (isset($trace[$i]["line"]) ? $trace[$i]["line"] : "") . "): " . (isset($trace[$i]["class"]) ? $trace[$i]["class"] . (($trace[$i]["type"] === "dynamic" or $trace[$i]["type"] === "->") ? "->" : "::") : "") . $trace[$i]["function"] . "(" . Utils::printable(substr($params, 0, -2)) . ")"; } return $messages; diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index c50547258..93452e7c4 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -32,6 +32,7 @@ use pocketmine\command\ConsoleCommandSender; use pocketmine\command\PluginIdentifiableCommand; use pocketmine\command\SimpleCommandMap; use pocketmine\entity\Arrow; +use pocketmine\entity\Attribute; use pocketmine\entity\Effect; use pocketmine\entity\Entity; use pocketmine\entity\FallingSand; @@ -53,6 +54,9 @@ use pocketmine\event\TranslationContainer; use pocketmine\inventory\CraftingManager; use pocketmine\inventory\InventoryType; use pocketmine\inventory\Recipe; +use pocketmine\inventory\ShapedRecipe; +use pocketmine\inventory\ShapelessRecipe; +use pocketmine\item\enchantment\Enchantment; use pocketmine\item\Item; use pocketmine\lang\BaseLang; use pocketmine\level\format\anvil\Anvil; @@ -62,6 +66,7 @@ use pocketmine\level\format\mcregion\McRegion; use pocketmine\level\generator\biome\Biome; use pocketmine\level\generator\Flat; use pocketmine\level\generator\Generator; +use pocketmine\level\generator\hell\Nether; use pocketmine\level\generator\normal\Normal; use pocketmine\level\Level; use pocketmine\metadata\EntityMetadataStore; @@ -80,7 +85,9 @@ use pocketmine\nbt\tag\String; use pocketmine\network\CompressBatchedTask; use pocketmine\network\Network; use pocketmine\network\protocol\BatchPacket; +use pocketmine\network\protocol\CraftingDataPacket; use pocketmine\network\protocol\DataPacket; +use pocketmine\network\protocol\PlayerListPacket; use pocketmine\network\query\QueryHandler; use pocketmine\network\RakLibInterface; use pocketmine\network\rcon\RCON; @@ -97,6 +104,7 @@ use pocketmine\scheduler\FileWriteTask; use pocketmine\scheduler\SendUsageTask; use pocketmine\scheduler\ServerScheduler; use pocketmine\tile\Chest; +use pocketmine\tile\EnchantTable; use pocketmine\tile\Furnace; use pocketmine\tile\Sign; use pocketmine\tile\Tile; @@ -111,6 +119,7 @@ use pocketmine\utils\Terminal; use pocketmine\utils\TextFormat; use pocketmine\utils\TextWrapper; use pocketmine\utils\Utils; +use pocketmine\utils\UUID; use pocketmine\utils\VersionString; /** @@ -249,6 +258,9 @@ class Server{ /** @var Player[] */ private $players = []; + /** @var Player[] */ + private $playerList = []; + private $identifiers = []; /** @var Level[] */ @@ -705,7 +717,7 @@ class Server{ * @return Player[] */ public function getOnlinePlayers(){ - return $this->players; + return $this->playerList; } public function addRecipe(Recipe $recipe){ @@ -1085,99 +1097,6 @@ class Server{ $level->setTickRate($this->baseTickRate); - /*foreach($entities->getAll() as $entity){ - if(!isset($entity["id"])){ - break; - } - if($entity["id"] === 64){ //Item Drop - $e = $this->server->api->entity->add($this->levels[$name], ENTITY_ITEM, $entity["Item"]["id"], array( - "meta" => $entity["Item"]["Damage"], - "stack" => $entity["Item"]["Count"], - "x" => $entity["Pos"][0], - "y" => $entity["Pos"][1], - "z" => $entity["Pos"][2], - "yaw" => $entity["Rotation"][0], - "pitch" => $entity["Rotation"][1], - )); - }elseif($entity["id"] === FALLING_SAND){ - $e = $this->server->api->entity->add($this->levels[$name], ENTITY_FALLING, $entity["id"], $entity); - $e->setPosition(new Vector3($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2]), $entity["Rotation"][0], $entity["Rotation"][1]); - $e->setHealth($entity["Health"]); - }elseif($entity["id"] === OBJECT_PAINTING or $entity["id"] === OBJECT_ARROW){ //Painting - $e = $this->server->api->entity->add($this->levels[$name], ENTITY_OBJECT, $entity["id"], $entity); - $e->setPosition(new Vector3($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2]), $entity["Rotation"][0], $entity["Rotation"][1]); - $e->setHealth(1); - }else{ - $e = $this->server->api->entity->add($this->levels[$name], ENTITY_MOB, $entity["id"], $entity); - $e->setPosition(new Vector3($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2]), $entity["Rotation"][0], $entity["Rotation"][1]); - $e->setHealth($entity["Health"]); - } - }*/ - - /*if(file_exists($path . "tiles.yml")){ - $tiles = new Config($path . "tiles.yml", Config::YAML); - foreach($tiles->getAll() as $tile){ - if(!isset($tile["id"])){ - continue; - } - $level->loadChunk($tile["x"] >> 4, $tile["z"] >> 4); - - $nbt = new Compound(false, []); - foreach($tile as $index => $data){ - switch($index){ - case "Items": - $tag = new Enum("Items", []); - $tag->setTagType(NBT::TAG_Compound); - foreach($data as $slot => $fields){ - $tag[(int) $slot] = new Compound(false, array( - "Count" => new Byte("Count", $fields["Count"]), - "Slot" => new Short("Slot", $fields["Slot"]), - "Damage" => new Short("Damage", $fields["Damage"]), - "id" => new String("id", $fields["id"]) - )); - } - $nbt["Items"] = $tag; - break; - - case "id": - case "Text1": - case "Text2": - case "Text3": - case "Text4": - $nbt[$index] = new String($index, $data); - break; - - case "x": - case "y": - case "z": - case "pairx": - case "pairz": - $nbt[$index] = new Int($index, $data); - break; - - case "BurnTime": - case "CookTime": - case "MaxTime": - $nbt[$index] = new Short($index, $data); - break; - } - } - switch($tile["id"]){ - case Tile::FURNACE: - new Furnace($level, $nbt); - break; - case Tile::CHEST: - new Chest($level, $nbt); - break; - case Tile::SIGN: - new Sign($level, $nbt); - break; - } - } - unlink($path . "tiles.yml"); - $level->save(true, true); - }*/ - return true; } @@ -1691,6 +1610,8 @@ class Server{ Item::init(); Biome::init(); Effect::init(); + Enchantment::init(); + Attribute::init(); /** TODO: @deprecated */ TextWrapper::init(); $this->craftingManager = new CraftingManager(); @@ -1726,6 +1647,8 @@ class Server{ Generator::addGenerator(Flat::class, "flat"); Generator::addGenerator(Normal::class, "normal"); Generator::addGenerator(Normal::class, "default"); + Generator::addGenerator(Nether::class, "hell"); + Generator::addGenerator(Nether::class, "nether"); foreach((array) $this->getProperty("worlds", []) as $name => $worldSetting){ if($this->loadLevel($name) === false){ @@ -1912,9 +1835,9 @@ class Server{ if(!$p->isEncoded){ $p->encode(); } - $str .= $p->buffer; + $str .= Binary::writeInt(strlen($p->buffer)) . $p->buffer; }else{ - $str .= $p; + $str .= Binary::writeInt(strlen($p)) . $p; } } @@ -1929,15 +1852,14 @@ class Server{ $task = new CompressBatchedTask($str, $targets, $this->networkCompressionLevel, $channel); $this->getScheduler()->scheduleAsyncTask($task); }else{ - $this->broadcastPacketsCallback(zlib_encode($str, ZLIB_ENCODING_DEFLATE, $this->networkCompressionLevel), $targets, $channel); + $this->broadcastPacketsCallback(zlib_encode($str, ZLIB_ENCODING_DEFLATE, $this->networkCompressionLevel), $targets); } Timings::$playerNetworkTimer->stopTiming(); } - public function broadcastPacketsCallback($data, array $identifiers, $channel = 0){ + public function broadcastPacketsCallback($data, array $identifiers){ $pk = new BatchPacket(); - $pk->setChannel($channel); $pk->payload = $data; $pk->encode(); $pk->isEncoded = true; @@ -2311,8 +2233,11 @@ class Server{ public function onPlayerLogin(Player $player){ if($this->sendUsageTicker > 0){ - $this->uniquePlayers[$player->getUniqueId()] = $player->getUniqueId(); + $this->uniquePlayers[$player->getRawUniqueId()] = $player->getRawUniqueId(); } + + $this->sendFullPlayerListData($player); + $this->sendRecipeList($player); } public function addPlayer($identifier, Player $player){ @@ -2320,6 +2245,66 @@ class Server{ $this->identifiers[spl_object_hash($player)] = $identifier; } + public function addOnlinePlayer(Player $player){ + $this->playerList[$player->getRawUniqueId()] = $player; + + $this->updatePlayerListData($player->getUniqueId(), $player->getId(), $player->getDisplayName(), $player->getSkinName(), $player->getSkinData()); + } + + public function removeOnlinePlayer(Player $player){ + if(isset($this->playerList[$player->getRawUniqueId()])){ + unset($this->playerList[$player->getRawUniqueId()]); + + $pk = new PlayerListPacket(); + $pk->type = PlayerListPacket::TYPE_REMOVE; + $pk->entries[] = [$player->getUniqueId()]; + Server::broadcastPacket($this->playerList, $pk); + } + } + + public function updatePlayerListData(UUID $uuid, $entityId, $name, $skinName, $skinData, array $players = null){ + $pk = new PlayerListPacket(); + $pk->type = PlayerListPacket::TYPE_ADD; + $pk->entries[] = [$uuid, $entityId, $name, $skinName, $skinData]; + Server::broadcastPacket($players === null ? $this->playerList : $players, $pk); + } + + public function removePlayerListData(UUID $uuid, array $players = null){ + $pk = new PlayerListPacket(); + $pk->type = PlayerListPacket::TYPE_REMOVE; + $pk->entries[] = [$uuid]; + Server::broadcastPacket($players === null ? $this->playerList : $players, $pk); + } + + public function sendFullPlayerListData(Player $p){ + $pk = new PlayerListPacket(); + $pk->type = PlayerListPacket::TYPE_ADD; + foreach($this->playerList as $player){ + $pk->entries[] = [$player->getUniqueId(), $player->getId(), $player->getDisplayName(), $player->getSkinName(), $player->getSkinData()]; + } + + $p->dataPacket($pk); + } + + public function sendRecipeList(Player $p){ + $pk = new CraftingDataPacket(); + $pk->cleanRecipes = true; + + foreach($this->getCraftingManager()->getRecipes() as $recipe){ + if($recipe instanceof ShapedRecipe){ + $pk->addShapedRecipe($recipe); + }elseif($recipe instanceof ShapelessRecipe){ + $pk->addShapelessRecipe($recipe); + } + } + + foreach($this->getCraftingManager()->getFurnaceRecipes() as $recipe){ + $pk->addFurnaceRecipe($recipe); + } + + $p->dataPacket($pk); + } + private function checkTickUpdates($currentTick, $tickTime){ foreach($this->players as $p){ if(!$p->loggedIn and ($tickTime - $p->creationTime) >= 10){ @@ -2370,7 +2355,7 @@ class Server{ public function doAutoSave(){ if($this->getAutoSave()){ Timings::$worldSaveTimer->startTiming(); - foreach($this->getOnlinePlayers() as $index => $player){ + foreach($this->players as $index => $player){ if($player->isOnline()){ $player->save(true); }elseif(!$player->isConnected()){ @@ -2594,6 +2579,7 @@ class Server{ Tile::registerTile(Chest::class); Tile::registerTile(Furnace::class); Tile::registerTile(Sign::class); + Tile::registerTile(EnchantTable::class); } } diff --git a/src/pocketmine/block/Anvil.php b/src/pocketmine/block/Anvil.php new file mode 100644 index 000000000..73c2e6c0f --- /dev/null +++ b/src/pocketmine/block/Anvil.php @@ -0,0 +1,82 @@ +meta = $meta; + } + + public function canBeActivated(){ + return true; + } + + public function getHardness(){ + return 5; + } + + public function getResistance(){ + return 6000; + } + + public function getName(){ + return "Anvil"; + } + + public function getToolType(){ + return Tool::TYPE_PICKAXE; + } + + public function onActivate(Item $item, Player $player = null){ + if($player instanceof Player){ + if($player->isCreative()){ + return true; + } + + $player->addWindow(new AnvilInventory($this)); + } + + return true; + } + + public function getDrops(Item $item){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ + return [ + [$this->id, 0, 1], //TODO break level + ]; + }else{ + return []; + } + } +} \ No newline at end of file diff --git a/src/pocketmine/block/Block.php b/src/pocketmine/block/Block.php index a36c35256..2bfc61b7d 100644 --- a/src/pocketmine/block/Block.php +++ b/src/pocketmine/block/Block.php @@ -25,9 +25,8 @@ namespace pocketmine\block; use pocketmine\entity\Entity; -use pocketmine\entity\Squid; -use pocketmine\entity\Villager; -use pocketmine\entity\Zombie; + + use pocketmine\item\Item; use pocketmine\item\Tool; use pocketmine\level\Level; @@ -182,9 +181,13 @@ class Block extends Position implements Metadatable{ const LILY_PAD = 111; const NETHER_BRICKS = 112; const NETHER_BRICK_BLOCK = 112; - + const NETHER_BRICK_FENCE = 113; const NETHER_BRICKS_STAIRS = 114; + const ENCHANTING_TABLE = 116; + const ENCHANT_TABLE = 116; + const ENCHANTMENT_TABLE = 116; + const BREWING_STAND = 117; const END_PORTAL_FRAME = 120; const END_STONE = 121; @@ -202,10 +205,13 @@ class Block extends Position implements Metadatable{ const COBBLE_WALL = 139; const STONE_WALL = 139; const COBBLESTONE_WALL = 139; - + const FLOWER_POT_BLOCK = 140; const CARROT_BLOCK = 141; const POTATO_BLOCK = 142; + const ANVIL = 145; + const TRAPPED_CHEST = 146; + const REDSTONE_BLOCK = 152; const QUARTZ_BLOCK = 155; @@ -231,11 +237,12 @@ class Block extends Position implements Metadatable{ const DARK_OAK_WOOD_STAIRS = 164; const DARK_OAK_WOODEN_STAIRS = 164; + const IRON_TRAPDOOR = 167; const HAY_BALE = 170; const CARPET = 171; const HARDENED_CLAY = 172; const COAL_BLOCK = 173; - + const PACKED_ICE = 174; const DOUBLE_PLANT = 175; const FENCE_GATE_SPRUCE = 183; @@ -250,7 +257,6 @@ class Block extends Position implements Metadatable{ const BEETROOT_BLOCK = 244; const STONECUTTER = 245; const GLOWING_OBSIDIAN = 246; - const NETHER_REACTOR = 247; /** @var \SplFixedArray */ public static $list = null; @@ -408,9 +414,11 @@ class Block extends Position implements Metadatable{ self::$list[self::MYCELIUM] = Mycelium::class; self::$list[self::WATER_LILY] = WaterLily::class; self::$list[self::NETHER_BRICKS] = NetherBrick::class; - + self::$list[self::NETHER_BRICK_FENCE] = NetherBrickFence::class; self::$list[self::NETHER_BRICKS_STAIRS] = NetherBrickStairs::class; + self::$list[self::ENCHANTING_TABLE] = EnchantingTable::class; + self::$list[self::BREWING_STAND] = BrewingStand::class; self::$list[self::END_PORTAL_FRAME] = EndPortalFrame::class; self::$list[self::END_STONE] = EndStone::class; self::$list[self::SANDSTONE_STAIRS] = SandstoneStairs::class; @@ -421,10 +429,11 @@ class Block extends Position implements Metadatable{ self::$list[self::BIRCH_WOOD_STAIRS] = BirchWoodStairs::class; self::$list[self::JUNGLE_WOOD_STAIRS] = JungleWoodStairs::class; self::$list[self::STONE_WALL] = StoneWall::class; - + self::$list[self::FLOWER_POT_BLOCK] = FlowerPot::class; self::$list[self::CARROT_BLOCK] = Carrot::class; self::$list[self::POTATO_BLOCK] = Potato::class; - + self::$list[self::ANVIL] = Anvil::class; + self::$list[self::TRAPPED_CHEST] = TrappedChest::class; self::$list[self::REDSTONE_BLOCK] = Redstone::class; self::$list[self::QUARTZ_BLOCK] = Quartz::class; @@ -438,11 +447,12 @@ class Block extends Position implements Metadatable{ self::$list[self::ACACIA_WOOD_STAIRS] = AcaciaWoodStairs::class; self::$list[self::DARK_OAK_WOOD_STAIRS] = DarkOakWoodStairs::class; + self::$list[self::IRON_TRAPDOOR] = IronTrapdoor::class; self::$list[self::HAY_BALE] = HayBale::class; self::$list[self::CARPET] = Carpet::class; self::$list[self::HARDENED_CLAY] = HardenedClay::class; self::$list[self::COAL_BLOCK] = Coal::class; - + self::$list[self::PACKED_ICE] = PackedIce::class; self::$list[self::DOUBLE_PLANT] = DoublePlant::class; self::$list[self::FENCE_GATE_SPRUCE] = FenceGateSpruce::class; @@ -457,7 +467,6 @@ class Block extends Position implements Metadatable{ self::$list[self::BEETROOT_BLOCK] = Beetroot::class; self::$list[self::STONECUTTER] = Stonecutter::class; self::$list[self::GLOWING_OBSIDIAN] = GlowingObsidian::class; - self::$list[self::NETHER_REACTOR] = NetherReactor::class; foreach(self::$list as $id => $class){ if($class !== null){ @@ -788,6 +797,10 @@ class Block extends Position implements Metadatable{ $base *= 3.33; } + if($item->isSword()){ + $base *= 0.5; + } + return $base; } diff --git a/src/pocketmine/block/BrewingStand.php b/src/pocketmine/block/BrewingStand.php new file mode 100644 index 000000000..e0e7aaa9a --- /dev/null +++ b/src/pocketmine/block/BrewingStand.php @@ -0,0 +1,46 @@ +isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::BRICKS_BLOCK, 0, 1], ]; diff --git a/src/pocketmine/block/BurningFurnace.php b/src/pocketmine/block/BurningFurnace.php index 416c44005..ed12a40b6 100644 --- a/src/pocketmine/block/BurningFurnace.php +++ b/src/pocketmine/block/BurningFurnace.php @@ -77,6 +77,17 @@ class BurningFurnace extends Solid{ new Int("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); + + if($item->hasCustomName()){ + $nbt->CustomName = new String("CustomName", $item->getCustomName()); + } + + if($item->hasCustomBlockData()){ + foreach($item->getCustomBlockData() as $key => $v){ + $nbt->{$key} = $v; + } + } + Tile::createTile("Furnace", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; @@ -106,6 +117,12 @@ class BurningFurnace extends Solid{ $furnace = Tile::createTile("Furnace", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); } + if(isset($furnace->namedtag->Lock) and $furnace->namedtag->Lock instanceof String){ + if($furnace->namedtag->Lock->getValue() !== $item->getCustomName()){ + return true; + } + } + if($player->isCreative()){ return true; } @@ -118,7 +135,7 @@ class BurningFurnace extends Solid{ public function getDrops(Item $item){ $drops = []; - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ $drops[] = [Item::FURNACE, 0, 1]; } diff --git a/src/pocketmine/block/Cake.php b/src/pocketmine/block/Cake.php index 42ded7a0f..7f0187f51 100644 --- a/src/pocketmine/block/Cake.php +++ b/src/pocketmine/block/Cake.php @@ -26,7 +26,7 @@ use pocketmine\item\Item; use pocketmine\level\Level; use pocketmine\math\AxisAlignedBB; use pocketmine\Player; -use pocketmine\Server; + class Cake extends Transparent{ diff --git a/src/pocketmine/block/Chest.php b/src/pocketmine/block/Chest.php index 4c2a7cf32..1f7017abd 100644 --- a/src/pocketmine/block/Chest.php +++ b/src/pocketmine/block/Chest.php @@ -104,6 +104,17 @@ class Chest extends Transparent{ new Int("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); + + if($item->hasCustomName()){ + $nbt->CustomName = new String("CustomName", $item->getCustomName()); + } + + if($item->hasCustomBlockData()){ + foreach($item->getCustomBlockData() as $key => $v){ + $nbt->{$key} = $v; + } + } + $tile = Tile::createTile("Chest", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); if($chest instanceof TileChest and $tile instanceof TileChest){ @@ -147,6 +158,11 @@ class Chest extends Transparent{ $chest = Tile::createTile("Chest", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); } + if(isset($chest->namedtag->Lock) and $chest->namedtag->Lock instanceof String){ + if($chest->namedtag->Lock->getValue() !== $item->getCustomName()){ + return true; + } + } if($player->isCreative()){ return true; diff --git a/src/pocketmine/block/Coal.php b/src/pocketmine/block/Coal.php index 75e4fb0d4..494534dcf 100644 --- a/src/pocketmine/block/Coal.php +++ b/src/pocketmine/block/Coal.php @@ -45,7 +45,7 @@ class Coal extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::COAL_BLOCK, 0, 1], ]; diff --git a/src/pocketmine/block/CoalOre.php b/src/pocketmine/block/CoalOre.php index 964ed7647..6c1a78e76 100644 --- a/src/pocketmine/block/CoalOre.php +++ b/src/pocketmine/block/CoalOre.php @@ -45,7 +45,7 @@ class CoalOre extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::COAL, 0, 1], ]; diff --git a/src/pocketmine/block/Cobblestone.php b/src/pocketmine/block/Cobblestone.php index 0a41aa186..5954edbba 100644 --- a/src/pocketmine/block/Cobblestone.php +++ b/src/pocketmine/block/Cobblestone.php @@ -45,7 +45,7 @@ class Cobblestone extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::COBBLESTONE, 0, 1], ]; diff --git a/src/pocketmine/block/Diamond.php b/src/pocketmine/block/Diamond.php index cbe924c95..1fd175dca 100644 --- a/src/pocketmine/block/Diamond.php +++ b/src/pocketmine/block/Diamond.php @@ -45,7 +45,7 @@ class Diamond extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 4){ + if($item->isPickaxe() >= Tool::TIER_IRON){ return [ [Item::DIAMOND_BLOCK, 0, 1], ]; diff --git a/src/pocketmine/block/DiamondOre.php b/src/pocketmine/block/DiamondOre.php index e9b9466db..c05abb384 100644 --- a/src/pocketmine/block/DiamondOre.php +++ b/src/pocketmine/block/DiamondOre.php @@ -45,7 +45,7 @@ class DiamondOre extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 4){ + if($item->isPickaxe() >= Tool::TIER_IRON){ return [ [Item::DIAMOND, 0, 1], ]; diff --git a/src/pocketmine/block/Door.php b/src/pocketmine/block/Door.php index c8ac0e7f4..61258f1e4 100644 --- a/src/pocketmine/block/Door.php +++ b/src/pocketmine/block/Door.php @@ -26,9 +26,7 @@ use pocketmine\level\Level; use pocketmine\level\sound\DoorSound; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; use pocketmine\Player; -use pocketmine\Server; abstract class Door extends Transparent{ diff --git a/src/pocketmine/block/DoubleSlab.php b/src/pocketmine/block/DoubleSlab.php index 23c712ab8..3ccdcf84b 100644 --- a/src/pocketmine/block/DoubleSlab.php +++ b/src/pocketmine/block/DoubleSlab.php @@ -49,13 +49,13 @@ class DoubleSlab extends Solid{ 4 => "Brick", 5 => "Stone Brick", 6 => "Quartz", - 7 => "", + 7 => "Nether Brick", ]; return "Double " . $names[$this->meta & 0x07] . " Slab"; } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::SLAB, $this->meta & 0x07, 2], ]; diff --git a/src/pocketmine/block/Emerald.php b/src/pocketmine/block/Emerald.php index b5db557e0..2f24e34c5 100644 --- a/src/pocketmine/block/Emerald.php +++ b/src/pocketmine/block/Emerald.php @@ -45,7 +45,7 @@ class Emerald extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 4){ + if($item->isPickaxe() >= Tool::TIER_IRON){ return [ [Item::EMERALD_BLOCK, 0, 1], ]; diff --git a/src/pocketmine/block/EmeraldOre.php b/src/pocketmine/block/EmeraldOre.php index 54971f3b4..90fac45c3 100644 --- a/src/pocketmine/block/EmeraldOre.php +++ b/src/pocketmine/block/EmeraldOre.php @@ -45,7 +45,7 @@ class EmeraldOre extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 4){ + if($item->isPickaxe() >= Tool::TIER_IRON){ return [ [Item::EMERALD, 0, 1], ]; diff --git a/src/pocketmine/block/EnchantingTable.php b/src/pocketmine/block/EnchantingTable.php new file mode 100644 index 000000000..5890367ab --- /dev/null +++ b/src/pocketmine/block/EnchantingTable.php @@ -0,0 +1,108 @@ +getLevel()->setBlock($block, $this, true, true); + $nbt = new Compound("", [ + new String("id", Tile::ENCHANT_TABLE), + new Int("x", $this->x), + new Int("y", $this->y), + new Int("z", $this->z) + ]); + + if($item->hasCustomName()){ + $nbt->CustomName = new String("CustomName", $item->getCustomName()); + } + + if($item->hasCustomBlockData()){ + foreach($item->getCustomBlockData() as $key => $v){ + $nbt->{$key} = $v; + } + } + + Tile::createTile(Tile::ENCHANT_TABLE, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); + + return true; + } + + public function canBeActivated(){ + return true; + } + + public function getHardness(){ + return 5; + } + + public function getResistance(){ + return 6000; + } + + public function getName(){ + return "Enchanting Table"; + } + + public function getToolType(){ + return Tool::TYPE_PICKAXE; + } + + public function onActivate(Item $item, Player $player = null){ + if($player instanceof Player){ + //TODO lock + if($player->isCreative()){ + return true; + } + + $player->addWindow(new EnchantInventory($this)); + } + + return true; + } + + public function getDrops(Item $item){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ + return [ + [$this->id, 0, 1], + ]; + }else{ + return []; + } + } +} \ No newline at end of file diff --git a/src/pocketmine/block/EndStone.php b/src/pocketmine/block/EndStone.php index fa14abbd7..fe67d2b84 100644 --- a/src/pocketmine/block/EndStone.php +++ b/src/pocketmine/block/EndStone.php @@ -21,7 +21,7 @@ namespace pocketmine\block; -use pocketmine\item\Item; + use pocketmine\item\Tool; class EndStone extends Solid{ diff --git a/src/pocketmine/block/Fence.php b/src/pocketmine/block/Fence.php index 2262e1490..510ea39f7 100644 --- a/src/pocketmine/block/Fence.php +++ b/src/pocketmine/block/Fence.php @@ -26,7 +26,13 @@ use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector3; class Fence extends Transparent{ - + const FENCE_OAK = 0; + const FENCE_SPRUCE = 1; + const FENCE_BIRCH = 2; + const FENCE_JUNGLE = 3; + const FENCE_ACACIA = 4; + const FENCE_DARKOAK = 5; + protected $id = self::FENCE; public function __construct($meta = 0){ @@ -38,18 +44,18 @@ class Fence extends Transparent{ } public function getToolType(){ - return Tool::TYPE_PICKAXE; + return Tool::TYPE_AXE; } public function getName(){ static $names = [ - 0 => "Oak Fence", - 1 => "Spruce Fence", - 2 => "Birch Fence", - 3 => "Jungle Fence", - 4 => "Acacia Fence", - 5 => "Dark Oak Fence", + self::FENCE_OAK => "Oak Fence", + self::FENCE_SPRUCE => "Spruce Fence", + self::FENCE_BIRCH => "Birch Fence", + self::FENCE_JUNGLE => "Jungle Fence", + self::FENCE_ACACIA => "Acacia Fence", + self::FENCE_DARKOAK => "Dark Oak Fence", "", "" ]; diff --git a/src/pocketmine/block/Flowable.php b/src/pocketmine/block/Flowable.php index d74c9867e..3c380301e 100644 --- a/src/pocketmine/block/Flowable.php +++ b/src/pocketmine/block/Flowable.php @@ -22,7 +22,7 @@ namespace pocketmine\block; -use pocketmine\item\Item; + abstract class Flowable extends Transparent{ diff --git a/src/pocketmine/block/NetherReactor.php b/src/pocketmine/block/FlowerPot.php similarity index 84% rename from src/pocketmine/block/NetherReactor.php rename to src/pocketmine/block/FlowerPot.php index 47e49582b..5451efdfc 100644 --- a/src/pocketmine/block/NetherReactor.php +++ b/src/pocketmine/block/FlowerPot.php @@ -21,21 +21,18 @@ namespace pocketmine\block; +use pocketmine\item\Item; -class NetherReactor extends Solid{ +class FlowerPot extends Transparent{ - protected $id = self::NETHER_REACTOR; + protected $id = self::FLOWER_POT_BLOCK; public function __construct($meta = 0){ $this->meta = $meta; } public function getName(){ - return "Nether Reactor"; - } - - public function canBeActivated(){ - return true; + return "Flower Pot Block"; } } \ No newline at end of file diff --git a/src/pocketmine/block/GlowingRedstoneOre.php b/src/pocketmine/block/GlowingRedstoneOre.php index a1675fc02..095241607 100644 --- a/src/pocketmine/block/GlowingRedstoneOre.php +++ b/src/pocketmine/block/GlowingRedstoneOre.php @@ -60,7 +60,7 @@ class GlowingRedstoneOre extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 4){ + if($item->isPickaxe() >= Tool::TIER_IRON){ return [ [Item::REDSTONE_DUST, 0, mt_rand(4, 5)], ]; diff --git a/src/pocketmine/block/Gold.php b/src/pocketmine/block/Gold.php index cb9421124..62b942f53 100644 --- a/src/pocketmine/block/Gold.php +++ b/src/pocketmine/block/Gold.php @@ -45,7 +45,7 @@ class Gold extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 4){ + if($item->isPickaxe() >= Tool::TIER_IRON){ return [ [Item::GOLD_BLOCK, 0, 1], ]; diff --git a/src/pocketmine/block/GoldOre.php b/src/pocketmine/block/GoldOre.php index 5c87c16d4..c56cff977 100644 --- a/src/pocketmine/block/GoldOre.php +++ b/src/pocketmine/block/GoldOre.php @@ -45,7 +45,7 @@ class GoldOre extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 4){ + if($item->isPickaxe() >= Tool::TIER_IRON){ return [ [Item::GOLD_ORE, 0, 1], ]; diff --git a/src/pocketmine/block/GrassPath.php b/src/pocketmine/block/GrassPath.php index c53ed2eed..db642a54c 100644 --- a/src/pocketmine/block/GrassPath.php +++ b/src/pocketmine/block/GrassPath.php @@ -21,16 +21,13 @@ namespace pocketmine\block; -use pocketmine\event\block\BlockSpreadEvent; + use pocketmine\item\Item; use pocketmine\item\Tool; -use pocketmine\level\generator\object\TallGrass as TallGrassObject; -use pocketmine\level\Level; + + use pocketmine\math\AxisAlignedBB; -use pocketmine\math\Vector3; -use pocketmine\Player; -use pocketmine\Server; -use pocketmine\utils\Random; + class GrassPath extends Transparent{ diff --git a/src/pocketmine/block/HardenedClay.php b/src/pocketmine/block/HardenedClay.php index c01c8c349..c959d5a42 100644 --- a/src/pocketmine/block/HardenedClay.php +++ b/src/pocketmine/block/HardenedClay.php @@ -21,7 +21,7 @@ namespace pocketmine\block; -use pocketmine\item\Item; + use pocketmine\item\Tool; class HardenedClay extends Solid{ diff --git a/src/pocketmine/block/Iron.php b/src/pocketmine/block/Iron.php index 66a2c28ee..2ffc4cd6a 100644 --- a/src/pocketmine/block/Iron.php +++ b/src/pocketmine/block/Iron.php @@ -45,7 +45,7 @@ class Iron extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 3){ + if($item->isPickaxe() >= Tool::TIER_STONE){ return [ [Item::IRON_BLOCK, 0, 1], ]; diff --git a/src/pocketmine/block/IronBars.php b/src/pocketmine/block/IronBars.php index bebe0f3a2..b5e70e873 100644 --- a/src/pocketmine/block/IronBars.php +++ b/src/pocketmine/block/IronBars.php @@ -45,7 +45,7 @@ class IronBars extends Thin{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::IRON_BARS, 0, 1], ]; diff --git a/src/pocketmine/block/IronDoor.php b/src/pocketmine/block/IronDoor.php index 447b723f9..70195b9a9 100644 --- a/src/pocketmine/block/IronDoor.php +++ b/src/pocketmine/block/IronDoor.php @@ -45,7 +45,7 @@ class IronDoor extends Door{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::IRON_DOOR, 0, 1], ]; diff --git a/src/pocketmine/block/IronOre.php b/src/pocketmine/block/IronOre.php index 01e8be86d..cda415136 100644 --- a/src/pocketmine/block/IronOre.php +++ b/src/pocketmine/block/IronOre.php @@ -45,7 +45,7 @@ class IronOre extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 3){ + if($item->isPickaxe() >= Tool::TIER_STONE){ return [ [Item::IRON_ORE, 0, 1], ]; diff --git a/src/pocketmine/block/IronTrapdoor.php b/src/pocketmine/block/IronTrapdoor.php new file mode 100644 index 000000000..b23d2a225 --- /dev/null +++ b/src/pocketmine/block/IronTrapdoor.php @@ -0,0 +1,156 @@ +meta = $meta; + } + + public function getName(){ + return "Iron Trapdoor"; + } + + public function getHardness(){ + return 5; + } + + public function canBeActivated(){ + return true; + } + + protected function recalculateBoundingBox(){ + + $damage = $this->getDamage(); + + $f = 0.1875; + + if(($damage & 0x08) > 0){ + $bb = new AxisAlignedBB( + $this->x, + $this->y + 1 - $f, + $this->z, + $this->x + 1, + $this->y + 1, + $this->z + 1 + ); + }else{ + $bb = new AxisAlignedBB( + $this->x, + $this->y, + $this->z, + $this->x + 1, + $this->y + $f, + $this->z + 1 + ); + } + + if(($damage & 0x04) > 0){ + if(($damage & 0x03) === 0){ + $bb->setBounds( + $this->x, + $this->y, + $this->z + 1 - $f, + $this->x + 1, + $this->y + 1, + $this->z + 1 + ); + }elseif(($damage & 0x03) === 1){ + $bb->setBounds( + $this->x, + $this->y, + $this->z, + $this->x + 1, + $this->y + 1, + $this->z + $f + ); + } + if(($damage & 0x03) === 2){ + $bb->setBounds( + $this->x + 1 - $f, + $this->y, + $this->z, + $this->x + 1, + $this->y + 1, + $this->z + 1 + ); + } + if(($damage & 0x03) === 3){ + $bb->setBounds( + $this->x, + $this->y, + $this->z, + $this->x + $f, + $this->y + 1, + $this->z + 1 + ); + } + } + + return $bb; + } + + public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ + if(($target->isTransparent() === false or $target->getId() === self::SLAB) and $face !== 0 and $face !== 1){ + $faces = [ + 2 => 0, + 3 => 1, + 4 => 2, + 5 => 3, + ]; + $this->meta = $faces[$face] & 0x03; + if($fy > 0.5){ + $this->meta |= 0x08; + } + $this->getLevel()->setBlock($block, $this, true, true); + + return true; + } + + return false; + } + + public function getDrops(Item $item){ + return [ + [$this->id, 0, 1], + ]; + } + + public function onActivate(Item $item, Player $player = null){ + $this->meta ^= 0x04; + $this->getLevel()->setBlock($this, $this, true); + $this->level->addSound(new DoorSound($this)); + return true; + } + + public function getToolType(){ + return Tool::TYPE_PICKAXE; + } +} \ No newline at end of file diff --git a/src/pocketmine/block/Lapis.php b/src/pocketmine/block/Lapis.php index aa0b9212f..5cc8a2537 100644 --- a/src/pocketmine/block/Lapis.php +++ b/src/pocketmine/block/Lapis.php @@ -45,7 +45,7 @@ class Lapis extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 3){ + if($item->isPickaxe() >= Tool::TIER_STONE){ return [ [Item::LAPIS_BLOCK, 0, 1], ]; diff --git a/src/pocketmine/block/LapisOre.php b/src/pocketmine/block/LapisOre.php index 44d6e9eeb..68ca5626e 100644 --- a/src/pocketmine/block/LapisOre.php +++ b/src/pocketmine/block/LapisOre.php @@ -45,7 +45,7 @@ class LapisOre extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 3){ + if($item->isPickaxe() >= Tool::TIER_STONE){ return [ [Item::DYE, 4, mt_rand(4, 8)], ]; diff --git a/src/pocketmine/block/MossStone.php b/src/pocketmine/block/MossStone.php index 5700d5928..6476028f2 100644 --- a/src/pocketmine/block/MossStone.php +++ b/src/pocketmine/block/MossStone.php @@ -45,7 +45,7 @@ class MossStone extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::MOSS_STONE, $this->meta, 1], ]; diff --git a/src/pocketmine/block/NetherBrick.php b/src/pocketmine/block/NetherBrick.php index 937713b80..12400a516 100644 --- a/src/pocketmine/block/NetherBrick.php +++ b/src/pocketmine/block/NetherBrick.php @@ -45,7 +45,7 @@ class NetherBrick extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::NETHER_BRICKS, 0, 1], ]; diff --git a/src/pocketmine/block/NetherBrickFence.php b/src/pocketmine/block/NetherBrickFence.php new file mode 100644 index 000000000..62a182375 --- /dev/null +++ b/src/pocketmine/block/NetherBrickFence.php @@ -0,0 +1,71 @@ +meta = $meta; + } + + public function getBreakTime(Item $item){ + if ($item instanceof Air){ + //Breaking by hand + return 10; + } + else{ + // Other breaktimes are equal to woodfences. + return parent::getBreakTime($item); + } + } + + public function getHardness(){ + return 2; + } + + public function getToolType(){ + return Tool::TYPE_PICKAXE; + } + + public function getName(){ + return "Nether Brick Fence"; + } + + public function canConnect(Block $block){ + //TODO: activate comments when the NetherBrickFenceGate class has been created. + return ($block instanceof NetherBrickFence /* or $block instanceof NetherBrickFenceGate */) ? true : $block->isSolid() and !$block->isTransparent(); + } + + public function getDrops(Item $item){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ + return [ + [$this->id, $this->meta, 1], + ]; + }else{ + return []; + } + } +} diff --git a/src/pocketmine/block/Netherrack.php b/src/pocketmine/block/Netherrack.php index 8d3d5135a..7496ad14d 100644 --- a/src/pocketmine/block/Netherrack.php +++ b/src/pocketmine/block/Netherrack.php @@ -45,7 +45,7 @@ class Netherrack extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::NETHERRACK, 0, 1], ]; diff --git a/src/pocketmine/block/Obsidian.php b/src/pocketmine/block/Obsidian.php index cc93e4379..f483c58b9 100644 --- a/src/pocketmine/block/Obsidian.php +++ b/src/pocketmine/block/Obsidian.php @@ -45,7 +45,7 @@ class Obsidian extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 5){ + if($item->isPickaxe() >= Tool::TIER_DIAMOND){ return [ [Item::OBSIDIAN, 0, 1], ]; diff --git a/src/pocketmine/block/PackedIce.php b/src/pocketmine/block/PackedIce.php new file mode 100644 index 000000000..8d9e30a9d --- /dev/null +++ b/src/pocketmine/block/PackedIce.php @@ -0,0 +1,47 @@ + "Quartz Block", - 1 => "Chiseled Quartz Block", - 2 => "Quartz Pillar", - 3 => "Quartz Pillar", + self::QUARTZ_NORMAL => "Quartz Block", + self::QUARTZ_CHISELED => "Chiseled Quartz Block", + self::QUARTZ_PILLAR => "Quartz Pillar", + self::QUARTZ_PILLAR2 => "Quartz Pillar", ]; return $names[$this->meta & 0x03]; } @@ -51,7 +56,7 @@ class Quartz extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::QUARTZ_BLOCK, $this->meta & 0x03, 1], ]; diff --git a/src/pocketmine/block/Redstone.php b/src/pocketmine/block/Redstone.php index c2707e016..41cd19d54 100644 --- a/src/pocketmine/block/Redstone.php +++ b/src/pocketmine/block/Redstone.php @@ -45,7 +45,7 @@ class Redstone extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::REDSTONE_BLOCK, 0, 1], ]; diff --git a/src/pocketmine/block/RedstoneOre.php b/src/pocketmine/block/RedstoneOre.php index 6b31c4090..2fb6df907 100644 --- a/src/pocketmine/block/RedstoneOre.php +++ b/src/pocketmine/block/RedstoneOre.php @@ -58,7 +58,7 @@ class RedstoneOre extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 2){ + if($item->isPickaxe() >= Tool::TIER_GOLD){ return [ [Item::REDSTONE_DUST, 0, mt_rand(4, 5)], ]; diff --git a/src/pocketmine/block/Sandstone.php b/src/pocketmine/block/Sandstone.php index a94a32ae3..60bca024a 100644 --- a/src/pocketmine/block/Sandstone.php +++ b/src/pocketmine/block/Sandstone.php @@ -26,6 +26,10 @@ use pocketmine\item\Tool; class Sandstone extends Solid{ + const NORMAL = 0; + const CHISELED = 1; + const SMOOTH = 2; + protected $id = self::SANDSTONE; public function __construct($meta = 0){ @@ -38,9 +42,9 @@ class Sandstone extends Solid{ public function getName(){ static $names = [ - 0 => "Sandstone", - 1 => "Chiseled Sandstone", - 2 => "Smooth Sandstone", + self::NORMAL => "Sandstone", + self::CHISELED => "Chiseled Sandstone", + self::SMOOTH => "Smooth Sandstone", 3 => "", ]; return $names[$this->meta & 0x03]; @@ -51,7 +55,7 @@ class Sandstone extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::SANDSTONE, $this->meta & 0x03, 1], ]; diff --git a/src/pocketmine/block/SignPost.php b/src/pocketmine/block/SignPost.php index c151f0731..5ae519979 100644 --- a/src/pocketmine/block/SignPost.php +++ b/src/pocketmine/block/SignPost.php @@ -88,7 +88,7 @@ class SignPost extends Transparent{ } public function onBreak(Item $item){ - $this->getLevel()->setBlock($this, new Air(), true, true, true); + $this->getLevel()->setBlock($this, new Air(), true, true); return true; } diff --git a/src/pocketmine/block/Slab.php b/src/pocketmine/block/Slab.php index 303d8fbd0..0bf6ae950 100644 --- a/src/pocketmine/block/Slab.php +++ b/src/pocketmine/block/Slab.php @@ -27,6 +27,14 @@ use pocketmine\math\AxisAlignedBB; use pocketmine\Player; class Slab extends Transparent{ + const STONE = 0; + const SANDSTONE = 1; + const WOODEN = 2; + const COBBLESTONE = 3; + const BRICK = 4; + const STONE_BRICK = 5; + const QUARTZ = 6; + const NETHER_BRICK = 7; protected $id = self::SLAB; @@ -40,14 +48,14 @@ class Slab extends Transparent{ public function getName(){ static $names = [ - 0 => "Stone", - 1 => "Sandstone", - 2 => "Wooden", - 3 => "Cobblestone", - 4 => "Brick", - 5 => "Stone Brick", - 6 => "Quartz", - 7 => "", + self::STONE => "Stone", + self::SANDSTONE => "Sandstone", + self::WOODEN => "Wooden", + self::COBBLESTONE => "Cobblestone", + self::BRICK => "Brick", + self::STONE_BRICK => "Stone Brick", + self::QUARTZ => "Quartz", + self::NETHER_BRICK => "Nether Brick", ]; return (($this->meta & 0x08) > 0 ? "Upper " : "") . $names[$this->meta & 0x07] . " Slab"; } @@ -125,7 +133,7 @@ class Slab extends Transparent{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [$this->id, $this->meta & 0x07, 1], ]; diff --git a/src/pocketmine/block/StainedClay.php b/src/pocketmine/block/StainedClay.php index c67d138ab..73ae98895 100644 --- a/src/pocketmine/block/StainedClay.php +++ b/src/pocketmine/block/StainedClay.php @@ -21,7 +21,7 @@ namespace pocketmine\block; -use pocketmine\item\Item; + use pocketmine\item\Tool; class StainedClay extends Solid{ diff --git a/src/pocketmine/block/Stair.php b/src/pocketmine/block/Stair.php index bb02a76d1..7416b6fd6 100644 --- a/src/pocketmine/block/Stair.php +++ b/src/pocketmine/block/Stair.php @@ -143,7 +143,7 @@ abstract class Stair extends Transparent{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [$this->getId(), 0, 1], ]; diff --git a/src/pocketmine/block/Stone.php b/src/pocketmine/block/Stone.php index 3b9eadd12..055731d7c 100644 --- a/src/pocketmine/block/Stone.php +++ b/src/pocketmine/block/Stone.php @@ -37,7 +37,6 @@ class Stone extends Solid{ public function __construct($meta = 0){ $this->meta = $meta; - } public function getHardness(){ diff --git a/src/pocketmine/block/StoneBricks.php b/src/pocketmine/block/StoneBricks.php index ecb17ed5e..461a39e4e 100644 --- a/src/pocketmine/block/StoneBricks.php +++ b/src/pocketmine/block/StoneBricks.php @@ -25,6 +25,10 @@ use pocketmine\item\Item; use pocketmine\item\Tool; class StoneBricks extends Solid{ + const NORMAL = 0; + const MOSSY = 1; + const CRACKED = 2; + const CHISELED = 3; protected $id = self::STONE_BRICKS; @@ -42,16 +46,16 @@ class StoneBricks extends Solid{ public function getName(){ static $names = [ - 0 => "Stone Bricks", - 1 => "Mossy Stone Bricks", - 2 => "Cracked Stone Bricks", - 3 => "Chiseled Stone Bricks", + self::NORMAL => "Stone Bricks", + self::MOSSY => "Mossy Stone Bricks", + self::CRACKED => "Cracked Stone Bricks", + self::CHISELED => "Chiseled Stone Bricks", ]; return $names[$this->meta & 0x03]; } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::STONE_BRICKS, $this->meta & 0x03, 1], ]; diff --git a/src/pocketmine/block/StoneWall.php b/src/pocketmine/block/StoneWall.php index 953c182c0..cf689b694 100644 --- a/src/pocketmine/block/StoneWall.php +++ b/src/pocketmine/block/StoneWall.php @@ -27,6 +27,8 @@ use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector3; class StoneWall extends Transparent{ + const NONE_MOSSY_WALL = 0; + const MOSSY_WALL = 1; protected $id = self::STONE_WALL; diff --git a/src/pocketmine/block/Stonecutter.php b/src/pocketmine/block/Stonecutter.php index 3dc127c46..918011e3f 100644 --- a/src/pocketmine/block/Stonecutter.php +++ b/src/pocketmine/block/Stonecutter.php @@ -55,7 +55,7 @@ class Stonecutter extends Solid{ } public function getDrops(Item $item){ - if($item->isPickaxe() >= 1){ + if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::STONECUTTER, 0, 1], ]; diff --git a/src/pocketmine/block/TrappedChest.php b/src/pocketmine/block/TrappedChest.php new file mode 100644 index 000000000..d1848db1d --- /dev/null +++ b/src/pocketmine/block/TrappedChest.php @@ -0,0 +1,181 @@ +meta = $meta; + } + + public function canBeActivated(){ + return true; + } + + public function getHardness(){ + return 2.5; + } + + public function getName(){ + return "Trapped Chest"; + } + + public function getToolType(){ + return Tool::TYPE_AXE; + } + + protected function recalculateBoundingBox(){ + return new AxisAlignedBB( + $this->x + 0.0625, + $this->y, + $this->z + 0.0625, + $this->x + 0.9375, + $this->y + 0.9475, + $this->z + 0.9375 + ); + } + + public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ + $faces = [ + 0 => 4, + 1 => 2, + 2 => 5, + 3 => 3, + ]; + + $chest = null; + $this->meta = $faces[$player instanceof Player ? $player->getDirection() : 0]; + + for($side = 2; $side <= 5; ++$side){ + if(($this->meta === 4 or $this->meta === 5) and ($side === 4 or $side === 5)){ + continue; + }elseif(($this->meta === 3 or $this->meta === 2) and ($side === 2 or $side === 3)){ + continue; + } + $c = $this->getSide($side); + if($c instanceof Chest and $c->getDamage() === $this->meta){ + $tile = $this->getLevel()->getTile($c); + if($tile instanceof TileChest and !$tile->isPaired()){ + $chest = $tile; + break; + } + } + } + + $this->getLevel()->setBlock($block, $this, true, true); + $nbt = new Compound("", [ + new Enum("Items", []), + new String("id", Tile::CHEST), + new Int("x", $this->x), + new Int("y", $this->y), + new Int("z", $this->z) + ]); + $nbt->Items->setTagType(NBT::TAG_Compound); + + if($item->hasCustomName()){ + $nbt->CustomName = new String("CustomName", $item->getCustomName()); + } + + if($item->hasCustomBlockData()){ + foreach($item->getCustomBlockData() as $key => $v){ + $nbt->{$key} = $v; + } + } + + $tile = Tile::createTile("Chest", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); + + if($chest instanceof TileChest and $tile instanceof TileChest){ + $chest->pairWith($tile); + $tile->pairWith($chest); + } + + return true; + } + + public function onBreak(Item $item){ + $t = $this->getLevel()->getTile($this); + if($t instanceof TileChest){ + $t->unpair(); + } + $this->getLevel()->setBlock($this, new Air(), true, true); + + return true; + } + + public function onActivate(Item $item, Player $player = null){ + if($player instanceof Player){ + $top = $this->getSide(1); + if($top->isTransparent() !== true){ + return true; + } + + $t = $this->getLevel()->getTile($this); + $chest = null; + if($t instanceof TileChest){ + $chest = $t; + }else{ + $nbt = new Compound("", [ + new Enum("Items", []), + new String("id", Tile::CHEST), + new Int("x", $this->x), + new Int("y", $this->y), + new Int("z", $this->z) + ]); + $nbt->Items->setTagType(NBT::TAG_Compound); + $chest = Tile::createTile("Chest", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); + } + + if(isset($chest->namedtag->Lock) and $chest->namedtag->Lock instanceof String){ + if($chest->namedtag->Lock->getValue() !== $item->getCustomName()){ + return true; + } + } + + if($player->isCreative()){ + return true; + } + $player->addWindow($chest->getInventory()); + } + + return true; + } + + public function getDrops(Item $item){ + return [ + [$this->id, 0, 1], + ]; + } +} \ No newline at end of file diff --git a/src/pocketmine/block/WallSign.php b/src/pocketmine/block/WallSign.php index 1a09123fa..66064a8bc 100644 --- a/src/pocketmine/block/WallSign.php +++ b/src/pocketmine/block/WallSign.php @@ -22,6 +22,8 @@ namespace pocketmine\block; +use pocketmine\level\Level; + class WallSign extends SignPost{ protected $id = self::WALL_SIGN; @@ -31,6 +33,20 @@ class WallSign extends SignPost{ } public function onUpdate($type){ + $faces = [ + 2 => 3, + 3 => 2, + 4 => 5, + 5 => 4, + ]; + if($type === Level::BLOCK_UPDATE_NORMAL){ + if(isset($faces[$this->meta])) { + if ($this->getSide($faces[$this->meta])->getId() === self::AIR) { + $this->getLevel()->useBreakOn($this); + } + return Level::BLOCK_UPDATE_NORMAL; + } + } return false; } } \ No newline at end of file diff --git a/src/pocketmine/block/WaterLily.php b/src/pocketmine/block/WaterLily.php index b3d6abee0..0a56de20e 100644 --- a/src/pocketmine/block/WaterLily.php +++ b/src/pocketmine/block/WaterLily.php @@ -21,9 +21,9 @@ namespace pocketmine\block; -use pocketmine\entity\Entity; + use pocketmine\item\Item; -use pocketmine\item\Tool; + use pocketmine\level\Level; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector3; diff --git a/src/pocketmine/command/PluginCommand.php b/src/pocketmine/command/PluginCommand.php index f1d34e751..c8fbc65d9 100644 --- a/src/pocketmine/command/PluginCommand.php +++ b/src/pocketmine/command/PluginCommand.php @@ -23,7 +23,7 @@ namespace pocketmine\command; use pocketmine\event\TranslationContainer; use pocketmine\plugin\Plugin; -use pocketmine\utils\TextFormat; + class PluginCommand extends Command implements PluginIdentifiableCommand{ diff --git a/src/pocketmine/command/SimpleCommandMap.php b/src/pocketmine/command/SimpleCommandMap.php index da16e26f8..243354131 100644 --- a/src/pocketmine/command/SimpleCommandMap.php +++ b/src/pocketmine/command/SimpleCommandMap.php @@ -29,6 +29,7 @@ use pocketmine\command\defaults\DeopCommand; use pocketmine\command\defaults\DifficultyCommand; use pocketmine\command\defaults\DumpMemoryCommand; use pocketmine\command\defaults\EffectCommand; +use pocketmine\command\defaults\EnchantCommand; use pocketmine\command\defaults\GamemodeCommand; use pocketmine\command\defaults\GarbageCollectorCommand; use pocketmine\command\defaults\GiveCommand; @@ -105,6 +106,7 @@ class SimpleCommandMap implements CommandMap{ $this->register("pocketmine", new SaveCommand("save-all")); $this->register("pocketmine", new GiveCommand("give")); $this->register("pocketmine", new EffectCommand("effect")); + $this->register("pocketmine", new EnchantCommand("enchant")); $this->register("pocketmine", new ParticleCommand("particle")); $this->register("pocketmine", new GamemodeCommand("gamemode")); $this->register("pocketmine", new KillCommand("kill")); diff --git a/src/pocketmine/command/defaults/BanCommand.php b/src/pocketmine/command/defaults/BanCommand.php index bd97abaea..e57e20a66 100644 --- a/src/pocketmine/command/defaults/BanCommand.php +++ b/src/pocketmine/command/defaults/BanCommand.php @@ -25,7 +25,7 @@ use pocketmine\command\Command; use pocketmine\command\CommandSender; use pocketmine\event\TranslationContainer; use pocketmine\Player; -use pocketmine\utils\TextFormat; + class BanCommand extends VanillaCommand{ diff --git a/src/pocketmine/command/defaults/BanIpCommand.php b/src/pocketmine/command/defaults/BanIpCommand.php index 385fac6c6..b5b82c5d9 100644 --- a/src/pocketmine/command/defaults/BanIpCommand.php +++ b/src/pocketmine/command/defaults/BanIpCommand.php @@ -25,7 +25,7 @@ use pocketmine\command\Command; use pocketmine\command\CommandSender; use pocketmine\event\TranslationContainer; use pocketmine\Player; -use pocketmine\utils\TextFormat; + class BanIpCommand extends VanillaCommand{ diff --git a/src/pocketmine/command/defaults/BanListCommand.php b/src/pocketmine/command/defaults/BanListCommand.php index 251a096d3..6fe8ba7b5 100644 --- a/src/pocketmine/command/defaults/BanListCommand.php +++ b/src/pocketmine/command/defaults/BanListCommand.php @@ -64,9 +64,9 @@ class BanListCommand extends VanillaCommand{ } if($args[0] === "ips"){ - $sender->sendMessage("commands.banlist.ips", [count($list)]); + $sender->sendMessage(new TranslationContainer("commands.banlist.ips", [count($list)])); }else{ - $sender->sendMessage("commands.banlist.players", [count($list)]); + $sender->sendMessage(new TranslationContainer("commands.banlist.players", [count($list)])); } $sender->sendMessage(substr($message, 0, -2)); diff --git a/src/pocketmine/command/defaults/DefaultGamemodeCommand.php b/src/pocketmine/command/defaults/DefaultGamemodeCommand.php index e14d58471..b6e804f80 100644 --- a/src/pocketmine/command/defaults/DefaultGamemodeCommand.php +++ b/src/pocketmine/command/defaults/DefaultGamemodeCommand.php @@ -24,7 +24,7 @@ namespace pocketmine\command\defaults; use pocketmine\command\CommandSender; use pocketmine\event\TranslationContainer; use pocketmine\Server; -use pocketmine\utils\TextFormat; + class DefaultGamemodeCommand extends VanillaCommand{ diff --git a/src/pocketmine/command/defaults/DifficultyCommand.php b/src/pocketmine/command/defaults/DifficultyCommand.php index ea7255997..c553037f7 100644 --- a/src/pocketmine/command/defaults/DifficultyCommand.php +++ b/src/pocketmine/command/defaults/DifficultyCommand.php @@ -27,7 +27,7 @@ use pocketmine\event\TranslationContainer; use pocketmine\network\Network; use pocketmine\network\protocol\SetDifficultyPacket; use pocketmine\Server; -use pocketmine\utils\TextFormat; + class DifficultyCommand extends VanillaCommand{ @@ -62,7 +62,7 @@ class DifficultyCommand extends VanillaCommand{ $pk = new SetDifficultyPacket(); $pk->difficulty = $sender->getServer()->getDifficulty(); - Server::broadcastPacket($sender->getServer()->getOnlinePlayers(), $pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + Server::broadcastPacket($sender->getServer()->getOnlinePlayers(), $pk); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.difficulty.success", [$difficulty])); }else{ diff --git a/src/pocketmine/command/defaults/DumpMemoryCommand.php b/src/pocketmine/command/defaults/DumpMemoryCommand.php index 4d35d16e7..b4efb17d3 100644 --- a/src/pocketmine/command/defaults/DumpMemoryCommand.php +++ b/src/pocketmine/command/defaults/DumpMemoryCommand.php @@ -22,8 +22,7 @@ namespace pocketmine\command\defaults; use pocketmine\command\CommandSender; -use pocketmine\utils\TextFormat; -use pocketmine\utils\Utils; + class DumpMemoryCommand extends VanillaCommand{ diff --git a/src/pocketmine/command/defaults/EffectCommand.php b/src/pocketmine/command/defaults/EffectCommand.php index 184bbf87b..6da6a7e16 100644 --- a/src/pocketmine/command/defaults/EffectCommand.php +++ b/src/pocketmine/command/defaults/EffectCommand.php @@ -21,7 +21,7 @@ namespace pocketmine\command\defaults; -use pocketmine\command\Command; + use pocketmine\command\CommandSender; use pocketmine\entity\Effect; use pocketmine\entity\InstantEffect; diff --git a/src/pocketmine/command/defaults/EnchantCommand.php b/src/pocketmine/command/defaults/EnchantCommand.php new file mode 100644 index 000000000..55ac5d5bf --- /dev/null +++ b/src/pocketmine/command/defaults/EnchantCommand.php @@ -0,0 +1,86 @@ +setPermission("pocketmine.command.enchant"); + } + + public function execute(CommandSender $sender, $currentAlias, array $args){ + if(!$this->testPermission($sender)){ + return true; + } + + if(count($args) < 2){ + $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); + + return true; + } + + $player = $sender->getServer()->getPlayer($args[0]); + + if($player === null){ + $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound")); + return true; + } + + $enchantId = (int) $args[1]; + $enchantLevel = isset($args[2]) ? (int) $args[2] : 1; + + $enchantment = Enchantment::getEnchantment($enchantId); + if($enchantment->getId() === Enchantment::TYPE_INVALID){ + $sender->sendMessage(new TranslationContainer("commands.enchant.notFound", [$enchantId])); + return true; + } + + $enchantment->setLevel($enchantLevel); + + $item = $player->getInventory()->getItemInHand(); + + if($item->getId() <= 0){ + $sender->sendMessage(new TranslationContainer("commands.enchant.noItem")); + return true; + } + + $item->addEnchantment($enchantment); + $player->getInventory()->setItemInHand($item); + + + self::broadcastCommandMessage($sender, new TranslationContainer("%commands.enchant.success")); + return true; + } +} \ No newline at end of file diff --git a/src/pocketmine/command/defaults/GarbageCollectorCommand.php b/src/pocketmine/command/defaults/GarbageCollectorCommand.php index 6794e83d6..6dcd3aa99 100644 --- a/src/pocketmine/command/defaults/GarbageCollectorCommand.php +++ b/src/pocketmine/command/defaults/GarbageCollectorCommand.php @@ -23,7 +23,7 @@ namespace pocketmine\command\defaults; use pocketmine\command\CommandSender; use pocketmine\utils\TextFormat; -use pocketmine\utils\Utils; + class GarbageCollectorCommand extends VanillaCommand{ diff --git a/src/pocketmine/command/defaults/GiveCommand.php b/src/pocketmine/command/defaults/GiveCommand.php index a07d7889c..b8cd20e39 100644 --- a/src/pocketmine/command/defaults/GiveCommand.php +++ b/src/pocketmine/command/defaults/GiveCommand.php @@ -25,6 +25,8 @@ use pocketmine\command\Command; use pocketmine\command\CommandSender; use pocketmine\event\TranslationContainer; use pocketmine\item\Item; +use pocketmine\nbt\NBT; +use pocketmine\nbt\tag\Compound; use pocketmine\Player; use pocketmine\utils\TextFormat; @@ -59,6 +61,23 @@ class GiveCommand extends VanillaCommand{ $item->setCount((int) $args[2]); } + if(isset($args[3])){ + $tags = $exception = null; + $data = implode(" ", array_slice($args, 3)); + try{ + $tags = NBT::parseJSON($data); + }catch (\Exception $ex){ + $exception = $ex; + } + + if(!($tags instanceof Compound) or $exception !== null){ + $sender->sendMessage(new TranslationContainer("commands.give.tagError", [$exception !== null ? $exception->getMessage() : "Invalid tag conversion"])); + return true; + } + + $item->setNamedTag($tags); + } + if($player instanceof Player){ if($item->getId() === 0){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.give.item.notFound", [$args[1]])); diff --git a/src/pocketmine/command/defaults/PardonCommand.php b/src/pocketmine/command/defaults/PardonCommand.php index d44862ac6..30f306666 100644 --- a/src/pocketmine/command/defaults/PardonCommand.php +++ b/src/pocketmine/command/defaults/PardonCommand.php @@ -24,7 +24,7 @@ namespace pocketmine\command\defaults; use pocketmine\command\Command; use pocketmine\command\CommandSender; use pocketmine\event\TranslationContainer; -use pocketmine\utils\TextFormat; + class PardonCommand extends VanillaCommand{ diff --git a/src/pocketmine/command/defaults/PardonIpCommand.php b/src/pocketmine/command/defaults/PardonIpCommand.php index 7e729bac0..bb25c24c9 100644 --- a/src/pocketmine/command/defaults/PardonIpCommand.php +++ b/src/pocketmine/command/defaults/PardonIpCommand.php @@ -24,7 +24,7 @@ namespace pocketmine\command\defaults; use pocketmine\command\Command; use pocketmine\command\CommandSender; use pocketmine\event\TranslationContainer; -use pocketmine\utils\TextFormat; + class PardonIpCommand extends VanillaCommand{ diff --git a/src/pocketmine/command/defaults/ParticleCommand.php b/src/pocketmine/command/defaults/ParticleCommand.php index 25c2f0e11..34eb08997 100644 --- a/src/pocketmine/command/defaults/ParticleCommand.php +++ b/src/pocketmine/command/defaults/ParticleCommand.php @@ -25,19 +25,26 @@ use pocketmine\block\Block; use pocketmine\command\CommandSender; use pocketmine\event\TranslationContainer; use pocketmine\item\Item; +use pocketmine\level\particle\AngryVillagerParticle; use pocketmine\level\particle\BubbleParticle; use pocketmine\level\particle\CriticalParticle; use pocketmine\level\particle\DustParticle; +use pocketmine\level\particle\EnchantmentTableParticle; use pocketmine\level\particle\EnchantParticle; use pocketmine\level\particle\ExplodeParticle; use pocketmine\level\particle\FlameParticle; +use pocketmine\level\particle\HappyVillagerParticle; use pocketmine\level\particle\HeartParticle; +use pocketmine\level\particle\HugeExplodeParticle; use pocketmine\level\particle\InkParticle; +use pocketmine\level\particle\InstantEnchantParticle; use pocketmine\level\particle\ItemBreakParticle; +use pocketmine\level\particle\LargeExplodeParticle; use pocketmine\level\particle\LavaDripParticle; use pocketmine\level\particle\LavaParticle; use pocketmine\level\particle\Particle; use pocketmine\level\particle\PortalParticle; +use pocketmine\level\particle\RainSplashParticle; use pocketmine\level\particle\RedstoneParticle; use pocketmine\level\particle\SmokeParticle; use pocketmine\level\particle\SplashParticle; @@ -123,6 +130,10 @@ class ParticleCommand extends VanillaCommand{ switch($name){ case "explode": return new ExplodeParticle($pos); + case "largeexplode": + return new LargeExplodeParticle($pos); + case "hugeexplosion": + return new HugeExplodeParticle($pos); case "bubble": return new BubbleParticle($pos); case "splash": @@ -136,6 +147,8 @@ class ParticleCommand extends VanillaCommand{ return new SmokeParticle($pos, $data !== null ? $data : 0); case "spell": return new EnchantParticle($pos); + case "instantspell": + return new InstantEnchantParticle($pos); case "dripwater": return new WaterDripParticle($pos); case "driplava": @@ -153,6 +166,8 @@ class ParticleCommand extends VanillaCommand{ return new RedstoneParticle($pos, $data !== null ? $data : 1); case "snowballpoof": return new ItemBreakParticle($pos, Item::get(Item::SNOWBALL)); + case "slime": + return new ItemBreakParticle($pos, Item::get(Item::SLIMEBALL)); case "itembreak": if($data !== null and $data !== 0){ return new ItemBreakParticle($pos, $data); @@ -167,6 +182,14 @@ class ParticleCommand extends VanillaCommand{ return new HeartParticle($pos, $data !== null ? $data : 0); case "ink": return new InkParticle($pos, $data !== null ? $data : 0); + case "droplet": + return new RainSplashParticle($pos); + case "enchantmenttable": + return new EnchantmentTableParticle($pos); + case "happyvillager": + return new HappyVillagerParticle($pos); + case "angryvillager": + return new AngryVillagerParticle($pos); } diff --git a/src/pocketmine/command/defaults/TimingsCommand.php b/src/pocketmine/command/defaults/TimingsCommand.php index 22e2fd79f..ccac246c0 100644 --- a/src/pocketmine/command/defaults/TimingsCommand.php +++ b/src/pocketmine/command/defaults/TimingsCommand.php @@ -24,7 +24,7 @@ namespace pocketmine\command\defaults; use pocketmine\command\CommandSender; use pocketmine\event\TimingsHandler; use pocketmine\event\TranslationContainer; -use pocketmine\utils\TextFormat; + class TimingsCommand extends VanillaCommand{ diff --git a/src/pocketmine/entity/Arrow.php b/src/pocketmine/entity/Arrow.php index c48187285..dd473637d 100644 --- a/src/pocketmine/entity/Arrow.php +++ b/src/pocketmine/entity/Arrow.php @@ -86,7 +86,7 @@ class Arrow extends Projectile{ $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->metadata = $this->dataProperties; - $player->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $player->dataPacket($pk); parent::spawnTo($player); } diff --git a/src/pocketmine/entity/Attribute.php b/src/pocketmine/entity/Attribute.php new file mode 100644 index 000000000..a312ed3a7 --- /dev/null +++ b/src/pocketmine/entity/Attribute.php @@ -0,0 +1,171 @@ + $maxValue or $defaultValue > $maxValue or $defaultValue < $minValue){ + throw new \InvalidArgumentException("Invalid ranges: min value: $minValue, max value: $maxValue, $defaultValue: $defaultValue"); + } + + return self::$attributes[(int) $id] = new Attribute($id, $name, $minValue, $maxValue, $defaultValue, $shouldSend); + } + + /** + * @param $id + * @return null|Attribute + */ + public static function getAttribute($id){ + return isset(self::$attributes[$id]) ? clone self::$attributes[$id] : null; + } + + /** + * @param $name + * @return null|Attribute + */ + public static function getAttributeByName($name){ + foreach(self::$attributes as $a){ + if($a->getName() === $name){ + return clone $a; + } + } + + return null; + } + + private function __construct($id, $name, $minValue, $maxValue, $defaultValue, $shouldSend = false){ + $this->id = (int) $id; + $this->name = (string) $name; + $this->minValue = (float) $minValue; + $this->maxValue = (float) $maxValue; + $this->defaultValue = (float) $defaultValue; + $this->shouldSend = (float) $shouldSend; + + $this->currentValue = $this->defaultValue; + } + + public function getMinValue(){ + return $this->minValue; + } + + public function setMinValue($minValue){ + if($minValue > $this->getMaxValue()){ + throw new \InvalidArgumentException("Value $minValue is bigger than the maxValue!"); + } + + $this->minValue = $minValue; + return $this; + } + + public function getMaxValue(){ + return $this->maxValue; + } + + public function setMaxValue($maxValue){ + if($maxValue < $this->getMinValue()){ + throw new \InvalidArgumentException("Value $maxValue is bigger than the minValue!"); + } + + $this->maxValue = $maxValue; + return $this; + } + + public function getDefaultValue(){ + return $this->defaultValue; + } + + public function setDefaultValue($defaultValue){ + if($defaultValue > $this->getMaxValue() or $defaultValue < $this->getMinValue()){ + throw new \InvalidArgumentException("Value $defaultValue exceeds the range!"); + } + + $this->defaultValue = $defaultValue; + return $this; + } + + public function getValue(){ + return $this->currentValue; + } + + public function setValue($value){ + if($value > $this->getMaxValue() or $value < $this->getMinValue()){ + throw new \InvalidArgumentException("Value $value exceeds the range!"); + } + + $this->currentValue = $value; + + return $this; + } + + public function getName(){ + return $this->name; + } + + public function getId(){ + return $this->id; + } + + public function isSyncable(){ + return $this->shouldSend; + } + +} diff --git a/src/pocketmine/entity/Effect.php b/src/pocketmine/entity/Effect.php index 9626f317c..b10dc0d30 100644 --- a/src/pocketmine/entity/Effect.php +++ b/src/pocketmine/entity/Effect.php @@ -26,7 +26,7 @@ use pocketmine\event\entity\EntityRegainHealthEvent; use pocketmine\network\Network; use pocketmine\network\protocol\MobEffectPacket; use pocketmine\Player; -use pocketmine\Server; + class Effect{ const SPEED = 1; @@ -249,7 +249,7 @@ class Effect{ $pk->eventId = MobEffectPacket::EVENT_ADD; } - $entity->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $entity->dataPacket($pk); } if($this->id === Effect::INVISIBILITY){ @@ -265,7 +265,7 @@ class Effect{ $pk->eventId = MobEffectPacket::EVENT_REMOVE; $pk->effectId = $this->getId(); - $entity->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $entity->dataPacket($pk); } if($this->id === Effect::INVISIBILITY){ diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index 987020325..96ea8d498 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -57,7 +57,7 @@ use pocketmine\network\Network; use pocketmine\network\protocol\MobEffectPacket; use pocketmine\network\protocol\RemoveEntityPacket; use pocketmine\network\protocol\SetEntityDataPacket; -use pocketmine\network\protocol\SetTimePacket; + use pocketmine\Player; use pocketmine\plugin\Plugin; use pocketmine\Server; @@ -92,6 +92,7 @@ abstract class Entity extends Location implements Metadatable{ const DATA_FLAG_ONFIRE = 0; const DATA_FLAG_SNEAKING = 1; const DATA_FLAG_RIDING = 2; + const DATA_FLAG_SPRINTING = 3; const DATA_FLAG_ACTION = 4; const DATA_FLAG_INVISIBLE = 5; @@ -302,6 +303,22 @@ abstract class Entity extends Location implements Metadatable{ $this->setDataProperty(self::DATA_SHOW_NAMETAG, self::DATA_TYPE_BYTE, $value ? 1 : 0); } + public function isSneaking(){ + return $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_SNEAKING); + } + + public function setSneaking($value = true){ + $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_SNEAKING, (bool) $value); + } + + public function isSprinting(){ + return $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_SPRINTING); + } + + public function setSprinting($value = true){ + $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_SPRINTING, (bool) $value); + } + /** * @return Effect[] */ @@ -433,8 +450,13 @@ abstract class Entity extends Location implements Metadatable{ public function saveNBT(){ if(!($this instanceof Player)){ $this->namedtag->id = new String("id", $this->getSaveId()); - $this->namedtag->CustomName = new String("CustomName", $this->getNameTag()); - $this->namedtag->CustomNameVisible = new String("CustomNameVisible", $this->isNameTagVisible()); + if($this->getNameTag() !== ""){ + $this->namedtag->CustomName = new String("CustomName", $this->getNameTag()); + $this->namedtag->CustomNameVisible = new String("CustomNameVisible", $this->isNameTagVisible()); + }else{ + unset($this->namedtag->CustomName); + unset($this->namedtag->CustomNameVisible); + } } $this->namedtag->Pos = new Enum("Pos", [ @@ -495,7 +517,9 @@ abstract class Entity extends Location implements Metadatable{ if(isset($this->namedtag->CustomName)){ $this->setNameTag($this->namedtag["CustomName"]); - $this->setNameTagVisible($this->namedtag["CustomNameVisible"] > 0); + if(isset($this->namedtag->CustomNameVisible)){ + $this->setNameTagVisible($this->namedtag["CustomNameVisible"] > 0); + } } $this->scheduleUpdate(); @@ -527,7 +551,7 @@ abstract class Entity extends Location implements Metadatable{ $pk->duration = $effect->getDuration(); $pk->eventId = MobEffectPacket::EVENT_ADD; - $player->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $player->dataPacket($pk); } } @@ -548,9 +572,9 @@ abstract class Entity extends Location implements Metadatable{ $pk->metadata = $data === null ? $this->dataProperties : $data; if(!is_array($player)){ - $player->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $player->dataPacket($pk); }else{ - Server::broadcastPacket($player, $pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + Server::broadcastPacket($player, $pk); } } @@ -561,7 +585,7 @@ abstract class Entity extends Location implements Metadatable{ if(isset($this->hasSpawned[$player->getLoaderId()])){ $pk = new RemoveEntityPacket(); $pk->eid = $this->getId(); - $player->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $player->dataPacket($pk); unset($this->hasSpawned[$player->getLoaderId()]); } } @@ -1456,7 +1480,7 @@ abstract class Entity extends Location implements Metadatable{ } public function spawnToAll(){ - if($this->chunk === null){ + if($this->chunk === null or $this->closed){ return; } foreach($this->level->getChunkPlayers($this->chunk->getX(), $this->chunk->getZ()) as $player){ diff --git a/src/pocketmine/entity/FallingSand.php b/src/pocketmine/entity/FallingSand.php index e6c97bc0e..645741586 100644 --- a/src/pocketmine/entity/FallingSand.php +++ b/src/pocketmine/entity/FallingSand.php @@ -23,11 +23,11 @@ namespace pocketmine\entity; use pocketmine\block\Block; -use pocketmine\block\Flowable; + use pocketmine\block\Liquid; use pocketmine\event\entity\EntityBlockChangeEvent; use pocketmine\event\entity\EntityDamageEvent; -use pocketmine\event\entity\EntityRegainHealthEvent; + use pocketmine\item\Item as ItemItem; use pocketmine\math\Vector3; use pocketmine\nbt\tag\Byte; @@ -170,7 +170,7 @@ class FallingSand extends Entity{ $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; - $player->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $player->dataPacket($pk); parent::spawnTo($player); } diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 3827ed639..08ccaba28 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -24,6 +24,7 @@ namespace pocketmine\entity; use pocketmine\inventory\InventoryHolder; use pocketmine\inventory\PlayerInventory; use pocketmine\item\Item as ItemItem; +use pocketmine\utils\UUID; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\Byte; use pocketmine\nbt\tag\Compound; @@ -46,29 +47,48 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ /** @var PlayerInventory */ protected $inventory; + + /** @var UUID */ + protected $uuid; + protected $rawUUID; + public $width = 0.6; public $length = 0.6; public $height = 1.8; public $eyeHeight = 1.62; + protected $skinName; protected $skin; - protected $isSlim = false; public function getSkinData(){ return $this->skin; } - public function isSkinSlim(){ - return $this->isSlim; + public function getSkinName(){ + return $this->skinName; + } + + /** + * @return UUID|null + */ + public function getUniqueId(){ + return $this->uuid; + } + + /** + * @return string + */ + public function getRawUniqueId(){ + return $this->rawUUID; } /** * @param string $str - * @param bool $isSlim + * @param string $skinName */ - public function setSkin($str, $isSlim = false){ + public function setSkin($str, $skinName){ $this->skin = $str; - $this->isSlim = (bool) $isSlim; + $this->skinName = $skinName; } public function getInventory(){ @@ -92,8 +112,10 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ } if(isset($this->namedtag->Skin) and $this->namedtag->Skin instanceof Compound){ - $this->setSkin($this->namedtag->Skin["Data"], $this->namedtag->Skin["Slim"] > 0); + $this->setSkin($this->namedtag->Skin["Data"], $this->namedtag->Skin["Name"]); } + + $this->uuid = UUID::fromData($this->getId(), $this->getSkinData(), $this->getNameTag()); } if(isset($this->namedtag->Inventory) and $this->namedtag->Inventory instanceof Enum){ @@ -101,9 +123,9 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ if($item["Slot"] >= 0 and $item["Slot"] < 9){ //Hotbar $this->inventory->setHotbarSlotIndex($item["Slot"], isset($item["TrueSlot"]) ? $item["TrueSlot"] : -1); }elseif($item["Slot"] >= 100 and $item["Slot"] < 104){ //Armor - $this->inventory->setItem($this->inventory->getSize() + $item["Slot"] - 100, ItemItem::get($item["id"], $item["Damage"], $item["Count"])); + $this->inventory->setItem($this->inventory->getSize() + $item["Slot"] - 100, NBT::getItemHelper($item)); }else{ - $this->inventory->setItem($item["Slot"] - 9, ItemItem::get($item["id"], $item["Damage"], $item["Count"])); + $this->inventory->setItem($item["Slot"] - 9, NBT::getItemHelper($item)); } } } @@ -136,16 +158,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ if($hotbarSlot !== -1){ $item = $this->inventory->getItem($hotbarSlot); if($item->getId() !== 0 and $item->getCount() > 0){ - $this->namedtag->Inventory[$slot] = new Compound("", [ - new Byte("Count", $item->getCount()), - new Short("Damage", $item->getDamage()), - new Byte("Slot", $slot), - new Byte("TrueSlot", $hotbarSlot), - new Short("id", $item->getId()), - ]); + $tag = NBT::putItemHelper($item, $slot); + $tag->TrueSlot = new Byte("TrueSlot", $hotbarSlot); + $this->namedtag->Inventory[$slot] = $tag; + continue; } } + $this->namedtag->Inventory[$slot] = new Compound("", [ new Byte("Count", 0), new Short("Damage", 0), @@ -160,24 +180,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ //$slotCount = (($this instanceof Player and ($this->gamemode & 0x01) === 1) ? Player::CREATIVE_SLOTS : Player::SURVIVAL_SLOTS) + 9; for($slot = 9; $slot < $slotCount; ++$slot){ $item = $this->inventory->getItem($slot - 9); - $this->namedtag->Inventory[$slot] = new Compound("", [ - new Byte("Count", $item->getCount()), - new Short("Damage", $item->getDamage()), - new Byte("Slot", $slot), - new Short("id", $item->getId()), - ]); + $this->namedtag->Inventory[$slot] = NBT::putItemHelper($item, $slot); } //Armor for($slot = 100; $slot < 104; ++$slot){ $item = $this->inventory->getItem($this->inventory->getSize() + $slot - 100); if($item instanceof ItemItem and $item->getId() !== ItemItem::AIR){ - $this->namedtag->Inventory[$slot] = new Compound("", [ - new Byte("Count", $item->getCount()), - new Short("Damage", $item->getDamage()), - new Byte("Slot", $slot), - new Short("id", $item->getId()), - ]); + $this->namedtag->Inventory[$slot] = NBT::putItemHelper($item, $slot); } } } @@ -185,7 +195,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ if(strlen($this->getSkinData()) > 0){ $this->namedtag->Skin = new Compound("Skin", [ "Data" => new String("Data", $this->getSkinData()), - "Slim" => new Byte("Slim", $this->isSkinSlim() ? 1 : 0) + "Name" => new String("Name", $this->getSkinName()) ]); } } @@ -198,8 +208,13 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ throw new \InvalidStateException((new \ReflectionClass($this))->getShortName() . " must have a valid skin set"); } + + if(!($this instanceof Player)){ + $this->server->updatePlayerListData($this->getUniqueId(), $this->getId(), $this->getName(), $this->skinName, $this->skin, [$player]); + } + $pk = new AddPlayerPacket(); - $pk->clientID = $this->getId(); + $pk->uuid = $this->getUniqueId(); $pk->username = $this->getName(); $pk->eid = $this->getId(); $pk->x = $this->x; @@ -210,24 +225,25 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; - $item = $this->getInventory()->getItemInHand(); - $pk->item = $item->getId(); - $pk->meta = $item->getDamage(); - $pk->skin = $this->skin; - $pk->slim = $this->isSlim; + $pk->item = $this->getInventory()->getItemInHand(); $pk->metadata = $this->dataProperties; - $player->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $player->dataPacket($pk); $this->inventory->sendArmorContents($player); + + if(!($this instanceof Player)){ + $this->server->removePlayerListData($this->getUniqueId(), [$player]); + } } } public function despawnFrom(Player $player){ if(isset($this->hasSpawned[$player->getLoaderId()])){ + $pk = new RemovePlayerPacket(); $pk->eid = $this->getId(); - $pk->clientID = $this->getId(); - $player->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $pk->clientId = $this->getUniqueId(); + $player->dataPacket($pk); unset($this->hasSpawned[$player->getLoaderId()]); } } diff --git a/src/pocketmine/entity/Item.php b/src/pocketmine/entity/Item.php index 0461c7c18..686a6ed4a 100644 --- a/src/pocketmine/entity/Item.php +++ b/src/pocketmine/entity/Item.php @@ -22,13 +22,14 @@ namespace pocketmine\entity; use pocketmine\event\entity\EntityDamageEvent; -use pocketmine\event\entity\EntityRegainHealthEvent; + use pocketmine\event\entity\ItemDespawnEvent; use pocketmine\event\entity\ItemSpawnEvent; use pocketmine\item\Item as ItemItem; -use pocketmine\math\Vector3; -use pocketmine\nbt\tag\Byte; -use pocketmine\nbt\tag\Compound; + +use pocketmine\nbt\NBT; + + use pocketmine\nbt\tag\Short; use pocketmine\nbt\tag\String; use pocketmine\network\Network; @@ -69,7 +70,11 @@ class Item extends Entity{ if(isset($this->namedtag->Thrower)){ $this->thrower = $this->namedtag["Thrower"]; } - $this->item = ItemItem::get($this->namedtag->Item["id"], $this->namedtag->Item["Damage"], $this->namedtag->Item["Count"]); + if(!isset($this->namedtag->Item)){ + $this->close(); + return; + } + $this->item = NBT::getItemHelper($this->namedtag->Item); $this->server->getPluginManager()->callEvent(new ItemSpawnEvent($this)); @@ -154,11 +159,7 @@ class Item extends Entity{ public function saveNBT(){ parent::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->Item = NBT::putItemHelper($this->item); $this->namedtag->Health = new Short("Health", $this->getHealth()); $this->namedtag->Age = new Short("Age", $this->age); $this->namedtag->PickupDelay = new Short("PickupDelay", $this->pickupDelay); @@ -233,7 +234,7 @@ class Item extends Entity{ $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->item = $this->getItem(); - $player->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $player->dataPacket($pk); $this->sendData($player); diff --git a/src/pocketmine/entity/Living.php b/src/pocketmine/entity/Living.php index 4bfe52ccf..99c5514ef 100644 --- a/src/pocketmine/entity/Living.php +++ b/src/pocketmine/entity/Living.php @@ -34,7 +34,7 @@ use pocketmine\math\Vector3; use pocketmine\nbt\tag\Short; use pocketmine\network\Network; use pocketmine\network\protocol\EntityEventPacket; -use pocketmine\Player; + use pocketmine\Server; use pocketmine\utils\BlockIterator; @@ -69,7 +69,7 @@ abstract class Living extends Entity implements Damageable{ $pk = new EntityEventPacket(); $pk->eid = $this->getId(); $pk->event = EntityEventPacket::RESPAWN; - Server::broadcastPacket($this->hasSpawned, $pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + Server::broadcastPacket($this->hasSpawned, $pk); } } @@ -127,7 +127,7 @@ abstract class Living extends Entity implements Damageable{ $pk = new EntityEventPacket(); $pk->eid = $this->getId(); $pk->event = $this->getHealth() <= 0 ? EntityEventPacket::DEATH_ANIMATION : EntityEventPacket::HURT_ANIMATION; //Ouch! - Server::broadcastPacket($this->hasSpawned, $pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + Server::broadcastPacket($this->hasSpawned, $pk); $this->attackTime = 10; //0.5 seconds cooldown } diff --git a/src/pocketmine/entity/PrimedTNT.php b/src/pocketmine/entity/PrimedTNT.php index eed361c5a..3eb546d9e 100644 --- a/src/pocketmine/entity/PrimedTNT.php +++ b/src/pocketmine/entity/PrimedTNT.php @@ -23,7 +23,7 @@ namespace pocketmine\entity; use pocketmine\event\entity\EntityDamageEvent; -use pocketmine\event\entity\EntityRegainHealthEvent; + use pocketmine\event\entity\ExplosionPrimeEvent; use pocketmine\level\Explosion; use pocketmine\nbt\tag\Byte; @@ -144,7 +144,7 @@ class PrimedTNT extends Entity implements Explosive{ $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->metadata = $this->dataProperties; - $player->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $player->dataPacket($pk); parent::spawnTo($player); } diff --git a/src/pocketmine/entity/Projectile.php b/src/pocketmine/entity/Projectile.php index d37c63c13..e37a418a6 100644 --- a/src/pocketmine/entity/Projectile.php +++ b/src/pocketmine/entity/Projectile.php @@ -26,7 +26,7 @@ use pocketmine\event\entity\EntityCombustByEntityEvent; use pocketmine\event\entity\EntityDamageByChildEntityEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; -use pocketmine\event\entity\EntityRegainHealthEvent; + use pocketmine\event\entity\ProjectileHitEvent; use pocketmine\level\format\FullChunk; use pocketmine\level\MovingObjectPosition; diff --git a/src/pocketmine/entity/Snowball.php b/src/pocketmine/entity/Snowball.php index 825d681f6..b35d590af 100644 --- a/src/pocketmine/entity/Snowball.php +++ b/src/pocketmine/entity/Snowball.php @@ -72,7 +72,7 @@ class Snowball extends Projectile{ $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->metadata = $this->dataProperties; - $player->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $player->dataPacket($pk); parent::spawnTo($player); } diff --git a/src/pocketmine/entity/Squid.php b/src/pocketmine/entity/Squid.php index d55bac496..4fb76dae0 100644 --- a/src/pocketmine/entity/Squid.php +++ b/src/pocketmine/entity/Squid.php @@ -68,7 +68,7 @@ class Squid extends WaterAnimal implements Ageable{ $pk = new EntityEventPacket(); $pk->eid = $this->getId(); $pk->event = EntityEventPacket::SQUID_INK_CLOUD; - Server::broadcastPacket($this->hasSpawned, $pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + Server::broadcastPacket($this->hasSpawned, $pk); } } @@ -160,7 +160,7 @@ class Squid extends WaterAnimal implements Ageable{ $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; - $player->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $player->dataPacket($pk); parent::spawnTo($player); } diff --git a/src/pocketmine/entity/Villager.php b/src/pocketmine/entity/Villager.php index a34e3b969..3aa458911 100644 --- a/src/pocketmine/entity/Villager.php +++ b/src/pocketmine/entity/Villager.php @@ -65,7 +65,7 @@ class Villager extends Creature implements NPC, Ageable{ $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; - $player->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $player->dataPacket($pk); parent::spawnTo($player); } diff --git a/src/pocketmine/entity/Zombie.php b/src/pocketmine/entity/Zombie.php index 300347818..1332b0edb 100644 --- a/src/pocketmine/entity/Zombie.php +++ b/src/pocketmine/entity/Zombie.php @@ -52,7 +52,7 @@ class Zombie extends Monster{ $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; - $player->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $player->dataPacket($pk); parent::spawnTo($player); } diff --git a/src/pocketmine/event/inventory/CraftItemEvent.php b/src/pocketmine/event/inventory/CraftItemEvent.php index 96eac6d43..b5c466bda 100644 --- a/src/pocketmine/event/inventory/CraftItemEvent.php +++ b/src/pocketmine/event/inventory/CraftItemEvent.php @@ -23,31 +23,42 @@ namespace pocketmine\event\inventory; use pocketmine\event\Cancellable; use pocketmine\event\Event; -use pocketmine\inventory\CraftingTransactionGroup; use pocketmine\inventory\Recipe; +use pocketmine\item\Item; +use pocketmine\Player; class CraftItemEvent extends Event implements Cancellable{ public static $handlerList = null; - /** @var CraftingTransactionGroup */ - private $ts; + /** @var Item[] */ + private $input = []; /** @var Recipe */ private $recipe; + /** @var \pocketmine\Player */ + private $player; + /** - * @param CraftingTransactionGroup $ts - * @param Recipe $recipe + * @param \pocketmine\Player $player + * @param Item[] $input + * @param Recipe $recipe */ - public function __construct(CraftingTransactionGroup $ts, Recipe $recipe){ - $this->ts = $ts; + public function __construct(Player $player, array $input, Recipe $recipe){ + $this->player = $player; + $this->input = $input; $this->recipe = $recipe; } /** - * @return CraftingTransactionGroup + * @return Item[] */ - public function getTransaction(){ - return $this->ts; + public function getInput(){ + $items = []; + foreach($items as $i => $item){ + $items[$i] = clone $item; + } + + return $items; } /** @@ -57,4 +68,10 @@ class CraftItemEvent extends Event implements Cancellable{ return $this->recipe; } -} \ No newline at end of file + /** + * @return \pocktemine\Player + */ + public function getPlayer(){ + return $this->player; + } +} diff --git a/src/pocketmine/event/player/PlayerToggleSneakEvent.php b/src/pocketmine/event/player/PlayerToggleSneakEvent.php new file mode 100644 index 000000000..d75decc2a --- /dev/null +++ b/src/pocketmine/event/player/PlayerToggleSneakEvent.php @@ -0,0 +1,42 @@ +player = $player; + $this->isSneaking = (bool) $isSneaking; + } + + public function isSneaking(){ + return $this->isSneaking; + } + +} \ No newline at end of file diff --git a/src/pocketmine/event/player/PlayerToggleSprintEvent.php b/src/pocketmine/event/player/PlayerToggleSprintEvent.php new file mode 100644 index 000000000..a7f17a8ad --- /dev/null +++ b/src/pocketmine/event/player/PlayerToggleSprintEvent.php @@ -0,0 +1,42 @@ +player = $player; + $this->isSprinting = (bool) $isSprinting; + } + + public function isSprinting(){ + return $this->isSprinting; + } + +} \ No newline at end of file diff --git a/src/pocketmine/inventory/AnvilInventory.php b/src/pocketmine/inventory/AnvilInventory.php new file mode 100644 index 000000000..b0e0115a7 --- /dev/null +++ b/src/pocketmine/inventory/AnvilInventory.php @@ -0,0 +1,47 @@ +holder; + } + + public function onClose(Player $who){ + parent::onClose($who); + + for($i = 0; $i < 2; ++$i){ + $this->getHolder()->getLevel()->dropItem($this->getHolder()->add(0.5, 0.5, 0.5), $this->getItem($i)); + $this->clear($i); + } + } +} \ No newline at end of file diff --git a/src/pocketmine/inventory/BaseInventory.php b/src/pocketmine/inventory/BaseInventory.php index 2e1d73ded..dee23dd9c 100644 --- a/src/pocketmine/inventory/BaseInventory.php +++ b/src/pocketmine/inventory/BaseInventory.php @@ -160,8 +160,9 @@ abstract class BaseInventory implements Inventory{ public function contains(Item $item){ $count = max(1, $item->getCount()); $checkDamage = $item->getDamage() === null ? false : true; + $checkTags = $item->getCompoundTag() === null ? false : true; foreach($this->getContents() as $i){ - if($item->equals($i, $checkDamage)){ + if($item->equals($i, $checkDamage, $checkTags)){ $count -= $i->getCount(); if($count <= 0){ return true; @@ -175,8 +176,9 @@ abstract class BaseInventory implements Inventory{ public function all(Item $item){ $slots = []; $checkDamage = $item->getDamage() === null ? false : true; + $checkTags = $item->getCompoundTag() === null ? false : true; foreach($this->getContents() as $index => $i){ - if($item->equals($i, $checkDamage)){ + if($item->equals($i, $checkDamage, $checkTags)){ $slots[$index] = $i; } } @@ -186,8 +188,10 @@ abstract class BaseInventory implements Inventory{ public function remove(Item $item){ $checkDamage = $item->getDamage() === null ? false : true; + $checkTags = $item->getCompoundTag() === null ? false : true; + foreach($this->getContents() as $index => $i){ - if($item->equals($i, $checkDamage)){ + if($item->equals($i, $checkDamage, $checkTags)){ $this->clear($index); } } @@ -196,8 +200,10 @@ abstract class BaseInventory implements Inventory{ public function first(Item $item){ $count = max(1, $item->getCount()); $checkDamage = $item->getDamage() === null ? false : true; + $checkTags = $item->getCompoundTag() === null ? false : true; + foreach($this->getContents() as $index => $i){ - if($item->equals($i, $checkDamage) and $i->getCount() >= $count){ + if($item->equals($i, $checkDamage, $checkTags) and $i->getCount() >= $count){ return $index; } } @@ -218,9 +224,10 @@ abstract class BaseInventory implements Inventory{ public function canAddItem(Item $item){ $item = clone $item; $checkDamage = $item->getDamage() === null ? false : true; + $checkTags = $item->getCompoundTag() === null ? false : true; for($i = 0; $i < $this->getSize(); ++$i){ $slot = $this->getItem($i); - if($item->equals($slot, $checkDamage)){ + if($item->equals($slot, $checkDamage, $checkTags)){ if(($diff = $slot->getMaxStackSize() - $slot->getCount()) > 0){ $item->setCount($item->getCount() - $diff); } @@ -258,7 +265,7 @@ abstract class BaseInventory implements Inventory{ } foreach($itemSlots as $index => $slot){ - if($slot->equals($item, true) and $item->getCount() < $item->getMaxStackSize()){ + if($slot->equals($item) and $item->getCount() < $item->getMaxStackSize()){ $amount = min($item->getMaxStackSize() - $item->getCount(), $slot->getCount(), $this->getMaxStackSize()); if($amount > 0){ $slot->setCount($slot->getCount() - $amount); @@ -316,7 +323,7 @@ abstract class BaseInventory implements Inventory{ } foreach($itemSlots as $index => $slot){ - if($slot->equals($item, $slot->getDamage() === null ? false : true)){ + if($slot->equals($item, $slot->getDamage() === null ? false : true, $slot->getCompoundTag() === null ? false : true)){ $amount = min($item->getCount(), $slot->getCount()); $slot->setCount($slot->getCount() - $amount); $item->setCount($item->getCount() - $amount); @@ -428,7 +435,7 @@ abstract class BaseInventory implements Inventory{ continue; } $pk->windowid = $id; - $player->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $player->dataPacket($pk); } } @@ -451,7 +458,7 @@ abstract class BaseInventory implements Inventory{ continue; } $pk->windowid = $id; - $player->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $player->dataPacket($pk); } } diff --git a/src/pocketmine/inventory/StonecutterShapelessRecipe.php b/src/pocketmine/inventory/BigShapedRecipe.php similarity index 92% rename from src/pocketmine/inventory/StonecutterShapelessRecipe.php rename to src/pocketmine/inventory/BigShapedRecipe.php index e29e34c86..927023ed2 100644 --- a/src/pocketmine/inventory/StonecutterShapelessRecipe.php +++ b/src/pocketmine/inventory/BigShapedRecipe.php @@ -21,6 +21,6 @@ namespace pocketmine\inventory; -class StonecutterShapelessRecipe extends ShapelessRecipe{ +class BigShapedRecipe extends ShapedRecipe{ } \ No newline at end of file diff --git a/src/pocketmine/inventory/ChestInventory.php b/src/pocketmine/inventory/ChestInventory.php index a2f8447c6..20cf04fe1 100644 --- a/src/pocketmine/inventory/ChestInventory.php +++ b/src/pocketmine/inventory/ChestInventory.php @@ -23,9 +23,9 @@ namespace pocketmine\inventory; use pocketmine\level\Level; use pocketmine\network\Network; -use pocketmine\network\protocol\TileEventPacket; +use pocketmine\network\protocol\BlockEventPacket; use pocketmine\Player; -use pocketmine\Server; + use pocketmine\tile\Chest; class ChestInventory extends ContainerInventory{ @@ -44,30 +44,30 @@ class ChestInventory extends ContainerInventory{ parent::onOpen($who); if(count($this->getViewers()) === 1){ - $pk = new TileEventPacket(); + $pk = new BlockEventPacket(); $pk->x = $this->getHolder()->getX(); $pk->y = $this->getHolder()->getY(); $pk->z = $this->getHolder()->getZ(); $pk->case1 = 1; $pk->case2 = 2; if(($level = $this->getHolder()->getLevel()) instanceof Level){ - $level->addChunkPacket($this->getHolder()->getX() >> 4, $this->getHolder()->getZ() >> 4, $pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $level->addChunkPacket($this->getHolder()->getX() >> 4, $this->getHolder()->getZ() >> 4, $pk); } } } public function onClose(Player $who){ if(count($this->getViewers()) === 1){ - $pk = new TileEventPacket(); + $pk = new BlockEventPacket(); $pk->x = $this->getHolder()->getX(); $pk->y = $this->getHolder()->getY(); $pk->z = $this->getHolder()->getZ(); $pk->case1 = 1; $pk->case2 = 0; if(($level = $this->getHolder()->getLevel()) instanceof Level){ - $level->addChunkPacket($this->getHolder()->getX() >> 4, $this->getHolder()->getZ() >> 4, $pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $level->addChunkPacket($this->getHolder()->getX() >> 4, $this->getHolder()->getZ() >> 4, $pk); } } parent::onClose($who); } -} \ No newline at end of file +} diff --git a/src/pocketmine/inventory/ContainerInventory.php b/src/pocketmine/inventory/ContainerInventory.php index c27dfcdda..72054d006 100644 --- a/src/pocketmine/inventory/ContainerInventory.php +++ b/src/pocketmine/inventory/ContainerInventory.php @@ -43,7 +43,7 @@ abstract class ContainerInventory extends BaseInventory{ $pk->x = $pk->y = $pk->z = 0; } - $who->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $who->dataPacket($pk); $this->sendContents($who); } @@ -51,7 +51,7 @@ abstract class ContainerInventory extends BaseInventory{ public function onClose(Player $who){ $pk = new ContainerClosePacket(); $pk->windowid = $who->getWindowId($this); - $who->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $who->dataPacket($pk); parent::onClose($who); } } \ No newline at end of file diff --git a/src/pocketmine/inventory/CraftingManager.php b/src/pocketmine/inventory/CraftingManager.php index 75d81bab8..aac0d350d 100644 --- a/src/pocketmine/inventory/CraftingManager.php +++ b/src/pocketmine/inventory/CraftingManager.php @@ -21,11 +21,19 @@ namespace pocketmine\inventory; + use pocketmine\block\Planks; +use pocketmine\block\Quartz; +use pocketmine\block\Sandstone; +use pocketmine\block\Slab; +use pocketmine\block\Fence; use pocketmine\block\Stone; +use pocketmine\block\StoneBricks; +use pocketmine\block\StoneWall; use pocketmine\block\Wood; use pocketmine\block\Wood2; use pocketmine\item\Item; +use pocketmine\utils\UUID; class CraftingManager{ @@ -38,6 +46,8 @@ class CraftingManager{ /** @var FurnaceRecipe[] */ public $furnaceRecipes = []; + private static $RECIPE_COUNT = 0; + public function __construct(){ $this->registerStonecutter(); @@ -52,70 +62,300 @@ class CraftingManager{ $this->registerFood(); $this->registerRecipe((new ShapelessRecipe(Item::get(Item::CLAY_BLOCK, 0, 1)))->addIngredient(Item::get(Item::CLAY, 0, 4))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::WORKBENCH, 0, 1)))->addIngredient(Item::get(Item::WOODEN_PLANK, null, 4))); + + $this->registerRecipe((new ShapedRecipe(Item::get(Item::WORKBENCH, 0, 1), + "XX", + "XX" + ))->setIngredient("X", Item::get(Item::WOODEN_PLANK, null))); + $this->registerRecipe((new ShapelessRecipe(Item::get(Item::GLOWSTONE_BLOCK, 0, 1)))->addIngredient(Item::get(Item::GLOWSTONE_DUST, 0, 4))); $this->registerRecipe((new ShapelessRecipe(Item::get(Item::LIT_PUMPKIN, 0, 1)))->addIngredient(Item::get(Item::PUMPKIN, 0, 1))->addIngredient(Item::get(Item::TORCH, 0, 1))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::SNOW_BLOCK, 0, 1)))->addIngredient(Item::get(Item::SNOWBALL, 0, 4))); + + $this->registerRecipe((new ShapedRecipe(Item::get(Item::SNOW_BLOCK, 0, 1), + "XX", + "XX" + ))->setIngredient("X", Item::get(Item::SNOWBALL))); + $this->registerRecipe((new ShapelessRecipe(Item::get(Item::SNOW_LAYER, 0, 6)))->addIngredient(Item::get(Item::SNOW_BLOCK, 0, 3))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::STICK, 0, 4)))->addIngredient(Item::get(Item::WOODEN_PLANK, null, 2))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::STONECUTTER, 0, 1)))->addIngredient(Item::get(Item::COBBLESTONE, 0, 4))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOODEN_PLANK, Planks::OAK, 4)))->addIngredient(Item::get(Item::WOOD, Wood::OAK, 1))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOODEN_PLANK, Planks::SPRUCE, 4)))->addIngredient(Item::get(Item::WOOD, Wood::SPRUCE, 1))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOODEN_PLANK, Planks::BIRCH, 4)))->addIngredient(Item::get(Item::WOOD, Wood::BIRCH, 1))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOODEN_PLANK, Planks::JUNGLE, 4)))->addIngredient(Item::get(Item::WOOD, Wood::JUNGLE, 1))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOODEN_PLANK, Planks::ACACIA, 4)))->addIngredient(Item::get(Item::WOOD2, Wood2::ACACIA, 1))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOODEN_PLANK, Planks::DARK_OAK, 4)))->addIngredient(Item::get(Item::WOOD2, Wood2::DARK_OAK, 1))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOOL, 0, 1)))->addIngredient(Item::get(Item::STRING, 0, 4))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::TORCH, 0, 4)))->addIngredient(Item::get(Item::COAL, 0, 1))->addIngredient(Item::get(Item::STICK, 0, 1))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::TORCH, 0, 4)))->addIngredient(Item::get(Item::COAL, 1, 1))->addIngredient(Item::get(Item::STICK, 0, 1))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::SUGAR, 0, 1)))->addIngredient(Item::get(Item::SUGARCANE, 0, 1))); + $this->registerRecipe((new ShapedRecipe(Item::get(Item::STICK, 0, 4), + "X ", + "X " + ))->setIngredient("X", Item::get(Item::WOODEN_PLANK, null))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::BED, 0, 1)))->addIngredient(Item::get(Item::WOOL, null, 3))->addIngredient(Item::get(Item::WOODEN_PLANK, null, 3))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::CHEST, 0, 1)))->addIngredient(Item::get(Item::WOODEN_PLANK, null, 8))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE, 0, 3)))->addIngredient(Item::get(Item::STICK, 0, 2))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::OAK, 4))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE, Planks::SPRUCE, 3)))->addIngredient(Item::get(Item::STICK, 0, 2))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::SPRUCE, 4))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE, Planks::BIRCH, 3)))->addIngredient(Item::get(Item::STICK, 0, 2))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::BIRCH, 4))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE, Planks::JUNGLE, 3)))->addIngredient(Item::get(Item::STICK, 0, 2))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::JUNGLE, 4))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE, Planks::ACACIA, 3)))->addIngredient(Item::get(Item::STICK, 0, 2))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::ACACIA, 4))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE, Planks::DARK_OAK, 3)))->addIngredient(Item::get(Item::STICK, 0, 2))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::DARK_OAK, 4))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::OAK, 2))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_SPRUCE, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::SPRUCE, 2))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_BIRCH, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::BIRCH, 2))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_JUNGLE, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::JUNGLE, 2))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_DARK_OAK, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::DARK_OAK, 2))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_ACACIA, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::ACACIA, 2))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FURNACE, 0, 1)))->addIngredient(Item::get(Item::COBBLESTONE, 0, 8))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::GLASS_PANE, 0, 16)))->addIngredient(Item::get(Item::GLASS, 0, 6))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::LADDER, 0, 2)))->addIngredient(Item::get(Item::STICK, 0, 7))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::NETHER_REACTOR, 0, 1)))->addIngredient(Item::get(Item::DIAMOND, 0, 3))->addIngredient(Item::get(Item::IRON_INGOT, 0, 6))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::TRAPDOOR, 0, 2)))->addIngredient(Item::get(Item::WOODEN_PLANK, null, 6))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::WOODEN_DOOR, 0, 1)))->addIngredient(Item::get(Item::WOODEN_PLANK, null, 6))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::WOODEN_STAIRS, 0, 4)))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::OAK, 6))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::WOOD_SLAB, Planks::OAK, 6)))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::OAK, 3))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::SPRUCE_WOOD_STAIRS, 0, 4)))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::SPRUCE, 6))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::WOOD_SLAB, Planks::SPRUCE, 6)))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::SPRUCE, 3))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::BIRCH_WOOD_STAIRS, 0, 4)))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::BIRCH, 6))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::WOOD_SLAB, Planks::BIRCH, 6)))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::BIRCH, 3))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::JUNGLE_WOOD_STAIRS, 0, 4)))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::JUNGLE, 6))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::WOOD_SLAB, Planks::JUNGLE, 6)))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::JUNGLE, 3))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::ACACIA_WOOD_STAIRS, 0, 4)))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::ACACIA, 6))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::WOOD_SLAB, Planks::ACACIA, 6)))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::ACACIA, 3))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::DARK_OAK_WOOD_STAIRS, 0, 4)))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::DARK_OAK, 6))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::WOOD_SLAB, Planks::DARK_OAK, 6)))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::DARK_OAK, 3))); + $this->registerRecipe((new ShapedRecipe(Item::get(Item::STONECUTTER, 0, 1), + "XX", + "XX" + ))->setIngredient("X", Item::get(Item::COBBLESTONE))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::BUCKET, 0, 1)))->addIngredient(Item::get(Item::IRON_INGOT, 0, 3))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::CLOCK, 0, 1)))->addIngredient(Item::get(Item::GOLD_INGOT, 0, 4))->addIngredient(Item::get(Item::REDSTONE_DUST, 0, 1))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::COMPASS, 0, 1)))->addIngredient(Item::get(Item::IRON_INGOT, 0, 4))->addIngredient(Item::get(Item::REDSTONE_DUST, 0, 1))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::TNT, 0, 1)))->addIngredient(Item::get(Item::GUNPOWDER, 0, 5))->addIngredient(Item::get(Item::SAND, null, 4))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::BOWL, 0, 4)))->addIngredient(Item::get(Item::WOODEN_PLANKS, null, 3))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::MINECART, 0, 1)))->addIngredient(Item::get(Item::IRON_INGOT, 0, 5))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::BOOK, 0, 1)))->addIngredient(Item::get(Item::PAPER, 0, 3))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::BOOKSHELF, 0, 1)))->addIngredient(Item::get(Item::WOODEN_PLANK, null, 6))->addIngredient(Item::get(Item::BOOK, 0, 3))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::PAINTING, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 8))->addIngredient(Item::get(Item::WOOL, null, 1))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::PAPER, 0, 1)))->addIngredient(Item::get(Item::SUGARCANE, 0, 3))); - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::SIGN, 0, 3)))->addIngredient(Item::get(Item::STICK, 0, 1))->addIngredient(Item::get(Item::WOODEN_PLANKS, null, 6))); //TODO: check if it gives one sign or three - $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::IRON_BARS, 0, 16)))->addIngredient(Item::get(Item::IRON_INGOT, 0, 6))); + $this->registerRecipe((new ShapedRecipe(Item::get(Item::WOODEN_PLANK, Planks::OAK, 4), + "X" + ))->setIngredient("X", Item::get(Item::WOOD, Wood::OAK, 1))); + + $this->registerRecipe((new ShapedRecipe(Item::get(Item::WOODEN_PLANK, Planks::SPRUCE, 4), + "X" + ))->setIngredient("X", Item::get(Item::WOOD, Wood::SPRUCE, 1))); + + $this->registerRecipe((new ShapedRecipe(Item::get(Item::WOODEN_PLANK, Planks::BIRCH, 4), + "X" + ))->setIngredient("X", Item::get(Item::WOOD, Wood::BIRCH, 1))); + + $this->registerRecipe((new ShapedRecipe(Item::get(Item::WOODEN_PLANK, Planks::JUNGLE, 4), + "X" + ))->setIngredient("X", Item::get(Item::WOOD, Wood::JUNGLE, 1))); + + $this->registerRecipe((new ShapedRecipe(Item::get(Item::WOODEN_PLANK, Planks::ACACIA, 4), + "X" + ))->setIngredient("X", Item::get(Item::WOOD2, Wood2::ACACIA, 1))); + + $this->registerRecipe((new ShapedRecipe(Item::get(Item::WOODEN_PLANK, Planks::DARK_OAK, 4), + "X" + ))->setIngredient("X", Item::get(Item::WOOD2, Wood2::DARK_OAK, 1))); + + $this->registerRecipe((new ShapedRecipe(Item::get(Item::WOOL, 0, 1), + "XX", + "XX" + ))->setIngredient("X", Item::get(Item::STRING, 0, 4))); + + $this->registerRecipe((new ShapedRecipe(Item::get(Item::TORCH, 0, 4), + "C ", + "S" + ))->setIngredient("C", Item::get(Item::COAL,0,1))->setIngredient("S", Item::get(Item::STICK,0,1))); + + $this->registerRecipe((new ShapedRecipe(Item::get(Item::TORCH, 0, 4), + "C ", + "S" + ))->setIngredient("C", Item::get(Item::COAL, 1, 1))->setIngredient("S", Item::get(Item::STICK, 0, 1))); + + $this->registerRecipe((new ShapedRecipe(Item::get(Item::SUGAR, 0, 1), + "S" + ))->setIngredient("S", Item::get(Item::SUGARCANE, 0, 1))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::BED, 0, 1), + "WWW", + "PPP" + ))->setIngredient("W", Item::get(Item::WOOL, null, 3))->setIngredient("P", Item::get(Item::WOODEN_PLANK, null, 3))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::CHEST, 0, 1), + "PPP", + "P P", + "PPP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, null, 8))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::FENCE, 0, 3), + "PSP", + "PSP" + ))->setIngredient("S", Item::get(Item::STICK, 0, 2))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::OAK, 4))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::FENCE, Planks::SPRUCE, 3), + "PSP", + "PSP" + ))->setIngredient("S", Item::get(Item::STICK, 0, 2))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::SPRUCE, 4))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::FENCE, Planks::BIRCH, 3), + "PSP", + "PSP" + ))->setIngredient("S", Item::get(Item::STICK, 0, 2))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::BIRCH, 4))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::FENCE, Planks::JUNGLE, 3), + "PSP", + "PSP" + ))->setIngredient("S", Item::get(Item::STICK, 0, 2))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::JUNGLE, 4))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::FENCE, Planks::ACACIA, 3), + "PSP", + "PSP" + ))->setIngredient("S", Item::get(Item::STICK, 0, 2))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::ACACIA, 4))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::FENCE, Planks::DARK_OAK, 3), + "PSP", + "PSP" + ))->setIngredient("S", Item::get(Item::STICK, 0, 2))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::DARK_OAK, 4))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::FENCE_GATE, 0, 1), + "SPS", + "SPS" + ))->setIngredient("S", Item::get(Item::STICK, 0, 4))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::OAK, 2))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::FENCE_GATE_SPRUCE, 0, 1), + "SPS", + "SPS" + ))->setIngredient("S", Item::get(Item::STICK, 0, 4))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::SPRUCE, 2))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::FENCE_GATE_BIRCH, 0, 1), + "SPS", + "SPS" + ))->setIngredient("S", Item::get(Item::STICK, 0, 4))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::BIRCH, 2))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::FENCE_GATE_JUNGLE, 0, 1), + "SPS", + "SPS" + ))->setIngredient("S", Item::get(Item::STICK, 0, 4))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::JUNGLE, 2))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::FENCE_GATE_DARK_OAK, 0, 1), + "SPS", + "SPS" + ))->setIngredient("S", Item::get(Item::STICK, 0, 4))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::DARK_OAK, 2))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::FENCE_GATE_ACACIA, 0, 1), + "SPS", + "SPS" + ))->setIngredient("S", Item::get(Item::STICK, 0, 4))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::ACACIA, 2))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::FURNACE, 0, 1), + "CCC", + "C C", + "CCC" + ))->setIngredient("C", Item::get(Item::COBBLESTONE, 0, 8))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::GLASS_PANE, 0, 16), + "GGG", + "GGG" + ))->setIngredient("G", Item::get(Item::GLASS, 0, 6))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::LADDER, 0, 2), + "S S", + "SSS", + "S S" + ))->setIngredient("S", Item::get(Item::STICK, 0, 7))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::TRAPDOOR, 0, 2), + "PPP", + "PPP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, null, 6))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::WOODEN_DOOR, 0, 1), + "PP", + "PP", + "PP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, null, 6))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::WOODEN_STAIRS, 0, 4), + " P", + " PP", + "PPP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::OAK, 6))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::WOOD_SLAB, Planks::OAK, 6), + "PPP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::OAK, 3))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::SPRUCE_WOOD_STAIRS, 0, 4), + " P", + " PP", + "PPP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::SPRUCE, 6))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::WOOD_SLAB, Planks::SPRUCE, 6), + "PPP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::SPRUCE, 3))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::BIRCH_WOOD_STAIRS, 0, 4), + " P", + " PP", + "PPP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::BIRCH, 6))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::WOOD_SLAB, Planks::BIRCH, 6), + "PPP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::BIRCH, 3))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::JUNGLE_WOOD_STAIRS, 0, 4), + "P", + "PP", + "PPP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::JUNGLE, 6))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::WOOD_SLAB, Planks::JUNGLE, 6), + "PPP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::JUNGLE, 3))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::ACACIA_WOOD_STAIRS, 0, 4), + " P", + " PP", + "PPP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::ACACIA, 6))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::WOOD_SLAB, Planks::ACACIA, 6), + "PPP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::ACACIA, 3))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::DARK_OAK_WOOD_STAIRS, 0, 4), + " P", + " PP", + "PPP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::DARK_OAK, 6))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::WOOD_SLAB, Planks::DARK_OAK, 6), + "PPP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, Planks::DARK_OAK, 3))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::BUCKET, 0, 1), + "I I", + " I" + ))->setIngredient("I", Item::get(Item::IRON_INGOT, 0, 3))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::CLOCK, 0, 1), + " G", + "GR", + " G" + ))->setIngredient("G", Item::get(Item::GOLD_INGOT, 0, 4))->setIngredient("R", Item::get(Item::REDSTONE_DUST, 0, 1))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::COMPASS, 0, 1), + " I ", + "IRI", + " I" + ))->setIngredient("I", Item::get(Item::IRON_INGOT, 0, 4))->setIngredient("R", Item::get(Item::REDSTONE_DUST, 0, 1))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::TNT, 0, 1), + "GSG", + "SGS", + "GSG" + ))->setIngredient("G", Item::get(Item::GUNPOWDER, 0, 5))->setIngredient("S", Item::get(Item::SAND, null, 4))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::BOWL, 0, 4), + "P P", + " P" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANKS, null, 3))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::MINECART, 0, 1), + "I I", + "III" + ))->setIngredient("I", Item::get(Item::IRON_INGOT, 0, 5))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::BOOK, 0, 1), + "P P", + " P " + ))->setIngredient("P", Item::get(Item::PAPER, 0, 3))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::BOOKSHELF, 0, 1), + "PBP", + "PBP", + "PBP" + ))->setIngredient("P", Item::get(Item::WOODEN_PLANK, null, 6))->setIngredient("B", Item::get(Item::BOOK, 0, 3))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::PAINTING, 0, 1), + "SSS", + "SWS", + "SSS" + ))->setIngredient("S", Item::get(Item::STICK, 0, 8))->setIngredient("W", Item::get(Item::WOOL, null, 1))); + + $this->registerRecipe((new ShapedRecipe(Item::get(Item::PAPER, 0, 3), + "SS", + "S" + ))->setIngredient("S", Item::get(Item::SUGARCANE, 0, 3))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::SIGN, 0, 3), + "PPP", + "PPP", + " S" + ))->setIngredient("S", Item::get(Item::STICK, 0, 1))->setIngredient("P", Item::get(Item::WOODEN_PLANKS, null, 6))); //TODO: check if it gives one sign or three + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::IRON_BARS, 0, 16), + "III", + "III", + "III" + ))->setIngredient("I", Item::get(Item::IRON_INGOT, 0, 9))); } protected function registerFurnace(){ @@ -141,38 +381,142 @@ class CraftingManager{ $this->registerRecipe(new FurnaceRecipe(Item::get(Item::HARDENED_CLAY, 0, 1), Item::get(Item::CLAY_BLOCK, 0, 1))); } - protected function registerStonecutter(){ - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::QUARTZ_BLOCK, 0, 1)))->addIngredient(Item::get(Item::QUARTZ, 0, 4))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::BRICK_STAIRS, 0, 4)))->addIngredient(Item::get(Item::BRICKS_BLOCK, 0, 6))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::BRICKS_BLOCK, 0, 1)))->addIngredient(Item::get(Item::BRICK, 0, 4))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::SLAB, 4, 6)))->addIngredient(Item::get(Item::BRICKS_BLOCK, 0, 3))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::QUARTZ_BLOCK, 1, 1)))->addIngredient(Item::get(Item::SLAB, 6, 2))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::SLAB, 3, 6)))->addIngredient(Item::get(Item::COBBLESTONE, 0, 3))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::COBBLESTONE_WALL, 0, 6)))->addIngredient(Item::get(Item::COBBLESTONE, 0, 6))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::COBBLESTONE_WALL, 1, 6)))->addIngredient(Item::get(Item::MOSS_STONE, 0, 6))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::NETHER_BRICKS, 0, 1)))->addIngredient(Item::get(Item::NETHER_BRICK, 0, 4))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::NETHER_BRICKS_STAIRS, 0, 4)))->addIngredient(Item::get(Item::NETHER_BRICKS, 0, 6))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::QUARTZ_BLOCK, 2, 2)))->addIngredient(Item::get(Item::QUARTZ_BLOCK, 0, 2))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::SLAB, 6, 6)))->addIngredient(Item::get(Item::QUARTZ_BLOCK, 0, 3))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::SANDSTONE_STAIRS, 0, 4)))->addIngredient(Item::get(Item::SANDSTONE, 0, 6))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::SANDSTONE, 0, 1)))->addIngredient(Item::get(Item::SAND, 0, 4))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::SANDSTONE, 2, 4)))->addIngredient(Item::get(Item::SANDSTONE, 0, 4))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::SANDSTONE, 1, 1)))->addIngredient(Item::get(Item::SLAB, 1, 2))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::SLAB, 1, 6)))->addIngredient(Item::get(Item::SANDSTONE, 0, 3))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::STONE_BRICK_STAIRS, 0, 4)))->addIngredient(Item::get(Item::STONE_BRICK, null, 6))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::STONE_BRICK, 0, 4)))->addIngredient(Item::get(Item::STONE, null, 4))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::STONE_BRICK, 3, 1)))->addIngredient(Item::get(Item::SLAB, 5, 2))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::STONE_BRICK, 1, 1)))->addIngredient(Item::get(Item::STONE_BRICK, 0, 1))->addIngredient(Item::get(Item::VINES, 0, 1))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::SLAB, 5, 6)))->addIngredient(Item::get(Item::STONE_BRICK, null, 3))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::SLAB, 0, 6)))->addIngredient(Item::get(Item::STONE, null, 3))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::COBBLESTONE_STAIRS, 0, 4)))->addIngredient(Item::get(Item::COBBLESTONE, 0, 6))); + protected function registerStonecutter(){ + $shapes = [ + "slab" => [ + " ", + "XXX", + " " + ], + "stairs" => [ + "X ", + "XX ", + "XXX" + ], + "wall/fence" => [ + "XXX", + "XXX", + " " + ], + "blockrecipe1" => [ + "XX", + "XX" + ], + "blockrecipe2X1" => [ + " ", + " X ", + " X " + ], + "blockrecipe2X2" => [ + "AB", + "BA" + ], + "blockrecipe1X2" => [ + " ", + "AB" + ] + ]; - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::STONE, Stone::POLISHED_GRANITE, 4)))->addIngredient(Item::get(Item::STONE, Stone::GRANITE, 4))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::STONE, Stone::POLISHED_DIORITE, 4)))->addIngredient(Item::get(Item::STONE, Stone::DIORITE, 4))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::STONE, Stone::POLISHED_ANDESITE, 4)))->addIngredient(Item::get(Item::STONE, Stone::ANDESITE, 4))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::STONE, Stone::GRANITE, 1)))->addIngredient(Item::get(Item::STONE, Stone::DIORITE, 1))->addIngredient(Item::get(Item::QUARTZ, 0, 1))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::STONE, Stone::DIORITE, 2)))->addIngredient(Item::get(Item::COBBLESTONE, 0, 2))->addIngredient(Item::get(Item::QUARTZ, 0, 2))); - $this->registerRecipe((new StonecutterShapelessRecipe(Item::get(Item::STONE, Stone::ANDESITE, 2)))->addIngredient(Item::get(Item::COBBLESTONE, 0, 1))->addIngredient(Item::get(Item::STONE, Stone::DIORITE, 1))); + $buildRecipes = []; + + // Single ingedient stone cutter recipes: + $RESULT_ITEMID = 0; $RESULT_META = 1; $INGREDIENT_ITEMID = 2; $INGREDIENT_META = 3; $RECIPE_SHAPE = 4;$RESULT_AMOUNT = 5; + $recipes = [ + //RESULT_ITEM_ID RESULT_META INGREDIENT_ITEMID INGREDIENT_META RECIPE_SHAPE RESULT_AMOUNT + [Item::SLAB, Slab::STONE, Item::STONE, Stone::NORMAL, "slab", 6], + [Item::SLAB, Slab::COBBLESTONE, Item::COBBLESTONE, 0, "slab", 6], + [Item::SLAB, Slab::SANDSTONE, Item::SANDSTONE, 0, "slab", 6], + [Item::SLAB, Slab::BRICK, Item::BRICK, 0, "slab", 6], + [Item::SLAB, Slab::STONE_BRICK, Item::STONE_BRICK, StoneBricks::NORMAL,"slab", 6], + [Item::SLAB, Slab::NETHER_BRICK, Item::NETHER_BRICK_BLOCK, 0, "slab", 6], + [Item::SLAB, Slab::QUARTZ, Item::QUARTZ_BLOCK, 0, "slab", 6], + [Item::COBBLESTONE_STAIRS, 0, Item::COBBLESTONE, 0, "stairs", 4], + [Item::SANDSTONE_STAIRS, 0, Item::SANDSTONE, 0, "stairs", 4], + [Item::STONE_BRICK_STAIRS, 0, Item::STONE_BRICK, StoneBricks::NORMAL,"stairs", 4], + [Item::BRICK_STAIRS, 0, Item::BRICKS_BLOCK, 0, "stairs", 4], + [Item::NETHER_BRICKS_STAIRS,0, Item::NETHER_BRICK_BLOCK, 0, "stairs", 4], + [Item::COBBLESTONE_WALL, StoneWall::NONE_MOSSY_WALL, Item::COBBLESTONE, 0, "wall/fence", 6], + [Item::COBBLESTONE_WALL, StoneWall::MOSSY_WALL, Item::MOSSY_STONE, 0, "wall/fence", 6], + [Item::NETHER_BRICK_FENCE, 0, Item::NETHER_BRICK_BLOCK, 0, "wall/fence", 6], + [Item::NETHER_BRICKS, 0, Item::NETHER_BRICK, 0, "blockrecipe1", 1], + [Item::SANDSTONE, SandStone::NORMAL, Item::SAND, 0, "blockrecipe1", 1], + [Item::SANDSTONE, Sandstone::CHISELED, Item::SANDSTONE, SandStone::NORMAL, "blockrecipe1", 4], + [Item::STONE_BRICK, StoneBricks::NORMAL, Item::STONE, Stone::NORMAL, "blockrecipe1", 4], + [Item::STONE_BRICK, StoneBricks::NORMAL, Item::STONE, Stone::POLISHED_GRANITE,"blockrecipe1", 4], + [Item::STONE_BRICK, StoneBricks::NORMAL, Item::STONE, Stone::POLISHED_DIORITE,"blockrecipe1", 4], + [Item::STONE_BRICK, StoneBricks::NORMAL, Item::STONE, Stone::POLISHED_ANDESITE,"blockrecipe1",4], + [Item::STONE, Stone::POLISHED_GRANITE, Item::STONE, Stone::GRANITE, "blockrecipe1", 4], + [Item::STONE, Stone::POLISHED_DIORITE, Item::STONE, Stone::DIORITE, "blockrecipe1", 4], + [Item::STONE, Stone::POLISHED_ANDESITE, Item::STONE, Stone::ANDESITE, "blockrecipe1", 4], + [Item::QUARTZ_BLOCK, Quartz::QUARTZ_NORMAL, Item::QUARTZ, Stone::ANDESITE, "blockrecipe1", 4], + [Item::QUARTZ_BLOCK, Quartz::QUARTZ_CHISELED, Item::SLAB, Slab::QUARTZ, "blockrecipe2X1", 1], + [Item::SANDSTONE, SandStone::CHISELED, Item::SLAB, Slab::SANDSTONE, "blockrecipe2X1", 1], + [Item::STONE_BRICK, StoneBricks::CHISELED, Item::SLAB, Slab::STONE_BRICK, "blockrecipe2X1", 1], + ]; + foreach ($recipes as $recipe){ + $buildRecipes[] = $this->createOneIngedientRecipe($shapes[$recipe[$RECIPE_SHAPE]], $recipe[$RESULT_ITEMID], $recipe[$RESULT_META], $recipe[$RESULT_AMOUNT], $recipe[$INGREDIENT_ITEMID], $recipe[$INGREDIENT_META], "X", "Stonecutter"); + } + + // Multi-ingredient stone recipes: + $buildRecipes[] = ((new StonecutterShapedRecipe(Item::get(Item::STONE, Stone::GRANITE, 1), + ...$shapes["blockrecipe1X2"] + ))->setIngredient("A", Item::get(Item::STONE, Stone::DIORITE, 1))->setIngredient("B", Item::get(Item::QUARTZ, Quartz::QUARTZ_NORMAL, 1))); + $buildRecipes[] = ((new StonecutterShapedRecipe(Item::get(Item::STONE, Stone::DIORITE, 2), + ...$shapes["blockrecipe2X2"] + ))->setIngredient("A", Item::get(Item::COBBLESTONE, 0, 2))->setIngredient("B", Item::get(Item::QUARTZ, 0, 2))); + $buildRecipes[] = ((new StonecutterShapedRecipe(Item::get(Item::STONE, Stone::ANDESITE, 2), + ...$shapes["blockrecipe1X2"] + ))->setIngredient("A", Item::get(Item::COBBLESTONE, 0, 1))->setIngredient("B", Item::get(Item::STONE, Stone::DIORITE, 1))); + $buildRecipes[] = ((new StonecutterShapedRecipe(Item::get(Item::STONE_BRICK, StoneBricks::MOSSY, 1), + ...$shapes["blockrecipe1X2"] + ))->setIngredient("A", Item::get(Item::STONE_BRICK, StoneBricks::NORMAL, 1))->setIngredient("B", Item::get(Item::VINES, 0, 1))); + + $this->sortAndAddRecipesArray($buildRecipes); + } + + private function sortAndAddRecipesArray(&$recipes){ + // Sort the recipes based on the result item name with the bubblesort algoritm. + for ($i = 0; $i < count($recipes); ++$i){ + $current = $recipes[$i]; + $result = $current->getResult(); + for ($j = count($recipes)-1; $j > $i; --$j) + { + if ($this->sort($result, $recipes[$j]->getResult())>0){ + $swap = $current; + $current = $recipes[$j]; + $recipes[$j] = $swap; + $result = $current->getResult(); + } + } + $this->registerRecipe($current); + } + } + + private function createOneIngedientRecipe($recipeshape, $resultitem, $resultitemmeta, $resultitemamound, $ingedienttype, $ingredientmeta, $ingredientname, $inventoryType = ""){ + $ingredientamount = 0; + $height = 0; + // count how many of the ingredient are in the recipe and check height for big or small recipe. + foreach ($recipeshape as $line){ + $height += 1; + $width = strlen($line); + $ingredientamount += substr_count($line, $ingredientname); + } + $recipe = null; + if ($height < 3){ + // Process small recipe + $fullClassName = "pocketmine\\inventory\\".$inventoryType."ShapedRecipe";// $ShapeClass."ShapedRecipe"; + $recipe = ((new $fullClassName(Item::get($resultitem, $resultitemmeta, $resultitemamound), + ...$recipeshape + ))->setIngredient($ingredientname, Item::get($ingedienttype, $ingredientmeta, $ingredientamount))); + } + else{ + // Process big recipe + $fullClassName = "pocketmine\\inventory\\".$inventoryType."BigShapedRecipe"; + $recipe = ((new $fullClassName(Item::get($resultitem, $resultitemmeta, $resultitemamound), + ...$recipeshape + ))->setIngredient($ingredientname, Item::get($ingedienttype, $ingredientmeta, $ingredientamount))); + } + return $recipe; } protected function registerFood(){ @@ -188,7 +532,6 @@ class CraftingManager{ } protected function registerArmor(){ - $cost = [5, 8, 7, 4]; $types = [ [Item::LEATHER, Item::FIRE, Item::IRON_INGOT, Item::DIAMOND, Item::GOLD_INGOT], [Item::LEATHER_CAP, Item::CHAIN_HELMET, Item::IRON_HELMET, Item::DIAMOND_HELMET, Item::GOLD_HELMET], @@ -196,31 +539,68 @@ class CraftingManager{ [Item::LEATHER_PANTS, Item::CHAIN_LEGGINGS, Item::IRON_LEGGINGS, Item::DIAMOND_LEGGINGS, Item::GOLD_LEGGINGS], [Item::LEATHER_BOOTS, Item::CHAIN_BOOTS, Item::IRON_BOOTS, Item::DIAMOND_BOOTS, Item::GOLD_BOOTS], ]; + + $shapes = [ + [ + "XXX", + "X X", + " " + ], + [ + "X X", + "XXX", + "XXX" + ], + [ + "XXX", + "X X", + "X X" + ], + [ + " ", + "X X", + "X X" + ] + ]; + for($i = 1; $i < 5; ++$i){ foreach($types[$i] as $j => $type){ - $this->registerRecipe((new BigShapelessRecipe(Item::get($type, 0, 1)))->addIngredient(Item::get($types[0][$j], 0, $cost[$i - 1]))); + $this->registerRecipe((new BigShapedRecipe(Item::get($type, 0, 1), ...$shapes[$i - 1]))->setIngredient("X", Item::get($types[0][$j], 0, 1))); } } } protected function registerWeapons(){ - $cost = [2]; $types = [ [Item::WOODEN_PLANK, Item::COBBLESTONE, Item::IRON_INGOT, Item::DIAMOND, Item::GOLD_INGOT], [Item::WOODEN_SWORD, Item::STONE_SWORD, Item::IRON_SWORD, Item::DIAMOND_SWORD, Item::GOLD_SWORD], ]; + + for($i = 1; $i < 2; ++$i){ foreach($types[$i] as $j => $type){ - $this->registerRecipe((new BigShapelessRecipe(Item::get($type, 0, 1)))->addIngredient(Item::get($types[0][$j], null, $cost[$i - 1]))->addIngredient(Item::get(Item::STICK, 0, 1))); + $this->registerRecipe((new BigShapedRecipe(Item::get($type, 0, 1), + " X ", + " X ", + " I " + ))->setIngredient("X", Item::get($types[0][$j], null))->setIngredient("I", Item::get(Item::STICK))); } } - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::ARROW, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 1))->addIngredient(Item::get(Item::FLINT, 0, 1))->addIngredient(Item::get(Item::FEATHER, 0, 1))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::BOW, 0, 1)))->addIngredient(Item::get(Item::STRING, 0, 3))->addIngredient(Item::get(Item::STICK, 0, 3))); + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::ARROW, 0, 1), + " F ", + " S ", + " P " + ))->setIngredient("S", Item::get(Item::STICK))->setIngredient("F", Item::get(Item::FLINT))->setIngredient("P", Item::get(Item::FEATHER))); + + $this->registerRecipe((new BigShapedRecipe(Item::get(Item::BOW, 0, 1), + " X~", + "X ~", + " X~" + ))->setIngredient("~", Item::get(Item::STRING))->setIngredient("X", Item::get(Item::STICK))); } protected function registerTools(){ - $cost = [3, 1, 3, 2]; $types = [ [Item::WOODEN_PLANK, Item::COBBLESTONE, Item::IRON_INGOT, Item::DIAMOND, Item::GOLD_INGOT], [Item::WOODEN_PICKAXE, Item::STONE_PICKAXE, Item::IRON_PICKAXE, Item::DIAMOND_PICKAXE, Item::GOLD_PICKAXE], @@ -228,14 +608,44 @@ class CraftingManager{ [Item::WOODEN_AXE, Item::STONE_AXE, Item::IRON_AXE, Item::DIAMOND_AXE, Item::GOLD_AXE], [Item::WOODEN_HOE, Item::STONE_HOE, Item::IRON_HOE, Item::DIAMOND_HOE, Item::GOLD_HOE], ]; + $shapes = [ + [ + "XXX", + " I ", + " I " + ], + [ + " X ", + " I ", + " I " + ], + [ + "XX ", + "XI ", + " I " + ], + [ + "XX ", + " I ", + " I " + ] + ]; + for($i = 1; $i < 5; ++$i){ foreach($types[$i] as $j => $type){ - $this->registerRecipe((new BigShapelessRecipe(Item::get($type, 0, 1)))->addIngredient(Item::get($types[0][$j], null, $cost[$i - 1]))->addIngredient(Item::get(Item::STICK, 0, 2))); + $this->registerRecipe((new BigShapedRecipe(Item::get($type, 0, 1), ...$shapes[$i - 1]))->setIngredient("X", Item::get($types[0][$j], null))->setIngredient("I", Item::get(Item::STICK))); } } - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::FLINT_AND_STEEL, 0, 1)))->addIngredient(Item::get(Item::IRON_INGOT, 0, 1))->addIngredient(Item::get(Item::FLINT, 0, 1))); - $this->registerRecipe((new ShapelessRecipe(Item::get(Item::SHEARS, 0, 1)))->addIngredient(Item::get(Item::IRON_INGOT, 0, 2))); + $this->registerRecipe((new ShapedRecipe(Item::get(Item::FLINT_AND_STEEL, 0, 1), + " S", + "F " + ))->setIngredient("F", Item::get(Item::FLINT))->setIngredient("S", Item::get(Item::IRON_INGOT))); + + $this->registerRecipe((new ShapedRecipe(Item::get(Item::SHEARS, 0, 1), + " X", + "X " + ))->setIngredient("X", Item::get(Item::IRON_INGOT))); } protected function registerDyes(){ @@ -243,9 +653,9 @@ class CraftingManager{ $this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOOL, 15 - $i, 1)))->addIngredient(Item::get(Item::DYE, $i, 1))->addIngredient(Item::get(Item::WOOL, 0, 1))); $this->registerRecipe((new ShapelessRecipe(Item::get(Item::STAINED_CLAY, 15 - $i, 8)))->addIngredient(Item::get(Item::DYE, $i, 1))->addIngredient(Item::get(Item::HARDENED_CLAY, 0, 8))); //TODO: add glass things? - //$this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOOL, 15 - $i, 1)))->addIngredient(Item::get(Item::DYE, $i, 1))->addIngredient(Item::get(Item::WOOL, 0, 1))); - //$this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOOL, 15 - $i, 1)))->addIngredient(Item::get(Item::DYE, $i, 1))->addIngredient(Item::get(Item::WOOL, 0, 1))); - //$this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOOL, 15 - $i, 1)))->addIngredient(Item::get(Item::DYE, $i, 1))->addIngredient(Item::get(Item::WOOL, 0, 1))); + $this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOOL, 15 - $i, 1)))->addIngredient(Item::get(Item::DYE, $i, 1))->addIngredient(Item::get(Item::WOOL, 0, 1))); + $this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOOL, 15 - $i, 1)))->addIngredient(Item::get(Item::DYE, $i, 1))->addIngredient(Item::get(Item::WOOL, 0, 1))); + $this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOOL, 15 - $i, 1)))->addIngredient(Item::get(Item::DYE, $i, 1))->addIngredient(Item::get(Item::WOOL, 0, 1))); $this->registerRecipe((new ShapelessRecipe(Item::get(Item::CARPET, $i, 3)))->addIngredient(Item::get(Item::WOOL, $i, 2))); } @@ -291,8 +701,8 @@ class CraftingManager{ $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::LAPIS_BLOCK, 0, 1)))->addIngredient(Item::get(Item::DYE, 4, 9))); $this->registerRecipe((new ShapelessRecipe(Item::get(Item::DYE, 4, 9)))->addIngredient(Item::get(Item::LAPIS_BLOCK, 0, 1))); - //$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::GOLD_INGOT, 0, 1)))->addIngredient(Item::get(Item::GOLD_NUGGET, 0, 9))); - //$this->registerRecipe((new ShapelessRecipe(Item::get(Item::GOLD_NUGGET, 0, 9)))->addIngredient(Item::get(Item::GOLD_INGOT, 0, 1))); + $this->registerRecipe((new BigShapelessRecipe(Item::get(Item::GOLD_INGOT, 0, 1)))->addIngredient(Item::get(Item::GOLD_NUGGET, 0, 9))); + $this->registerRecipe((new ShapelessRecipe(Item::get(Item::GOLD_NUGGET, 0, 9)))->addIngredient(Item::get(Item::GOLD_INGOT, 0, 1))); } @@ -314,6 +724,14 @@ class CraftingManager{ } } + /** + * @param UUID $id + * @return Recipe + */ + public function getRecipe(UUID $id){ + $index = $id->toBinary(); + return isset($this->recipes[$index]) ? $this->recipes[$index] : null; + } /** * @return Recipe[] @@ -348,7 +766,22 @@ class CraftingManager{ * @param ShapedRecipe $recipe */ public function registerShapedRecipe(ShapedRecipe $recipe){ + $result = $recipe->getResult(); + $this->recipes[$recipe->getId()->toBinary()] = $recipe; + $ingredients = $recipe->getIngredientMap(); + $hash = ""; + foreach($ingredients as $v){ + foreach($v as $item){ + if($item !== null){ + /** @var Item $item */ + $hash .= $item->getId() . ":" . ($item->getDamage() === null ? "?" : $item->getDamage()) . "x" . $item->getCount() . ","; + } + } + $hash .= ";"; + } + + $this->recipeLookup[$result->getId() . ":" . $result->getDamage()][$hash] = $recipe; } /** @@ -356,7 +789,7 @@ class CraftingManager{ */ public function registerShapelessRecipe(ShapelessRecipe $recipe){ $result = $recipe->getResult(); - $this->recipes[spl_object_hash($recipe)] = $recipe; + $this->recipes[$recipe->getId()->toBinary()] = $recipe; $hash = ""; $ingredients = $recipe->getIngredientList(); usort($ingredients, [$this, "sort"]); @@ -404,7 +837,7 @@ class CraftingManager{ foreach($ingredients as $item){ $amount = $item->getCount(); foreach($checkInput as $k => $checkItem){ - if($checkItem->equals($item, $checkItem->getDamage() === null ? false : true)){ + if($checkItem->equals($item, $checkItem->getDamage() === null ? false : true, $checkItem->getCompoundTag() === null ? false : true)){ $remove = min($checkItem->getCount(), $amount); $checkItem->setCount($checkItem->getCount() - $remove); if($checkItem->getCount() === 0){ @@ -432,86 +865,12 @@ class CraftingManager{ } - /** - * @param CraftingTransactionGroup $ts - * - * @return Recipe - */ - public function matchTransaction(CraftingTransactionGroup $ts){ - $result = $ts->getResult(); - - if(!($result instanceof Item)){ - return false; - } - $k = $result->getId() . ":" . $result->getDamage(); - - if(!isset($this->recipeLookup[$k])){ - return false; - } - $hash = ""; - $input = $ts->getRecipe(); - usort($input, [$this, "sort"]); - $inputCount = 0; - foreach($input as $item){ - $inputCount += $item->getCount(); - $hash .= $item->getId() . ":" . ($item->getDamage() === null ? "?" : $item->getDamage()) . "x" . $item->getCount() . ","; - } - if(!isset($this->recipeLookup[$k][$hash])){ - $hasRecipe = null; - foreach($this->recipeLookup[$k] as $recipe){ - if($recipe instanceof ShapelessRecipe){ - if($recipe->getIngredientCount() !== $inputCount){ - continue; - } - $checkInput = $recipe->getIngredientList(); - foreach($input as $item){ - $amount = $item->getCount(); - foreach($checkInput as $k => $checkItem){ - if($checkItem->equals($item, $checkItem->getDamage() === null ? false : true)){ - $remove = min($checkItem->getCount(), $amount); - $checkItem->setCount($checkItem->getCount() - $remove); - if($checkItem->getCount() === 0){ - unset($checkInput[$k]); - } - $amount -= $remove; - if($amount === 0){ - break; - } - } - } - } - - if(count($checkInput) === 0){ - $hasRecipe = $recipe; - break; - } - } - if($hasRecipe instanceof Recipe){ - break; - } - } - - if($hasRecipe === null){ - return false; - } - - $recipe = $hasRecipe; - }else{ - $recipe = $this->recipeLookup[$k][$hash]; - } - - $checkResult = $recipe->getResult(); - if($checkResult->equals($result, true) and $checkResult->getCount() === $result->getCount()){ - return $recipe; - } - - return null; - } - /** * @param Recipe $recipe */ public function registerRecipe(Recipe $recipe){ + $recipe->setId(UUID::fromData(++self::$RECIPE_COUNT, $recipe->getResult()->getId(), $recipe->getResult()->getDamage(), $recipe->getResult()->getCount(), $recipe->getResult()->getCompoundTag())); + if($recipe instanceof ShapedRecipe){ $this->registerShapedRecipe($recipe); }elseif($recipe instanceof ShapelessRecipe){ diff --git a/src/pocketmine/inventory/CraftingTransactionGroup.php b/src/pocketmine/inventory/CraftingTransactionGroup.php deleted file mode 100644 index 74d23da20..000000000 --- a/src/pocketmine/inventory/CraftingTransactionGroup.php +++ /dev/null @@ -1,111 +0,0 @@ -transactions = $group->getTransactions(); - $this->inventories = $group->getInventories(); - $this->source = $group->getSource(); - - $this->matchItems($this->output, $this->input); - } - - public function addTransaction(Transaction $transaction){ - parent::addTransaction($transaction); - $this->input = []; - $this->output = []; - $this->matchItems($this->output, $this->input); - } - - /** - * Gets the Items that have been used - * - * @return Item[] - */ - public function getRecipe(){ - return $this->input; - } - - /** - * @return Item - */ - public function getResult(){ - reset($this->output); - - return current($this->output); - } - - public function canExecute(){ - if(count($this->output) !== 1 or count($this->input) === 0){ - return false; - } - - return $this->getMatchingRecipe() instanceof Recipe; - } - - /** - * @return Recipe - */ - public function getMatchingRecipe(){ - if($this->recipe === null){ - $this->recipe = Server::getInstance()->getCraftingManager()->matchTransaction($this); - } - - return $this->recipe; - } - - public function execute(){ - if($this->hasExecuted() or !$this->canExecute()){ - return false; - } - - Server::getInstance()->getPluginManager()->callEvent($ev = new CraftItemEvent($this, $this->getMatchingRecipe())); - if($ev->isCancelled()){ - foreach($this->inventories as $inventory){ - $inventory->sendContents($inventory->getViewers()); - } - - return false; - } - - foreach($this->transactions as $transaction){ - $transaction->getInventory()->setItem($transaction->getSlot(), $transaction->getTargetItem(), $this->getSource()); - } - $this->hasExecuted = true; - - return true; - } -} \ No newline at end of file diff --git a/src/pocketmine/inventory/DoubleChestInventory.php b/src/pocketmine/inventory/DoubleChestInventory.php index 94c611cb4..34b0b7bf8 100644 --- a/src/pocketmine/inventory/DoubleChestInventory.php +++ b/src/pocketmine/inventory/DoubleChestInventory.php @@ -24,9 +24,9 @@ namespace pocketmine\inventory; use pocketmine\item\Item; use pocketmine\level\Level; use pocketmine\network\Network; -use pocketmine\network\protocol\TileEventPacket; +use pocketmine\network\protocol\BlockEventPacket; use pocketmine\Player; -use pocketmine\Server; + use pocketmine\tile\Chest; class DoubleChestInventory extends ChestInventory implements InventoryHolder{ @@ -99,28 +99,28 @@ class DoubleChestInventory extends ChestInventory implements InventoryHolder{ parent::onOpen($who); if(count($this->getViewers()) === 1){ - $pk = new TileEventPacket(); + $pk = new BlockEventPacket(); $pk->x = $this->right->getHolder()->getX(); $pk->y = $this->right->getHolder()->getY(); $pk->z = $this->right->getHolder()->getZ(); $pk->case1 = 1; $pk->case2 = 2; if(($level = $this->right->getHolder()->getLevel()) instanceof Level){ - $level->addChunkPacket($this->right->getHolder()->getX() >> 4, $this->right->getHolder()->getZ() >> 4, $pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $level->addChunkPacket($this->right->getHolder()->getX() >> 4, $this->right->getHolder()->getZ() >> 4, $pk); } } } public function onClose(Player $who){ if(count($this->getViewers()) === 1){ - $pk = new TileEventPacket(); + $pk = new BlockEventPacket(); $pk->x = $this->right->getHolder()->getX(); $pk->y = $this->right->getHolder()->getY(); $pk->z = $this->right->getHolder()->getZ(); $pk->case1 = 1; $pk->case2 = 0; if(($level = $this->right->getHolder()->getLevel()) instanceof Level){ - $level->addChunkPacket($this->right->getHolder()->getX() >> 4, $this->right->getHolder()->getZ() >> 4, $pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $level->addChunkPacket($this->right->getHolder()->getX() >> 4, $this->right->getHolder()->getZ() >> 4, $pk); } } parent::onClose($who); @@ -139,4 +139,4 @@ class DoubleChestInventory extends ChestInventory implements InventoryHolder{ public function getRightSide(){ return $this->right; } -} \ No newline at end of file +} diff --git a/src/pocketmine/inventory/EnchantInventory.php b/src/pocketmine/inventory/EnchantInventory.php new file mode 100644 index 000000000..39f21b570 --- /dev/null +++ b/src/pocketmine/inventory/EnchantInventory.php @@ -0,0 +1,47 @@ +holder; + } + + public function onClose(Player $who){ + parent::onClose($who); + + for($i = 0; $i < 2; ++$i){ + $this->getHolder()->getLevel()->dropItem($this->getHolder()->add(0.5, 0.5, 0.5), $this->getItem($i)); + $this->clear($i); + } + } +} \ No newline at end of file diff --git a/src/pocketmine/inventory/FakeBlockMenu.php b/src/pocketmine/inventory/FakeBlockMenu.php new file mode 100644 index 000000000..aac6b9c29 --- /dev/null +++ b/src/pocketmine/inventory/FakeBlockMenu.php @@ -0,0 +1,40 @@ +inventory = $inventory; + parent::__construct($pos->x, $pos->y, $pos->z, $pos->level); + } + + public function getInventory(){ + return $this->inventory; + } +} \ No newline at end of file diff --git a/src/pocketmine/inventory/FurnaceRecipe.php b/src/pocketmine/inventory/FurnaceRecipe.php index dfefdca21..34b4dbf81 100644 --- a/src/pocketmine/inventory/FurnaceRecipe.php +++ b/src/pocketmine/inventory/FurnaceRecipe.php @@ -23,8 +23,12 @@ namespace pocketmine\inventory; use pocketmine\item\Item; use pocketmine\Server; +use pocketmine\utils\UUID; class FurnaceRecipe implements Recipe{ + + private $id = null; + /** @var Item */ private $output; @@ -40,6 +44,18 @@ class FurnaceRecipe implements Recipe{ $this->ingredient = clone $ingredient; } + public function getId(){ + return $this->id; + } + + public function setId(UUID $id){ + if($this->id !== null){ + throw new \InvalidStateException("Id is already set"); + } + + $this->id = $id; + } + /** * @param Item $item */ diff --git a/src/pocketmine/inventory/InventoryType.php b/src/pocketmine/inventory/InventoryType.php index b149b680f..046a6464f 100644 --- a/src/pocketmine/inventory/InventoryType.php +++ b/src/pocketmine/inventory/InventoryType.php @@ -32,6 +32,9 @@ class InventoryType{ const CRAFTING = 4; const WORKBENCH = 5; const STONECUTTER = 6; + const BREWING_STAND = 7; + const ANVIL = 8; + const ENCHANT_TABLE = 9; private static $default = []; @@ -59,7 +62,10 @@ class InventoryType{ static::$default[static::FURNACE] = new InventoryType(3, "Furnace", 2); static::$default[static::CRAFTING] = new InventoryType(5, "Crafting", 1); //4 CRAFTING slots, 1 RESULT static::$default[static::WORKBENCH] = new InventoryType(10, "Crafting", 1); //9 CRAFTING slots, 1 RESULT - static::$default[static::STONECUTTER] = new InventoryType(10, "Crafting", 3); //9 CRAFTING slots, 1 RESULT + static::$default[static::STONECUTTER] = new InventoryType(10, "Crafting", 1); //9 CRAFTING slots, 1 RESULT + static::$default[static::ENCHANT_TABLE] = new InventoryType(2, "Enchant", 4); //1 INPUT/OUTPUT, 1 LAPIS + static::$default[static::BREWING_STAND] = new InventoryType(4, "Brewing", 5); //1 INPUT, 3 POTION + static::$default[static::ANVIL] = new InventoryType(3, "Anvil", 6); //2 INPUT, 1 OUTPUT } /** diff --git a/src/pocketmine/inventory/PlayerInventory.php b/src/pocketmine/inventory/PlayerInventory.php index 05b9f2a39..e788421c6 100644 --- a/src/pocketmine/inventory/PlayerInventory.php +++ b/src/pocketmine/inventory/PlayerInventory.php @@ -29,8 +29,8 @@ use pocketmine\item\Item; use pocketmine\network\Network; use pocketmine\network\protocol\ContainerSetContentPacket; use pocketmine\network\protocol\ContainerSetSlotPacket; -use pocketmine\network\protocol\PlayerArmorEquipmentPacket; -use pocketmine\network\protocol\PlayerEquipmentPacket; +use pocketmine\network\protocol\MobArmorEquipmentPacket; +use pocketmine\network\protocol\MobEquipmentPacket; use pocketmine\Player; use pocketmine\Server; @@ -126,20 +126,19 @@ class PlayerInventory extends BaseInventory{ public function sendHeldItem($target){ $item = $this->getItemInHand(); - $pk = new PlayerEquipmentPacket(); + $pk = new MobEquipmentPacket(); $pk->eid = ($target === $this->getHolder() ? 0 : $this->getHolder()->getId()); - $pk->item = $item->getId(); - $pk->meta = $item->getDamage(); + $pk->item = $item; $pk->slot = $this->getHeldItemSlot(); $pk->selectedSlot = $this->getHeldItemIndex(); if(!is_array($target)){ - $target->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $target->dataPacket($pk); if($target === $this->getHolder()){ $this->sendSlot($this->getHeldItemSlot(), $target); } }else{ - Server::broadcastPacket($target, $pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + Server::broadcastPacket($target, $pk); foreach($target as $player){ if($player === $this->getHolder()){ $this->sendSlot($this->getHeldItemSlot(), $player); @@ -304,22 +303,14 @@ class PlayerInventory extends BaseInventory{ if($target instanceof Player){ $target = [$target]; } + $armor = $this->getArmorContents(); - $slots = []; - foreach($armor as $i => $slot){ - if($slot->getId() === Item::AIR){ - $slots[$i] = 255; - }else{ - $slots[$i] = $slot->getId(); - } - } - - $pk = new PlayerArmorEquipmentPacket(); + $pk = new MobArmorEquipmentPacket(); $pk->eid = $this->getHolder()->getId(); - $pk->slots = $slots; + $pk->slots = $armor; $pk->encode(); - $pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING); + $pk; $pk->isEncoded = true; foreach($target as $player){ @@ -362,19 +353,10 @@ class PlayerInventory extends BaseInventory{ } $armor = $this->getArmorContents(); - $slots = []; - foreach($armor as $i => $slot){ - if($slot->getId() === Item::AIR){ - $slots[$i] = 255; - }else{ - $slots[$i] = $slot->getId(); - } - } - - $pk = new PlayerArmorEquipmentPacket(); + $pk = new MobArmorEquipmentPacket(); $pk->eid = $this->getHolder()->getId(); - $pk->slots = $slots; + $pk->slots = $armor; $pk->encode(); $pk->isEncoded = true; @@ -387,7 +369,7 @@ class PlayerInventory extends BaseInventory{ $pk2->item = $this->getItem($index); $player->dataPacket($pk2); }else{ - $player->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING)); + $player->dataPacket($pk); } } } @@ -401,10 +383,17 @@ class PlayerInventory extends BaseInventory{ } $pk = new ContainerSetContentPacket(); - $pk->setChannel(Network::CHANNEL_WORLD_EVENTS); $pk->slots = []; - for($i = 0; $i < $this->getSize(); ++$i){ //Do not send armor by error here - $pk->slots[$i] = $this->getItem($i); + $holder = $this->getHolder(); + if($holder instanceof Player and $holder->isCreative()){ + //TODO: Remove this workaround because of broken client + foreach(Item::getCreativeItems() as $i => $item){ + $pk->slots[$i] = Item::getCreativeItem($i); + } + }else{ + for($i = 0; $i < $this->getSize(); ++$i){ //Do not send armor by error here + $pk->slots[$i] = $this->getItem($i); + } } foreach($target as $player){ @@ -434,7 +423,6 @@ class PlayerInventory extends BaseInventory{ } $pk = new ContainerSetSlotPacket(); - $pk->setChannel(Network::CHANNEL_WORLD_EVENTS); $pk->slot = $index; $pk->item = clone $this->getItem($index); diff --git a/src/pocketmine/inventory/Recipe.php b/src/pocketmine/inventory/Recipe.php index ae11e250b..bcac5ba75 100644 --- a/src/pocketmine/inventory/Recipe.php +++ b/src/pocketmine/inventory/Recipe.php @@ -21,6 +21,8 @@ namespace pocketmine\inventory; +use pocketmine\utils\UUID; + interface Recipe{ /** @@ -29,4 +31,9 @@ interface Recipe{ public function getResult(); public function registerToCraftingManager(); + + /** + * @return UUID + */ + public function getId(); } \ No newline at end of file diff --git a/src/pocketmine/inventory/ShapedRecipe.php b/src/pocketmine/inventory/ShapedRecipe.php index 6371a68d8..95773d03a 100644 --- a/src/pocketmine/inventory/ShapedRecipe.php +++ b/src/pocketmine/inventory/ShapedRecipe.php @@ -23,16 +23,22 @@ namespace pocketmine\inventory; use pocketmine\item\Item; use pocketmine\Server; +use pocketmine\utils\UUID; +use pocketmine\math\Vector2; class ShapedRecipe implements Recipe{ /** @var Item */ private $output; - /** @var string[] */ - private $rows = []; + private $id = null; - /** @var Item[] */ + /** @var string[] */ + private $shape = []; + + /** @var Item[][] */ private $ingredients = []; + /** @var Vector2[][] */ + private $shapeItems = []; /** * @param Item $result @@ -40,27 +46,57 @@ class ShapedRecipe implements Recipe{ * * @throws \Exception */ - public function __construct(Item $result, array $shape = []){ + public function __construct(Item $result, ...$shape){ if(count($shape) === 0){ throw new \InvalidArgumentException("Must provide a shape"); } if(count($shape) > 3){ throw new \InvalidStateException("Crafting recipes should be 1, 2, 3 rows, not " . count($shape)); } - foreach($shape as $row){ + foreach($shape as $y => $row){ if(strlen($row) === 0 or strlen($row) > 3){ throw new \InvalidStateException("Crafting rows should be 1, 2, 3 characters, not " . count($row)); } - $this->rows[] = $row; + $this->ingredients[] = array_fill(0, strlen($row), null); $len = strlen($row); for($i = 0; $i < $len; ++$i){ - $this->ingredients[$row{$i}] = null; + $this->shape[$row{$i}] = null; + + if(!isset($this->shapeItems[$row{$i}])){ + $this->shapeItems[$row{$i}] = [new Vector2($i, $y)]; + }else{ + $this->shapeItems[$row{$i}][] = new Vector2($i, $y); + } } } $this->output = clone $result; } + public function getWidth(){ + return count($this->ingredients[0]); + } + + public function getHeight(){ + return count($this->ingredients); + } + + public function getResult(){ + return $this->output; + } + + public function getId(){ + return $this->id; + } + + public function setId(UUID $id){ + if($this->id !== null){ + throw new \InvalidStateException("Id is already set"); + } + + $this->id = $id; + } + /** * @param string $key * @param Item $item @@ -69,36 +105,54 @@ class ShapedRecipe implements Recipe{ * @throws \Exception */ public function setIngredient($key, Item $item){ - if(!isset($this->ingredients[$key])){ + if(!array_key_exists($key, $this->shape)){ throw new \Exception("Symbol does not appear in the shape: " . $key); } - $this->ingredients[$key] = $item; + $this->fixRecipe($key, $item); return $this; } + protected function fixRecipe($key, $item){ + foreach($this->shapeItems[$key] as $entry){ + $this->ingredients[$entry->y][$entry->x] = clone $item; + } + } + /** - * @return Item[] + * @return Item[][] */ public function getIngredientMap(){ $ingredients = []; - foreach($this->ingredients as $key => $ingredient){ - if($ingredient instanceof Item){ - $ingredients[$key] = clone $ingredient; - }else{ - $ingredients[$key] = $ingredient; + foreach($this->ingredients as $y => $row){ + $ingredients[$y] = []; + foreach($row as $x => $ingredient){ + if($ingredient !== null){ + $ingredients[$y][$x] = clone $ingredient; + }else{ + $ingredients[$y][$x] = Item::get(Item::AIR); + } } } return $ingredients; } + /** + * @param $x + * @param $y + * @return null|Item + */ + public function getIngredient($x, $y){ + return isset($this->ingredients[$y][$x]) ? $this->ingredients[$y][$x] : Item::get(Item::AIR); + } + /** * @return string[] */ public function getShape(){ - return $this->rows; + return $this->shape; } public function registerToCraftingManager(){ diff --git a/src/pocketmine/inventory/ShapelessRecipe.php b/src/pocketmine/inventory/ShapelessRecipe.php index be87c8006..99b0778c5 100644 --- a/src/pocketmine/inventory/ShapelessRecipe.php +++ b/src/pocketmine/inventory/ShapelessRecipe.php @@ -23,11 +23,14 @@ namespace pocketmine\inventory; use pocketmine\item\Item; use pocketmine\Server; +use pocketmine\utils\UUID; class ShapelessRecipe implements Recipe{ /** @var Item */ private $output; + private $id = null; + /** @var Item[] */ private $ingredients = []; @@ -35,6 +38,18 @@ class ShapelessRecipe implements Recipe{ $this->output = clone $result; } + public function getId(){ + return $this->id; + } + + public function setId(UUID $id){ + if($this->id !== null){ + throw new \InvalidStateException("Id is already set"); + } + + $this->id = $id; + } + public function getResult(){ return clone $this->output; } @@ -72,7 +87,7 @@ class ShapelessRecipe implements Recipe{ if($item->getCount() <= 0){ break; } - if($ingredient->equals($item, $item->getDamage() === null ? false : true)){ + if($ingredient->equals($item, $item->getDamage() === null ? false : true, $item->getCompoundTag() === null ? false : true)){ unset($this->ingredients[$index]); $item->setCount($item->getCount() - 1); } diff --git a/src/pocketmine/inventory/SimpleTransactionGroup.php b/src/pocketmine/inventory/SimpleTransactionGroup.php index 672bdcd22..595cff384 100644 --- a/src/pocketmine/inventory/SimpleTransactionGroup.php +++ b/src/pocketmine/inventory/SimpleTransactionGroup.php @@ -98,7 +98,7 @@ class SimpleTransactionGroup implements TransactionGroup{ } $checkSourceItem = $ts->getInventory()->getItem($ts->getSlot()); $sourceItem = $ts->getSourceItem(); - if(!$checkSourceItem->equals($sourceItem, true) or $sourceItem->getCount() !== $checkSourceItem->getCount()){ + if(!$checkSourceItem->deepEquals($sourceItem) or $sourceItem->getCount() !== $checkSourceItem->getCount()){ return false; } if($sourceItem->getId() !== Item::AIR){ @@ -108,7 +108,7 @@ class SimpleTransactionGroup implements TransactionGroup{ foreach($needItems as $i => $needItem){ foreach($haveItems as $j => $haveItem){ - if($needItem->equals($haveItem, true)){ + if($needItem->deepEquals($haveItem)){ $amount = min($needItem->getCount(), $haveItem->getCount()); $needItem->setCount($needItem->getCount() - $amount); $haveItem->setCount($haveItem->getCount() - $amount); diff --git a/src/pocketmine/inventory/StonecutterBigShapedRecipe.php b/src/pocketmine/inventory/StonecutterBigShapedRecipe.php new file mode 100644 index 000000000..a1cb800a3 --- /dev/null +++ b/src/pocketmine/inventory/StonecutterBigShapedRecipe.php @@ -0,0 +1,26 @@ +block = Block::get(Item::FLOWER_POT_BLOCK); + parent::__construct(self::FLOWER_POT, 0, $count, "Flower Pot"); + } +} \ No newline at end of file diff --git a/src/pocketmine/item/GlowstoneDust.php b/src/pocketmine/item/GlowstoneDust.php new file mode 100644 index 000000000..67e10f06a --- /dev/null +++ b/src/pocketmine/item/GlowstoneDust.php @@ -0,0 +1,30 @@ +read($tag); + return self::$cachedParser->getData(); + } + + /** + * @param Compound $tag + * @return string + */ + private static function writeCompoundTag(Compound $tag){ + if(self::$cachedParser === null){ + self::$cachedParser = new NBT(NBT::LITTLE_ENDIAN); + } + + self::$cachedParser->setData($tag); + return self::$cachedParser->write(); + } + //All Block IDs are here too const AIR = 0; const STONE = 1; @@ -174,9 +210,13 @@ class Item{ const LILY_PAD = 111; const NETHER_BRICKS = 112; const NETHER_BRICK_BLOCK = 112; - + const NETHER_BRICK_FENCE = 113; const NETHER_BRICKS_STAIRS = 114; + const ENCHANTING_TABLE = 116; + const ENCHANT_TABLE = 116; + const ENCHANTMENT_TABLE = 116; + const BREWING_STAND = 117; const END_PORTAL = 120; const END_STONE = 121; @@ -194,10 +234,13 @@ class Item{ const COBBLE_WALL = 139; const STONE_WALL = 139; const COBBLESTONE_WALL = 139; - + const FLOWER_POT_BLOCK = 140; const CARROT_BLOCK = 141; const POTATO_BLOCK = 142; + const ANVIL = 145; + const TRAPPED_CHEST = 146; + const REDSTONE_BLOCK = 152; const QUARTZ_BLOCK = 155; @@ -223,11 +266,12 @@ class Item{ const DARK_OAK_WOOD_STAIRS = 164; const DARK_OAK_WOODEN_STAIRS = 164; + const IRON_TRAPDOOR = 167; const HAY_BALE = 170; const CARPET = 171; const HARDENED_CLAY = 172; const COAL_BLOCK = 173; - + const PACKED_ICE = 174; const DOUBLE_PLANT = 175; const FENCE_GATE_SPRUCE = 183; @@ -242,7 +286,6 @@ class Item{ const BEETROOT_BLOCK = 244; const STONECUTTER = 245; const GLOWING_OBSIDIAN = 246; - const NETHER_REACTOR = 247; //Normal Item IDs @@ -346,7 +389,7 @@ class Item{ const EGG = 344; const COMPASS = 345; - + const FISHING_ROD = 346; const CLOCK = 347; const GLOWSTONE_DUST = 348; const RAW_FISH = 349; @@ -373,10 +416,14 @@ class Item{ const RAW_CHICKEN = 365; const COOKED_CHICKEN = 366; + const GOLD_NUGGET = 371; + const GOLDEN_NUGGET = 371; + const SPAWN_EGG = 383; const EMERALD = 388; + const FLOWER_POT = 390; const CARROT = 391; const CARROTS = 391; const POTATO = 392; @@ -402,6 +449,8 @@ class Item{ protected $block; protected $id; protected $meta; + private $tags = ""; + private $cachedNBT = null; public $count; protected $durability = 0; protected $name; @@ -413,31 +462,48 @@ class Item{ public static function init(){ if(self::$list === null){ self::$list = new \SplFixedArray(65536); - self::$list[self::SUGARCANE] = Sugarcane::class; - self::$list[self::WHEAT_SEEDS] = WheatSeeds::class; - self::$list[self::PUMPKIN_SEEDS] = PumpkinSeeds::class; - self::$list[self::MELON_SEEDS] = MelonSeeds::class; - self::$list[self::MUSHROOM_STEW] = MushroomStew::class; - self::$list[self::BEETROOT_SOUP] = BeetrootSoup::class; - self::$list[self::CARROT] = Carrot::class; - self::$list[self::POTATO] = Potato::class; - self::$list[self::BEETROOT_SEEDS] = BeetrootSeeds::class; - self::$list[self::SIGN] = Sign::class; - self::$list[self::WOODEN_DOOR] = WoodenDoor::class; - self::$list[self::BUCKET] = Bucket::class; - self::$list[self::IRON_DOOR] = IronDoor::class; - self::$list[self::CAKE] = Cake::class; - self::$list[self::BED] = Bed::class; - self::$list[self::PAINTING] = Painting::class; - self::$list[self::COAL] = Coal::class; + self::$list[self::IRON_SHOVEL] = IronShovel::class; + self::$list[self::IRON_PICKAXE] = IronPickaxe::class; + self::$list[self::IRON_AXE] = IronAxe::class; + self::$list[self::FLINT_STEEL] = FlintSteel::class; self::$list[self::APPLE] = Apple::class; - self::$list[self::SPAWN_EGG] = SpawnEgg::class; + self::$list[self::BOW] = Bow::class; + self::$list[self::ARROW] = Arrow::class; + self::$list[self::COAL] = Coal::class; self::$list[self::DIAMOND] = Diamond::class; + self::$list[self::IRON_INGOT] = IronIngot::class; + self::$list[self::GOLD_INGOT] = GoldIngot::class; + self::$list[self::IRON_SWORD] = IronSword::class; + self::$list[self::WOODEN_SWORD] = WoodenSword::class; + self::$list[self::WOODEN_SHOVEL] = WoodenShovel::class; + self::$list[self::WOODEN_PICKAXE] = WoodenPickaxe::class; + self::$list[self::WOODEN_AXE] = WoodenAxe::class; + self::$list[self::STONE_SWORD] = StoneSword::class; + self::$list[self::STONE_SHOVEL] = StoneShovel::class; + self::$list[self::STONE_PICKAXE] = StonePickaxe::class; + self::$list[self::STONE_AXE] = StoneAxe::class; + self::$list[self::DIAMOND_SWORD] = DiamondSword::class; + self::$list[self::DIAMOND_SHOVEL] = DiamondShovel::class; + self::$list[self::DIAMOND_PICKAXE] = DiamondPickaxe::class; + self::$list[self::DIAMOND_AXE] = DiamondAxe::class; self::$list[self::STICK] = Stick::class; - self::$list[self::SNOWBALL] = Snowball::class; self::$list[self::BOWL] = Bowl::class; + self::$list[self::MUSHROOM_STEW] = MushroomStew::class; + self::$list[self::GOLD_SWORD] = GoldSword::class; + self::$list[self::GOLD_SHOVEL] = GoldShovel::class; + self::$list[self::GOLD_PICKAXE] = GoldPickaxe::class; + self::$list[self::GOLD_AXE] = GoldAxe::class; + self::$list[self::STRING] = StringItem::class; self::$list[self::FEATHER] = Feather::class; - self::$list[self::BRICK] = Brick::class; + self::$list[self::GUNPOWDER] = Gunpowder::class; + self::$list[self::WOODEN_HOE] = WoodenHoe::class; + self::$list[self::STONE_HOE] = StoneHoe::class; + self::$list[self::IRON_HOE] = IronHoe::class; + self::$list[self::DIAMOND_HOE] = DiamondHoe::class; + self::$list[self::GOLD_HOE] = GoldHoe::class; + self::$list[self::WHEAT_SEEDS] = WheatSeeds::class; + self::$list[self::WHEAT] = Wheat::class; + self::$list[self::BREAD] = Bread::class; self::$list[self::LEATHER_CAP] = LeatherCap::class; self::$list[self::LEATHER_TUNIC] = LeatherTunic::class; self::$list[self::LEATHER_PANTS] = LeatherPants::class; @@ -450,47 +516,68 @@ class Item{ self::$list[self::IRON_CHESTPLATE] = IronChestplate::class; self::$list[self::IRON_LEGGINGS] = IronLeggings::class; self::$list[self::IRON_BOOTS] = IronBoots::class; - self::$list[self::GOLD_HELMET] = GoldHelmet::class; - self::$list[self::GOLD_CHESTPLATE] = GoldChestplate::class; - self::$list[self::GOLD_LEGGINGS] = GoldLeggings::class; - self::$list[self::GOLD_BOOTS] = GoldBoots::class; self::$list[self::DIAMOND_HELMET] = DiamondHelmet::class; self::$list[self::DIAMOND_CHESTPLATE] = DiamondChestplate::class; self::$list[self::DIAMOND_LEGGINGS] = DiamondLeggings::class; self::$list[self::DIAMOND_BOOTS] = DiamondBoots::class; - self::$list[self::IRON_SWORD] = IronSword::class; - self::$list[self::IRON_INGOT] = IronIngot::class; - self::$list[self::GOLD_INGOT] = GoldIngot::class; - self::$list[self::IRON_SHOVEL] = IronShovel::class; - self::$list[self::IRON_PICKAXE] = IronPickaxe::class; - self::$list[self::IRON_AXE] = IronAxe::class; - self::$list[self::IRON_HOE] = IronHoe::class; - self::$list[self::DIAMOND_SWORD] = DiamondSword::class; - self::$list[self::DIAMOND_SHOVEL] = DiamondShovel::class; - self::$list[self::DIAMOND_PICKAXE] = DiamondPickaxe::class; - self::$list[self::DIAMOND_AXE] = DiamondAxe::class; - self::$list[self::DIAMOND_HOE] = DiamondHoe::class; - self::$list[self::GOLD_SWORD] = GoldSword::class; - self::$list[self::GOLD_SHOVEL] = GoldShovel::class; - self::$list[self::GOLD_PICKAXE] = GoldPickaxe::class; - self::$list[self::GOLD_AXE] = GoldAxe::class; - self::$list[self::GOLD_HOE] = GoldHoe::class; - self::$list[self::STONE_SWORD] = StoneSword::class; - self::$list[self::STONE_SHOVEL] = StoneShovel::class; - self::$list[self::STONE_PICKAXE] = StonePickaxe::class; - self::$list[self::STONE_AXE] = StoneAxe::class; - self::$list[self::STONE_HOE] = StoneHoe::class; - self::$list[self::WOODEN_SWORD] = WoodenSword::class; - self::$list[self::WOODEN_SHOVEL] = WoodenShovel::class; - self::$list[self::WOODEN_PICKAXE] = WoodenPickaxe::class; - self::$list[self::WOODEN_AXE] = WoodenAxe::class; - self::$list[self::WOODEN_HOE] = WoodenHoe::class; - self::$list[self::FLINT_STEEL] = FlintSteel::class; - self::$list[self::SHEARS] = Shears::class; - self::$list[self::BOW] = Bow::class; - + self::$list[self::GOLD_HELMET] = GoldHelmet::class; + self::$list[self::GOLD_CHESTPLATE] = GoldChestplate::class; + self::$list[self::GOLD_LEGGINGS] = GoldLeggings::class; + self::$list[self::GOLD_BOOTS] = GoldBoots::class; + self::$list[self::FLINT] = Flint::class; + self::$list[self::RAW_PORKCHOP] = RawPorkchop::class; + self::$list[self::COOKED_PORKCHOP] = CookedPorkchop::class; + self::$list[self::PAINTING] = Painting::class; + self::$list[self::GOLDEN_APPLE] = GoldenApple::class; + self::$list[self::SIGN] = Sign::class; + self::$list[self::WOODEN_DOOR] = WoodenDoor::class; + self::$list[self::BUCKET] = Bucket::class; + self::$list[self::MINECART] = Minecart::class; + self::$list[self::IRON_DOOR] = IronDoor::class; + self::$list[self::REDSTONE] = Redstone::class; + self::$list[self::SNOWBALL] = Snowball::class; + self::$list[self::LEATHER] = Leather::class; + self::$list[self::BRICK] = Brick::class; + self::$list[self::CLAY] = Clay::class; + self::$list[self::SUGARCANE] = Sugarcane::class; + self::$list[self::PAPER] = Paper::class; + self::$list[self::BOOK] = Book::class; + self::$list[self::SLIMEBALL] = Slimeball::class; + self::$list[self::EGG] = Egg::class; + self::$list[self::COMPASS] = Compass::class; + self::$list[self::CLOCK] = Clock::class; + self::$list[self::GLOWSTONE_DUST] = GlowstoneDust::class; self::$list[self::RAW_FISH] = Fish::class; self::$list[self::COOKED_FISH] = CookedFish::class; + self::$list[self::DYE] = Dye::class; + self::$list[self::BONE] = Bone::class; + self::$list[self::SUGAR] = Sugar::class; + self::$list[self::CAKE] = Cake::class; + self::$list[self::BED] = Bed::class; + self::$list[self::COOKIE] = Cookie::class; + self::$list[self::SHEARS] = Shears::class; + self::$list[self::MELON] = Melon::class; + self::$list[self::PUMPKIN_SEEDS] = PumpkinSeeds::class; + self::$list[self::MELON_SEEDS] = MelonSeeds::class; + self::$list[self::RAW_BEEF] = RawBeef::class; + self::$list[self::STEAK] = Steak::class; + self::$list[self::RAW_CHICKEN] = RawChicken::class; + self::$list[self::COOKED_CHICKEN] = CookedChicken::class; + self::$list[self::GOLD_NUGGET] = GoldNugget::class; + self::$list[self::SPAWN_EGG] = SpawnEgg::class; + self::$list[self::EMERALD] = Emerald::class; + self::$list[self::FLOWER_POT] = FlowerPot::class; + self::$list[self::CARROT] = Carrot::class; + self::$list[self::POTATO] = Potato::class; + self::$list[self::BAKED_POTATO] = BakedPotato::class; + self::$list[self::PUMPKIN_PIE] = PumpkinPie::class; + self::$list[self::NETHER_BRICK] = NetherBrick::class; + self::$list[self::QUARTZ] = Quartz::class; + self::$list[self::QUARTZ] = NetherQuartz::class; + // self::$list[self::CAMERA] = Camera::class; + self::$list[self::BEETROOT] = Beetroot::class; + self::$list[self::BEETROOT_SEEDS] = BeetrootSeeds::class; + self::$list[self::BEETROOT_SOUP] = BeetrootSoup::class; for($i = 0; $i < 256; ++$i){ if(Block::$list[$i] !== null){ @@ -498,7 +585,7 @@ class Item{ } } } - + self::initCreativeItems(); } @@ -506,6 +593,7 @@ class Item{ private static function initCreativeItems(){ self::clearCreativeItems(); + //Building self::addCreativeItem(Item::get(Item::COBBLESTONE, 0)); self::addCreativeItem(Item::get(Item::STONE_BRICKS, 0)); @@ -520,7 +608,6 @@ class Item{ self::addCreativeItem(Item::get(Item::WOODEN_PLANKS, 4)); self::addCreativeItem(Item::get(Item::WOODEN_PLANKS, 5)); self::addCreativeItem(Item::get(Item::BRICKS, 0)); - self::addCreativeItem(Item::get(Item::STONE, 0)); self::addCreativeItem(Item::get(Item::STONE, 1)); self::addCreativeItem(Item::get(Item::STONE, 2)); @@ -564,6 +651,7 @@ class Item{ self::addCreativeItem(Item::get(Item::TRUNK2, 1)); self::addCreativeItem(Item::get(Item::NETHER_BRICKS, 0)); self::addCreativeItem(Item::get(Item::NETHERRACK, 0)); + self::addCreativeItem(Item::get(Item::SOUL_SAND, 0)); self::addCreativeItem(Item::get(Item::BEDROCK, 0)); self::addCreativeItem(Item::get(Item::COBBLESTONE_STAIRS, 0)); self::addCreativeItem(Item::get(Item::OAK_WOODEN_STAIRS, 0)); @@ -589,6 +677,7 @@ class Item{ self::addCreativeItem(Item::get(Item::SLAB, 4)); self::addCreativeItem(Item::get(Item::SLAB, 5)); self::addCreativeItem(Item::get(Item::SLAB, 6)); + self::addCreativeItem(Item::get(Item::SLAB, 7)); self::addCreativeItem(Item::get(Item::QUARTZ_BLOCK, 0)); self::addCreativeItem(Item::get(Item::QUARTZ_BLOCK, 1)); self::addCreativeItem(Item::get(Item::QUARTZ_BLOCK, 2)); @@ -601,6 +690,7 @@ class Item{ self::addCreativeItem(Item::get(Item::EMERALD_ORE, 0)); self::addCreativeItem(Item::get(Item::OBSIDIAN, 0)); self::addCreativeItem(Item::get(Item::ICE, 0)); + self::addCreativeItem(Item::get(Item::PACKED_ICE, 0)); self::addCreativeItem(Item::get(Item::SNOW_BLOCK, 0)); self::addCreativeItem(Item::get(Item::END_STONE, 0)); @@ -619,24 +709,31 @@ class Item{ self::addCreativeItem(Item::get(Item::GLASS, 0)); self::addCreativeItem(Item::get(Item::GLOWSTONE_BLOCK, 0)); self::addCreativeItem(Item::get(Item::VINES, 0)); - self::addCreativeItem(Item::get(Item::NETHER_REACTOR, 0)); self::addCreativeItem(Item::get(Item::LADDER, 0)); self::addCreativeItem(Item::get(Item::SPONGE, 0)); self::addCreativeItem(Item::get(Item::GLASS_PANE, 0)); - self::addCreativeItem(Item::get(Item::WOODEN_DOOR, 0)); + self::addCreativeItem(Item::get(Item::WOODEN_DOOR, 0)); // Oak + self::addCreativeItem(Item::get(Item::WOODEN_DOOR, 1)); // Spruce + self::addCreativeItem(Item::get(Item::WOODEN_DOOR, 2)); // Birch + self::addCreativeItem(Item::get(Item::WOODEN_DOOR, 3)); // Jungle + self::addCreativeItem(Item::get(Item::WOODEN_DOOR, 4)); // Acacia + self::addCreativeItem(Item::get(Item::WOODEN_DOOR, 5)); // Dark oak + self::addCreativeItem(Item::get(Item::IRON_DOOR, 0)); self::addCreativeItem(Item::get(Item::TRAPDOOR, 0)); - self::addCreativeItem(Item::get(Item::FENCE, 0)); - self::addCreativeItem(Item::get(Item::FENCE, 1)); - self::addCreativeItem(Item::get(Item::FENCE, 2)); - self::addCreativeItem(Item::get(Item::FENCE, 3)); - self::addCreativeItem(Item::get(Item::FENCE, 4)); - self::addCreativeItem(Item::get(Item::FENCE, 5)); + self::addCreativeItem(Item::get(Item::IRON_TRAPDOOR, 0)); + self::addCreativeItem(Item::get(Item::FENCE, Fence::FENCE_OAK)); + self::addCreativeItem(Item::get(Item::FENCE, Fence::FENCE_SPRUCE)); + self::addCreativeItem(Item::get(Item::FENCE, Fence::FENCE_BIRCH)); + self::addCreativeItem(Item::get(Item::FENCE, Fence::FENCE_JUNGLE)); + self::addCreativeItem(Item::get(Item::FENCE, Fence::FENCE_ACACIA)); + self::addCreativeItem(Item::get(Item::FENCE, Fence::FENCE_DARKOAK)); + self::addCreativeItem(Item::get(Item::NETHER_BRICK_FENCE, 0)); self::addCreativeItem(Item::get(Item::FENCE_GATE, 0)); - self::addCreativeItem(Item::get(Item::FENCE_GATE_BIRCH, 0)); self::addCreativeItem(Item::get(Item::FENCE_GATE_SPRUCE, 0)); - self::addCreativeItem(Item::get(Item::FENCE_GATE_DARK_OAK, 0)); + self::addCreativeItem(Item::get(Item::FENCE_GATE_BIRCH, 0)); self::addCreativeItem(Item::get(Item::FENCE_GATE_JUNGLE, 0)); self::addCreativeItem(Item::get(Item::FENCE_GATE_ACACIA, 0)); + self::addCreativeItem(Item::get(Item::FENCE_GATE_DARK_OAK, 0)); self::addCreativeItem(Item::get(Item::IRON_BARS, 0)); self::addCreativeItem(Item::get(Item::BED, 0)); self::addCreativeItem(Item::get(Item::BOOKSHELF, 0)); @@ -644,8 +741,14 @@ class Item{ self::addCreativeItem(Item::get(Item::WORKBENCH, 0)); self::addCreativeItem(Item::get(Item::STONECUTTER, 0)); self::addCreativeItem(Item::get(Item::CHEST, 0)); + self::addCreativeItem(Item::get(Item::TRAPPED_CHEST, 0)); self::addCreativeItem(Item::get(Item::FURNACE, 0)); + self::addCreativeItem(Item::get(Item::BREWING_STAND, 0)); + // TODO: Note Block self::addCreativeItem(Item::get(Item::END_PORTAL, 0)); + self::addCreativeItem(Item::get(Item::ANVIL, 0)); + self::addCreativeItem(Item::get(Item::ANVIL, 4)); + self::addCreativeItem(Item::get(Item::ANVIL, 8)); self::addCreativeItem(Item::get(Item::DANDELION, 0)); self::addCreativeItem(Item::get(Item::RED_FLOWER, Flower::TYPE_POPPY)); self::addCreativeItem(Item::get(Item::RED_FLOWER, Flower::TYPE_BLUE_ORCHID)); @@ -656,17 +759,18 @@ class Item{ self::addCreativeItem(Item::get(Item::RED_FLOWER, Flower::TYPE_WHITE_TULIP)); self::addCreativeItem(Item::get(Item::RED_FLOWER, Flower::TYPE_PINK_TULIP)); self::addCreativeItem(Item::get(Item::RED_FLOWER, Flower::TYPE_OXEYE_DAISY)); - //TODO: Lilac - //TODO: Double Tallgrass - //TODO: Large Fern - //TODO: Rose Bush - //TODO: Peony + // TODO: Sunflower + // TODO: Lilac + // TODO: Double Tallgrass + // TODO: Large Fern + // TODO: Rose Bush + // TODO: Peony self::addCreativeItem(Item::get(Item::BROWN_MUSHROOM, 0)); self::addCreativeItem(Item::get(Item::RED_MUSHROOM, 0)); - //TODO: Mushroom block (brown, cover) - //TODO: Mushroom block (red, cover) - //TODO: Mushroom block (brown, stem) - //TODO: Mushroom block (red, stem) + // TODO: Mushroom block (brown, cover) + // TODO: Mushroom block (red, cover) + // TODO: Mushroom block (brown, stem) + // TODO: Mushroom block (red, stem) self::addCreativeItem(Item::get(Item::CACTUS, 0)); self::addCreativeItem(Item::get(Item::MELON_BLOCK, 0)); self::addCreativeItem(Item::get(Item::PUMPKIN, 0)); @@ -689,8 +793,15 @@ class Item{ self::addCreativeItem(Item::get(Item::LEAVES2, 0)); self::addCreativeItem(Item::get(Item::LEAVES2, 1)); self::addCreativeItem(Item::get(Item::CAKE, 0)); + // TODO: Skeleton skull + // TODO: Wither skeleton skull + // TODO: Zombie head + // TODO: Head + // TODO: Creeper head self::addCreativeItem(Item::get(Item::SIGN, 0)); + self::addCreativeItem(Item::get(Item::FLOWER_POT, 0)); self::addCreativeItem(Item::get(Item::MONSTER_SPAWNER, 0)); + self::addCreativeItem(Item::get(Item::ENCHANTMENT_TABLE, 0)); self::addCreativeItem(Item::get(Item::WOOL, 0)); self::addCreativeItem(Item::get(Item::WOOL, 7)); self::addCreativeItem(Item::get(Item::WOOL, 6)); @@ -725,58 +836,183 @@ class Item{ self::addCreativeItem(Item::get(Item::CARPET, 8)); //Tools - //TODO self::addCreativeItem(Item::get(Item::RAILS, 0)); - //TODO self::addCreativeItem(Item::get(Item::POWERED_RAILS, 0)); + // TODO: self::addCreativeItem(Item::get(Item::RAILS, 0)); + // TODO: self::addCreativeItem(Item::get(Item::POWERED_RAILS, 0)); + // TODO: Detector rail + // TODO: Activator rail self::addCreativeItem(Item::get(Item::TORCH, 0)); self::addCreativeItem(Item::get(Item::BUCKET, 0)); self::addCreativeItem(Item::get(Item::BUCKET, 1)); self::addCreativeItem(Item::get(Item::BUCKET, 8)); self::addCreativeItem(Item::get(Item::BUCKET, 10)); self::addCreativeItem(Item::get(Item::TNT, 0)); - self::addCreativeItem(Item::get(Item::IRON_HOE, 0)); - self::addCreativeItem(Item::get(Item::IRON_SHOVEL, 0)); - self::addCreativeItem(Item::get(Item::IRON_SWORD, 0)); + self::addCreativeItem(Item::get(Item::REDSTONE, 0)); self::addCreativeItem(Item::get(Item::BOW, 0)); - self::addCreativeItem(Item::get(Item::SHEARS, 0)); + self::addCreativeItem(Item::get(Item::FISHING_ROD, 0)); self::addCreativeItem(Item::get(Item::FLINT_AND_STEEL, 0)); + self::addCreativeItem(Item::get(Item::SHEARS, 0)); self::addCreativeItem(Item::get(Item::CLOCK, 0)); self::addCreativeItem(Item::get(Item::COMPASS, 0)); self::addCreativeItem(Item::get(Item::MINECART, 0)); + // TODO: Oak boat + // TODO: Spruce boat + // TODO: Birch boat + // TODO: Jungle boat + // TODO: Acacia boat + // TODO: Dark Oak boat self::addCreativeItem(Item::get(Item::SPAWN_EGG, Villager::NETWORK_ID)); //self::addCreativeItem(Item::get(Item::SPAWN_EGG, 10)); //Chicken //self::addCreativeItem(Item::get(Item::SPAWN_EGG, 11)); //Cow //self::addCreativeItem(Item::get(Item::SPAWN_EGG, 12)); //Pig //self::addCreativeItem(Item::get(Item::SPAWN_EGG, 13)); //Sheep - //TODO: Wolf - //TODO: Mooshroom - //TODO: Creeper - //TODO: Enderman - //TODO: Silverfish - //TODO: Skeleton - //TODO: Slime + // TODO: Wolf + // TODO: Ocelot + // TODO: Mooshroom + // TODO: Bat + // TODO: Rabbit + // TODO: Creeper + // TODO: Enderman + // TODO: Silverfish + // TODO: Skeleton + // TODO: Slime + // TODO: Spider self::addCreativeItem(Item::get(Item::SPAWN_EGG, Zombie::NETWORK_ID)); //TODO: PigZombie self::addCreativeItem(Item::get(Item::SPAWN_EGG, Squid::NETWORK_ID)); - + // TODO: Cave spider + // TODO: Magma cube + // TODO: Ghast + // TODO: Blaze + self::addCreativeItem(Item::get(Item::WOODEN_SWORD, 0)); + self::addCreativeItem(Item::get(Item::WOODEN_HOE, 0)); + self::addCreativeItem(Item::get(Item::WOODEN_SHOVEL, 0)); + self::addCreativeItem(Item::get(Item::WOODEN_PICKAXE, 0)); + self::addCreativeItem(Item::get(Item::WOODEN_AXE, 0)); + self::addCreativeItem(Item::get(Item::STONE_SWORD, 0)); + self::addCreativeItem(Item::get(Item::STONE_HOE, 0)); + self::addCreativeItem(Item::get(Item::STONE_SHOVEL, 0)); + self::addCreativeItem(Item::get(Item::STONE_PICKAXE, 0)); + self::addCreativeItem(Item::get(Item::STONE_AXE, 0)); + self::addCreativeItem(Item::get(Item::IRON_SWORD, 0)); + self::addCreativeItem(Item::get(Item::IRON_HOE, 0)); + self::addCreativeItem(Item::get(Item::IRON_SHOVEL, 0)); + self::addCreativeItem(Item::get(Item::IRON_PICKAXE, 0)); + self::addCreativeItem(Item::get(Item::IRON_AXE, 0)); + self::addCreativeItem(Item::get(Item::DIAMOND_SWORD, 0)); + self::addCreativeItem(Item::get(Item::DIAMOND_HOE, 0)); + self::addCreativeItem(Item::get(Item::DIAMOND_SHOVEL, 0)); + self::addCreativeItem(Item::get(Item::DIAMOND_PICKAXE, 0)); + self::addCreativeItem(Item::get(Item::DIAMOND_AXE, 0)); + self::addCreativeItem(Item::get(Item::GOLD_SWORD, 0)); + self::addCreativeItem(Item::get(Item::GOLD_HOE, 0)); + self::addCreativeItem(Item::get(Item::GOLD_SHOVEL, 0)); + self::addCreativeItem(Item::get(Item::GOLD_PICKAXE, 0)); + self::addCreativeItem(Item::get(Item::GOLD_AXE, 0)); + self::addCreativeItem(Item::get(Item::LEATHER_CAP, 0)); + self::addCreativeItem(Item::get(Item::LEATHER_TUNIC, 0)); + self::addCreativeItem(Item::get(Item::LEATHER_PANTS, 0)); + self::addCreativeItem(Item::get(Item::LEATHER_BOOTS, 0)); + self::addCreativeItem(Item::get(Item::CHAIN_HELMET, 0)); + self::addCreativeItem(Item::get(Item::CHAIN_CHESTPLATE, 0)); + self::addCreativeItem(Item::get(Item::CHAIN_LEGGINGS, 0)); + self::addCreativeItem(Item::get(Item::CHAIN_BOOTS, 0)); + self::addCreativeItem(Item::get(Item::IRON_HELMET, 0)); + self::addCreativeItem(Item::get(Item::IRON_CHESTPLATE, 0)); + self::addCreativeItem(Item::get(Item::IRON_LEGGINGS, 0)); + self::addCreativeItem(Item::get(Item::IRON_BOOTS, 0)); + self::addCreativeItem(Item::get(Item::DIAMOND_HELMET, 0)); + self::addCreativeItem(Item::get(Item::DIAMOND_CHESTPLATE, 0)); + self::addCreativeItem(Item::get(Item::DIAMOND_LEGGINGS, 0)); + self::addCreativeItem(Item::get(Item::DIAMOND_BOOTS, 0)); + self::addCreativeItem(Item::get(Item::GOLD_HELMET, 0)); + self::addCreativeItem(Item::get(Item::GOLD_CHESTPLATE, 0)); + self::addCreativeItem(Item::get(Item::GOLD_LEGGINGS, 0)); + self::addCreativeItem(Item::get(Item::GOLD_BOOTS, 0)); + // TODO: Lever + // TODO: Redstone lamp + // TODO: Redstone torch + // TODO: Wood pressure plate + // TODO: Stone pressure plate + // TODO: Weighted pressure plate light + // TODO: Weighted pressure plate heavy + // TODO: Wood button + // TODO: Stone button + // TODO: Daylight sensor + // TODO: Tripwire hook self::addCreativeItem(Item::get(Item::SNOWBALL)); - //Seeds + self::addCreativeItem(Item::get(Item::COAL, 0)); + self::addCreativeItem(Item::get(Item::COAL, 1)); + self::addCreativeItem(Item::get(Item::DIAMOND, 0)); + self::addCreativeItem(Item::get(Item::IRON_INGOT, 0)); + self::addCreativeItem(Item::get(Item::GOLD_INGOT, 0)); + self::addCreativeItem(Item::get(Item::EMERALD, 0)); + self::addCreativeItem(Item::get(Item::STICK, 0)); + self::addCreativeItem(Item::get(Item::BOWL, 0)); + self::addCreativeItem(Item::get(Item::STRING, 0)); + self::addCreativeItem(Item::get(Item::FEATHER, 0)); + self::addCreativeItem(Item::get(Item::FLINT, 0)); + self::addCreativeItem(Item::get(Item::LEATHER, 0)); + // TODO: Rabbit hide + self::addCreativeItem(Item::get(Item::CLAY, 0)); + self::addCreativeItem(Item::get(Item::SUGAR, 0)); + self::addCreativeItem(Item::get(Item::NETHER_QUARTZ, 0)); + self::addCreativeItem(Item::get(Item::PAPER, 0)); + self::addCreativeItem(Item::get(Item::BOOK, 0)); + self::addCreativeItem(Item::get(Item::ARROW, 0)); + self::addCreativeItem(Item::get(Item::BONE, 0)); self::addCreativeItem(Item::get(Item::SUGARCANE, 0)); self::addCreativeItem(Item::get(Item::WHEAT, 0)); self::addCreativeItem(Item::get(Item::SEEDS, 0)); - self::addCreativeItem(Item::get(Item::MELON_SEEDS, 0)); self::addCreativeItem(Item::get(Item::PUMPKIN_SEEDS, 0)); - self::addCreativeItem(Item::get(Item::CARROT, 0)); - self::addCreativeItem(Item::get(Item::POTATO, 0)); + self::addCreativeItem(Item::get(Item::MELON_SEEDS, 0)); self::addCreativeItem(Item::get(Item::BEETROOT_SEEDS, 0)); self::addCreativeItem(Item::get(Item::EGG, 0)); + self::addCreativeItem(Item::get(Item::APPLE, 0)); + self::addCreativeItem(Item::get(Item::GOLDEN_APPLE, 0)); + // TODO: Golden apple enchanted self::addCreativeItem(Item::get(Item::RAW_FISH, 0)); - self::addCreativeItem(Item::get(Item::RAW_FISH, 1)); - self::addCreativeItem(Item::get(Item::RAW_FISH, 2)); - self::addCreativeItem(Item::get(Item::RAW_FISH, 3)); + self::addCreativeItem(Item::get(Item::RAW_FISH, 1)); // TODO: Raw salmon + self::addCreativeItem(Item::get(Item::RAW_FISH, 2)); // TODO: Clownfish + self::addCreativeItem(Item::get(Item::RAW_FISH, 3)); // TODO: Pufferfish self::addCreativeItem(Item::get(Item::COOKED_FISH, 0)); - self::addCreativeItem(Item::get(Item::COOKED_FISH, 1)); + self::addCreativeItem(Item::get(Item::COOKED_FISH, 1)); //salmon + // TODO: Rotten flesh + // TODO: Mushroom stew + self::addCreativeItem(Item::get(Item::BREAD, 0)); + self::addCreativeItem(Item::get(Item::RAW_PORKCHOP, 0)); + self::addCreativeItem(Item::get(Item::COOKED_PORKCHOP, 0)); + self::addCreativeItem(Item::get(Item::RAW_CHICKEN, 0)); + self::addCreativeItem(Item::get(Item::COOKED_CHICKEN, 0)); + self::addCreativeItem(Item::get(Item::RAW_BEEF, 0)); + self::addCreativeItem(Item::get(Item::STEAK, 0)); + self::addCreativeItem(Item::get(Item::MELON, 0)); + self::addCreativeItem(Item::get(Item::CARROT, 0)); + self::addCreativeItem(Item::get(Item::POTATO, 0)); + self::addCreativeItem(Item::get(Item::BAKED_POTATO, 0)); + // TODO: Poisonous potato + self::addCreativeItem(Item::get(Item::COOKIE, 0)); + self::addCreativeItem(Item::get(Item::PUMPKIN_PIE, 0)); + // TODO: Raw rabbit + // TODO: Cooked rabbit + // TODO: Rabbit stew + // TODO: Magma cream + // TODO: Blaze rod + self::addCreativeItem(Item::get(Item::GOLD_NUGGET, 0)); + // TODO: Golden carrot + // TODO: Glistering melon + // TODO: Rabbit's foot + // TODO: Ghast tear + self::addCreativeItem(Item::get(Item::SLIMEBALL, 0)); + // TODO: Blaze powder + // TODO: Nether wart + self::addCreativeItem(Item::get(Item::GUNPOWDER, 0)); + self::addCreativeItem(Item::get(Item::GLOWSTONE_DUST, 0)); + // TODO: Spider eye + // TODO: Fermented spider eye + // TODO: Bottle o' enchanting + // TODO: Enchanted books self::addCreativeItem(Item::get(Item::DYE, 0)); self::addCreativeItem(Item::get(Item::DYE, 7)); self::addCreativeItem(Item::get(Item::DYE, 6)); @@ -793,6 +1029,9 @@ class Item{ self::addCreativeItem(Item::get(Item::DYE, 10)); self::addCreativeItem(Item::get(Item::DYE, 9)); self::addCreativeItem(Item::get(Item::DYE, 8)); + // TODO: Glass bottle + // TODO: Water bottle + // TODO: Potions } public static function clearCreativeItems(){ @@ -846,18 +1085,18 @@ class Item{ return -1; } - public static function get($id, $meta = 0, $count = 1){ + public static function get($id, $meta = 0, $count = 1, $tags = ""){ try{ $class = self::$list[$id]; if($class === null){ - return new Item($id, $meta, $count); + return (new Item($id, $meta, $count))->setCompoundTag($tags); }elseif($id < 256){ - return new ItemBlock(new $class($meta), $meta, $count); + return (new ItemBlock(new $class($meta), $meta, $count))->setCompoundTag($tags); }else{ - return new $class($meta, $count); + return (new $class($meta, $count))->setCompoundTag($tags); } }catch(\RuntimeException $e){ - return new Item($id, $meta, $count); + return (new Item($id, $meta, $count))->setCompoundTag($tags); } } @@ -870,7 +1109,7 @@ class Item{ return $blocks; }else{ - $b = explode(":", str_replace(" ", "_", trim($str))); + $b = explode(":", str_replace([" ", "minecraft:"], ["_", ""], trim($str))); if(!isset($b[1])){ $meta = 0; }else{ @@ -901,6 +1140,281 @@ class Item{ } } + public function setCompoundTag($tags){ + if($tags instanceof Compound){ + $this->setNamedTag($tags); + }else{ + $this->tags = $tags; + $this->cachedNBT = null; + } + + return $this; + } + + /** + * @return string + */ + public function getCompoundTag(){ + return $this->tags; + } + + public function hasCompoundTag(){ + return $this->tags !== "" and $this->tags !== null; + } + + public function hasCustomBlockData(){ + if(!$this->hasCompoundTag()){ + return false; + } + + $tag = $this->getNamedTag(); + if(isset($tag->BlockEntityTag) and $tag->BlockEntityTag instanceof Compound){ + return true; + } + + return false; + } + + public function clearCustomBlockData(){ + if(!$this->hasCompoundTag()){ + return $this; + } + $tag = $this->getNamedTag(); + + if(isset($tag->BlockEntityTag) and $tag->BlockEntityTag instanceof Compound){ + unset($tag->display->BlockEntityTag); + $this->setNamedTag($tag); + } + + return $this; + } + + public function setCustomBlockData(Compound $compound){ + $tags = clone $compound; + $tags->setName("BlockEntityTag"); + + if(!$this->hasCompoundTag()){ + $tag = new Compound("", []); + }else{ + $tag = $this->getNamedTag(); + } + + $tag->BlockEntityTag = $tags; + $this->setNamedTag($tag); + + return $this; + } + + public function getCustomBlockData(){ + if(!$this->hasCompoundTag()){ + return null; + } + + $tag = $this->getNamedTag(); + if(isset($tag->BlockEntityTag) and $tag->BlockEntityTag instanceof Compound){ + return $tag->BlockEntityTag; + } + + return null; + } + + public function hasEnchantments(){ + if(!$this->hasCompoundTag()){ + return false; + } + + $tag = $this->getNamedTag(); + if(isset($tag->ench)){ + $tag = $tag->ench; + if($tag instanceof Enum){ + return true; + } + } + + return false; + } + + /** + * @param $id + * @return Enchantment|null + */ + public function getEnchantment($id){ + if(!$this->hasEnchantments()){ + return null; + } + + foreach($this->getNamedTag()->ench as $entry){ + if($entry["id"] === $id){ + $e = Enchantment::getEnchantment($entry["id"]); + $e->setLevel($entry["lvl"]); + return $e; + } + } + + return null; + } + + /** + * @param Enchantment $ench + */ + public function addEnchantment(Enchantment $ench){ + if(!$this->hasCompoundTag()){ + $tag = new Compound("", []); + }else{ + $tag = $this->getNamedTag(); + } + + if(!isset($tag->ench)){ + $tag->ench = new Enum("ench", []); + $tag->ench->setTagType(NBT::TAG_Compound); + } + + $found = false; + + foreach($tag->ench as $k => $entry){ + if($entry["id"] === $ench->getId()){ + $tag->ench->{$k} = new Compound("", [ + "id" => new Short("id", $ench->getId()), + "lvl" => new Short("lvl", $ench->getLevel()) + ]); + $found = true; + break; + } + } + + if(!$found){ + $tag->ench->{count($tag->ench) + 1} = new Compound("", [ + "id" => new Short("id", $ench->getId()), + "lvl" => new Short("lvl", $ench->getLevel()) + ]); + } + + $this->setNamedTag($tag); + } + + /** + * @return Enchantment[] + */ + public function getEnchantments(){ + if(!$this->hasEnchantments()){ + return []; + } + + $enchantments = []; + + foreach($this->getNamedTag()->ench as $entry){ + $e = Enchantment::getEnchantment($entry["id"]); + $e->setLevel($entry["lvl"]); + $enchantments[] = $e; + } + + return $enchantments; + } + + public function hasCustomName(){ + if(!$this->hasCompoundTag()){ + return false; + } + + $tag = $this->getNamedTag(); + if(isset($tag->display)){ + $tag = $tag->display; + if($tag instanceof Compound and isset($tag->Name) and $tag->Name instanceof String){ + return true; + } + } + + return false; + } + + public function getCustomName(){ + if(!$this->hasCompoundTag()){ + return ""; + } + + $tag = $this->getNamedTag(); + if(isset($tag->display)){ + $tag = $tag->display; + if($tag instanceof Compound and isset($tag->Name) and $tag->Name instanceof String){ + return $tag->Name->getValue(); + } + } + + return ""; + } + + public function setCustomName($name){ + if((string) $name === ""){ + $this->clearCustomName(); + } + + if(!$this->hasCompoundTag()){ + $tag = new Compound("", []); + }else{ + $tag = $this->getNamedTag(); + } + + if(isset($tag->display) and $tag->display instanceof Compound){ + $tag->display->Name = new String("Name", $name); + }else{ + $tag->display = new Compound("display", [ + "Name" => new String("Name", $name) + ]); + } + + return $this; + } + + public function clearCustomName(){ + if(!$this->hasCompoundTag()){ + return $this; + } + $tag = $this->getNamedTag(); + + if(isset($tag->display) and $tag->display instanceof Compound){ + unset($tag->display->Name); + if($tag->display->getCount() === 0){ + unset($tag->display); + } + + $this->setNamedTag($tag); + } + + return $this; + } + + public function getNamedTagEntry($name){ + $tag = $this->getNamedTag(); + if($tag !== null){ + return isset($tag->{$name}) ? $tag->{$name} : null; + } + + return null; + } + + public function getNamedTag(){ + if(!$this->hasCompoundTag()){ + return null; + }elseif($this->cachedNBT !== null){ + return $this->cachedNBT; + } + return $this->cachedNBT = self::parseCompoundTag($this->tags); + } + + public function setNamedTag(Compound $tag){ + if($tag->getCount() === 0){ + return $this->clearNamedTag(); + } + + $this->cachedNBT = $tag; + $this->tags = self::writeCompoundTag($tag); + + return $this; + } + + public function clearNamedTag(){ + return $this->setCompoundTag(""); + } public function getCount(){ return $this->count; @@ -911,11 +1425,11 @@ class Item{ } final public function getName(){ - return $this->name; + return $this->hasCustomName() ? $this->getCustomName() : $this->name; } - final public function isPlaceable(){ - return (($this->block instanceof Block) and $this->block->isPlaceable === true); + final public function canBePlaced(){ + return $this->block !== null and $this->block->canBePlaced(); } public function getBlock(){ @@ -1001,7 +1515,7 @@ class Item{ } final public function __toString(){ - return "Item " . $this->name . " (" . $this->id . ":" . ($this->meta === null ? "?" : $this->meta) . ")x" . $this->count; + return "Item " . $this->name . " (" . $this->id . ":" . ($this->meta === null ? "?" : $this->meta) . ")x" . $this->count . ($this->hasCompoundTag() ? " tags:0x".bin2hex($this->getCompoundTag()) : ""); } public function getDestroySpeed(Block $block, Player $player){ @@ -1012,8 +1526,18 @@ class Item{ return false; } - public final function equals(Item $item, $checkDamage = false){ - return $this->id === $item->getId() and ($checkDamage === false or $this->getDamage() === $item->getDamage()); + public final function equals(Item $item, $checkDamage = true, $checkCompound = true){ + return $this->id === $item->getId() and ($checkDamage === false or $this->getDamage() === $item->getDamage()) and ($checkCompound === false or $this->getCompoundTag() === $item->getCompoundTag()); + } + + public final function deepEquals(Item $item, $checkDamage = true, $checkCompound = true){ + if($item->equals($item, $checkDamage, $checkCompound)){ + return true; + }elseif($item->hasCompoundTag() or $this->hasCompoundTag()){ + return NBT::matchTree($this->getNamedTag(), $item->getNamedTag()); + } + + return false; } } diff --git a/src/pocketmine/item/Leather.php b/src/pocketmine/item/Leather.php new file mode 100644 index 000000000..56a17dcfd --- /dev/null +++ b/src/pocketmine/item/Leather.php @@ -0,0 +1,30 @@ +hasCustomName()){ + $nbt->CustomName = new String("CustomName", $this->getCustomName()); + } + $entity = Entity::createEntity($this->meta, $chunk, $nbt); if($entity instanceof Entity){ @@ -72,7 +77,6 @@ class SpawnEgg extends Item{ --$this->count; } $entity->spawnToAll(); - return true; } diff --git a/src/pocketmine/item/Steak.php b/src/pocketmine/item/Steak.php new file mode 100644 index 000000000..bc18b0a1a --- /dev/null +++ b/src/pocketmine/item/Steak.php @@ -0,0 +1,30 @@ +isHoe()){ + if($this->isUnbreakable()){ + return true; + } + + if($object instanceof Block){ + if( + $object->getToolType() === Tool::TYPE_PICKAXE and $this->isPickaxe() or + $object->getToolType() === Tool::TYPE_SHOVEL and $this->isShovel() or + $object->getToolType() === Tool::TYPE_AXE and $this->isAxe() or + $object->getToolType() === Tool::TYPE_SWORD and $this->isSword() or + $object->getToolType() === Tool::TYPE_SHEARS and $this->isShears() + ){ + $this->meta++; + }elseif(!$this->isShears() and $object->getBreakTime($this) > 0){ + $this->meta += 2; + } + }elseif($this->isHoe()){ if(($object instanceof Block) and ($object->getId() === self::GRASS or $object->getId() === self::DIRT)){ $this->meta++; } @@ -101,6 +118,11 @@ abstract class Tool extends Item{ return $levels[$type]; } + public function isUnbreakable(){ + $tag = $this->getNamedTagEntry("Unbreakable"); + return $tag !== null and $tag->getValue() > 0; + } + public function isPickaxe(){ return false; } diff --git a/src/pocketmine/item/Wheat.php b/src/pocketmine/item/Wheat.php new file mode 100644 index 000000000..b853c6d29 --- /dev/null +++ b/src/pocketmine/item/Wheat.php @@ -0,0 +1,30 @@ +id = (int) $id; + $this->name = (string) $name; + $this->rarity = (int) $rarity; + $this->activationType = (int) $activationType; + $this->slot = (int) $slot; + } + + public function getId(){ + return $this->id; + } + + public function getName(){ + return $this->name; + } + + public function getRarity(){ + return $this->rarity; + } + + public function getActivationType(){ + return $this->activationType; + } + + public function getSlot(){ + return $this->slot; + } + + public function hasSlot($slot){ + return ($this->slot & $slot) > 0; + } + + public function getLevel(){ + return $this->level; + } + + public function setLevel($level){ + $this->level = (int) $level; + + return $this; + } + +} \ No newline at end of file diff --git a/src/pocketmine/item/enchantment/EnchantmentEntry.php b/src/pocketmine/item/enchantment/EnchantmentEntry.php new file mode 100644 index 000000000..e1a40d37c --- /dev/null +++ b/src/pocketmine/item/enchantment/EnchantmentEntry.php @@ -0,0 +1,55 @@ +enchantments = $enchantments; + $this->cost = (int) $cost; + $this->randomName = $randomName; + } + + public function getEnchantments(){ + return $this->enchantments; + } + + public function getCost(){ + return $this->cost; + } + + public function getRandomName(){ + return $this->randomName; + } + +} \ No newline at end of file diff --git a/src/pocketmine/item/enchantment/EnchantmentList.php b/src/pocketmine/item/enchantment/EnchantmentList.php new file mode 100644 index 000000000..a4fbd09cc --- /dev/null +++ b/src/pocketmine/item/enchantment/EnchantmentList.php @@ -0,0 +1,54 @@ +enchantments = new \SplFixedArray($size); + } + + /** + * @param $slot + * @param EnchantmentEntry $entry + */ + public function setSlot($slot, EnchantmentEntry $entry){ + $this->enchantments[$slot] = $entry; + } + + /** + * @param $slot + * @return EnchantmentEntry + */ + public function getSlot($slot){ + return $this->enchantments[$slot]; + } + + public function getSize(){ + return $this->enchantments->getSize(); + } + +} \ No newline at end of file diff --git a/src/pocketmine/lang/locale b/src/pocketmine/lang/locale index 5a8cc27b7..a7d53d922 160000 --- a/src/pocketmine/lang/locale +++ b/src/pocketmine/lang/locale @@ -1 +1 @@ -Subproject commit 5a8cc27b7ee6f8f1d16a4fe45e57676ee9b57c5a +Subproject commit a7d53d9229337323220c7370916a153accc0ebf3 diff --git a/src/pocketmine/level/Explosion.php b/src/pocketmine/level/Explosion.php index 0cadd5ecc..8f6270cd5 100644 --- a/src/pocketmine/level/Explosion.php +++ b/src/pocketmine/level/Explosion.php @@ -39,7 +39,7 @@ use pocketmine\nbt\tag\Enum; use pocketmine\nbt\tag\Float; use pocketmine\network\Network; use pocketmine\network\protocol\ExplodePacket; -use pocketmine\Server; + use pocketmine\utils\Random; class Explosion{ @@ -233,7 +233,7 @@ class Explosion{ $pk->z = $this->source->z; $pk->radius = $this->size; $pk->records = $send; - $this->level->addChunkPacket($source->x >> 4, $source->z >> 4, $pk->setChannel(Network::CHANNEL_BLOCKS)); + $this->level->addChunkPacket($source->x >> 4, $source->z >> 4, $pk); return true; } diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 1fd7e3336..dec5d55aa 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -79,7 +79,8 @@ use pocketmine\math\Vector3; use pocketmine\metadata\BlockMetadataStore; use pocketmine\metadata\Metadatable; use pocketmine\metadata\MetadataValue; -use pocketmine\nbt\tag\Byte; +use pocketmine\nbt\NBT; + use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\Double; use pocketmine\nbt\tag\Enum; @@ -89,6 +90,7 @@ use pocketmine\nbt\tag\Short; use pocketmine\nbt\tag\String; use pocketmine\network\Network; use pocketmine\network\protocol\DataPacket; +use pocketmine\network\protocol\FullChunkDataPacket; use pocketmine\network\protocol\LevelEventPacket; use pocketmine\network\protocol\MoveEntityPacket; use pocketmine\network\protocol\SetEntityMotionPacket; @@ -96,7 +98,7 @@ use pocketmine\network\protocol\SetTimePacket; use pocketmine\network\protocol\UpdateBlockPacket; use pocketmine\Player; use pocketmine\plugin\Plugin; -use pocketmine\scheduler\AsyncTask; + use pocketmine\Server; use pocketmine\tile\Chest; use pocketmine\tile\Tile; @@ -280,6 +282,10 @@ class Level implements ChunkManager, Metadatable{ return PHP_INT_SIZE === 8 ? (($x & 0xFFFFFFF) << 35) | (($y & 0x7f) << 28) | ($z & 0xFFFFFFF) : $x . ":" . $y .":". $z; } + public static function chunkBlockHash($x, $y, $z){ + return ($x << 11) | ($z << 7) | $y; + } + public static function getBlockXYZ($hash, &$x, &$y, &$z){ if(PHP_INT_SIZE === 8){ $x = ($hash >> 35) << 36 >> 36; @@ -460,9 +466,9 @@ class Level implements ChunkManager, Metadatable{ }else{ if($pk !== null){ if(!is_array($pk)){ - Server::broadcastPacket($players, $pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + Server::broadcastPacket($players, $pk); }else{ - $this->server->batchPackets($players, $pk, false, Network::CHANNEL_WORLD_EVENTS); + $this->server->batchPackets($players, $pk, false); } } } @@ -484,9 +490,9 @@ class Level implements ChunkManager, Metadatable{ }else{ if($pk !== null){ if(!is_array($pk)){ - Server::broadcastPacket($players, $pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + Server::broadcastPacket($players, $pk); }else{ - $this->server->batchPackets($players, $pk, false, Network::CHANNEL_WORLD_EVENTS); + $this->server->batchPackets($players, $pk, false); } } } @@ -652,7 +658,7 @@ class Level implements ChunkManager, Metadatable{ $pk->time = (int) $this->time; $pk->started = $this->stopTime == false; - Server::broadcastPacket($this->players, $pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + Server::broadcastPacket($this->players, $pk); } /** @@ -745,7 +751,7 @@ class Level implements ChunkManager, Metadatable{ Level::getXZ($index, $chunkX, $chunkZ); $pk = new MoveEntityPacket(); $pk->entities = $entry; - $this->addChunkPacket($chunkX, $chunkZ, $pk->setChannel(Network::CHANNEL_MOVEMENT)); + $this->addChunkPacket($chunkX, $chunkZ, $pk); } $this->moveToSend = []; @@ -753,7 +759,7 @@ class Level implements ChunkManager, Metadatable{ Level::getXZ($index, $chunkX, $chunkZ); $pk = new SetEntityMotionPacket(); $pk->entities = $entry; - $this->addChunkPacket($chunkX, $chunkZ, $pk->setChannel(Network::CHANNEL_MOVEMENT)); + $this->addChunkPacket($chunkX, $chunkZ, $pk); } $this->motionToSend = []; @@ -798,6 +804,17 @@ class Level implements ChunkManager, Metadatable{ } } + public function sendBlockExtraData($x, $y, $z, $id, $data, array $targets = null){ + $pk = new LevelEventPacket; + $pk->evid = LevelEventPacket::EVENT_SET_DATA; + $pk->x = $x + 0.5; + $pk->y = $y + 0.5; + $pk->z = $z + 0.5; + $pk->data = ($data << 8) | $id; + + Server::broadcastPacket($targets === null ? $this->getChunkPlayers($x >> 4, $z >> 4) : $targets, $pk); + } + /** * @param Player[] $target * @param Block[] $blocks @@ -842,7 +859,7 @@ class Level implements ChunkManager, Metadatable{ } - Server::broadcastPacket($target, $pk->setChannel(Network::CHANNEL_BLOCKS)); + Server::broadcastPacket($target, $pk); } public function clearCache($full = false){ @@ -1445,6 +1462,9 @@ class Level implements ChunkManager, Metadatable{ */ public function dropItem(Vector3 $source, Item $item, Vector3 $motion = null, $delay = 10){ $motion = $motion === null ? new Vector3(lcg_value() * 0.2 - 0.1, 0.2, lcg_value() * 0.2 - 0.1) : $motion; + $itemTag = NBT::putItemHelper($item); + $itemTag->setName("Item"); + if($item->getId() > 0 and $item->getCount() > 0){ $itemEntity = Entity::createEntity("Item", $this->getChunk($source->getX() >> 4, $source->getZ() >> 4, true), new Compound("", [ "Pos" => new Enum("Pos", [ @@ -1463,11 +1483,7 @@ class Level implements ChunkManager, Metadatable{ 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()) - ]), + "Item" => $itemTag, "PickupDelay" => new Short("PickupDelay", $delay) ])); @@ -1511,7 +1527,12 @@ class Level implements ChunkManager, Metadatable{ return false; } - $breakTime = $player->isCreative() ? 0.15 : $target->getBreakTime($item); + $breakTime = $target->getBreakTime($item); + + if($player->isCreative() and $breakTime > 0.15){ + $breakTime = 0.15; + } + if($player->hasEffect(Effect::SWIFTNESS)){ $breakTime *= 1 - (0.2 * ($player->getEffect(Effect::SWIFTNESS)->getAmplifier() + 1)); } @@ -1526,7 +1547,7 @@ class Level implements ChunkManager, Metadatable{ return false; } - $player->lastBreak = PHP_INT_MAX; + $player->lastBreak = microtime(true); $drops = $ev->getDrops(); @@ -1546,19 +1567,31 @@ class Level implements ChunkManager, Metadatable{ } } + $tag = $item->getNamedTagEntry("CanDestroy"); + if($tag instanceof Enum){ + $canBreak = false; + foreach($tag as $v){ + if($v instanceof String){ + $entry = Item::fromString($v->getValue()); + if($entry->getId() > 0 and $entry->getBlock() !== null and $entry->getBlock()->getId() === $target->getId()){ + $canBreak = true; + break; + } + } + } + + if(!$canBreak){ + return false; + } + } + if($createParticles){ $players = $this->getChunkPlayers($target->x >> 4, $target->z >> 4); if($player !== null){ unset($players[$player->getLoaderId()]); } - $pk = new LevelEventPacket(); - $pk->evid = 2001; - $pk->x = $target->x + 0.5; - $pk->y = $target->y + 0.5; - $pk->z = $target->z + 0.5; - $pk->data = $target->getId() + ($target->getDamage() << 12); - Server::broadcastPacket($players, $pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $this->addParticle(new DestroyBlockParticle($target->add(0.5), $target), $players); } $target->onBreak($item); @@ -1633,11 +1666,11 @@ class Level implements ChunkManager, Metadatable{ $this->server->getPluginManager()->callEvent($ev); if(!$ev->isCancelled()){ $target->onUpdate(self::BLOCK_UPDATE_TOUCH); - if($target->canBeActivated() === true and $target->onActivate($item, $player) === true){ + if(!$player->isSneaking() and $target->canBeActivated() === true and $target->onActivate($item, $player) === true){ return true; } - if($item->canBeActivated() and $item->onActivate($this, $player, $block, $target, $face, $fx, $fy, $fz)){ + if(!$player->isSneaking() and $item->canBeActivated() and $item->onActivate($this, $player, $block, $target, $face, $fx, $fy, $fz)){ if($item->getCount() <= 0){ $item = Item::get(Item::AIR, 0, 0); @@ -1651,7 +1684,7 @@ class Level implements ChunkManager, Metadatable{ return true; } - if($item->isPlaceable()){ + if($item->canBePlaced()){ $hand = $item->getBlock(); $hand->position($block); }elseif($block->getId() === Item::FIRE){ @@ -1696,6 +1729,24 @@ class Level implements ChunkManager, Metadatable{ } } + $tag = $item->getNamedTagEntry("CanPlaceOn"); + if($tag instanceof Enum){ + $canPlace = false; + foreach($tag as $v){ + if($v instanceof String){ + $entry = Item::fromString($v->getValue()); + if($entry->getId() > 0 and $entry->getBlock() !== null and $entry->getBlock()->getId() === $target->getId()){ + $canPlace = true; + break; + } + } + } + + if(!$canPlace){ + return false; + } + } + if($player !== null){ $ev = new BlockPlaceEvent($player, $hand, $block, $target, $item); @@ -1717,7 +1768,8 @@ class Level implements ChunkManager, Metadatable{ } if($hand->getId() === Item::SIGN_POST or $hand->getId() === Item::WALL_SIGN){ - $tile = Tile::createTile("Sign", $this->getChunk($block->x >> 4, $block->z >> 4), new Compound("", [ + + $nbt = new Compound("", [ "id" => new String("id", Tile::SIGN), "x" => new Int("x", $block->x), "y" => new Int("y", $block->y), @@ -1726,10 +1778,19 @@ class Level implements ChunkManager, Metadatable{ "Text2" => new String("Text2", ""), "Text3" => new String("Text3", ""), "Text4" => new String("Text4", "") - ])); + ]); + if($player !== null){ - $tile->namedtag->Creator = new String("Creator", $player->getUniqueId()); + $nbt->Creator = new String("Creator", $player->getRawUniqueId()); } + + if($item->hasCustomBlockData()){ + foreach($item->getCustomBlockData() as $key => $v){ + $nbt->{$key} = $v; + } + } + + Tile::createTile("Sign", $this->getChunk($block->x >> 4, $block->z >> 4), $nbt); } $item->setCount($item->getCount() - 1); if($item->getCount() <= 0){ @@ -1926,6 +1987,34 @@ class Level implements ChunkManager, Metadatable{ } } + /** + * Gets the raw block extra data + * + * @param int $x + * @param int $y + * @param int $z + * + * @return int 16-bit + */ + public function getBlockExtraDataAt($x, $y, $z){ + return $this->getChunk($x >> 4, $z >> 4, true)->getBlockExtraData($x & 0x0f, $y & 0x7f, $z & 0x0f); + } + + /** + * Sets the raw block metadata. + * + * @param int $x + * @param int $y + * @param int $z + * @param int $id + * @param int $data + */ + public function setBlockExtraDataAt($x, $y, $z, $id, $data){ + $this->getChunk($x >> 4, $z >> 4, true)->setBlockExtraData($x & 0x0f, $y & 0x7f, $z & 0x0f, ($data << 8) | $id); + + $this->sendBlockExtraData($x, $y, $z, $id, $data); + } + /** * Gets the raw block metadata * @@ -2301,13 +2390,13 @@ class Level implements ChunkManager, Metadatable{ } } - public function chunkRequestCallback($x, $z, $payload){ + public function chunkRequestCallback($x, $z, $payload, $ordering = FullChunkDataPacket::ORDER_COLUMNS){ $this->timings->syncChunkSendTimer->startTiming(); $index = Level::chunkHash($x, $z); if(!isset($this->chunkCache[$index]) and $this->cacheChunks and $this->server->getMemoryManager()->canUseChunkCache()){ - $this->chunkCache[$index] = Player::getChunkCacheFromData($x, $z, $payload); + $this->chunkCache[$index] = Player::getChunkCacheFromData($x, $z, $payload, $ordering); $this->sendChunkFromCache($x, $z); $this->timings->syncChunkSendTimer->stopTiming(); return; @@ -2317,7 +2406,7 @@ class Level implements ChunkManager, Metadatable{ foreach($this->chunkSendQueue[$index] as $player){ /** @var Player $player */ if($player->isConnected() and isset($player->usedChunks[$index])){ - $player->sendChunk($x, $z, $payload); + $player->sendChunk($x, $z, $payload, $ordering); } } unset($this->chunkSendQueue[$index]); @@ -2575,13 +2664,18 @@ class Level implements ChunkManager, Metadatable{ $x = $v->x & 0x0f; $z = $v->z & 0x0f; if($chunk !== null){ - $y = (int) min(127, $v->y); + $y = (int) min(126, $v->y); + $wasAir = ($chunk->getBlockId($x, $y - 1, $z) === 0); for(; $y > 0; --$y){ $b = $chunk->getFullBlock($x, $y, $z); $block = Block::get($b >> 4, $b & 0x0f); if($this->isFullBlock($block)){ - $y++; - break; + if($wasAir){ + $y++; + break; + } + }else{ + $wasAir = true; } } diff --git a/src/pocketmine/level/format/FullChunk.php b/src/pocketmine/level/format/FullChunk.php index 5406b4f61..783a939ff 100644 --- a/src/pocketmine/level/format/FullChunk.php +++ b/src/pocketmine/level/format/FullChunk.php @@ -119,6 +119,23 @@ interface FullChunk{ */ public function setBlockData($x, $y, $z, $data); + /** + * @param int $x 0-15 + * @param int $y 0-127 + * @param int $z 0-15 + * + * @return int (16-bit) + */ + public function getBlockExtraData($x, $y, $z); + + /** + * @param int $x 0-15 + * @param int $y 0-127 + * @param int $z 0-15 + * @param int $data (16-bit) + */ + public function setBlockExtraData($x, $y, $z, $data); + /** * @param int $x 0-15 * @param int $y 0-127 @@ -312,6 +329,8 @@ interface FullChunk{ public function getBlockDataArray(); + public function getBlockExtraDataArray(); + public function getBlockSkyLightArray(); public function getBlockLightArray(); diff --git a/src/pocketmine/level/format/anvil/Anvil.php b/src/pocketmine/level/format/anvil/Anvil.php index 963e7c840..5e0ccd5a6 100644 --- a/src/pocketmine/level/format/anvil/Anvil.php +++ b/src/pocketmine/level/format/anvil/Anvil.php @@ -24,11 +24,16 @@ namespace pocketmine\level\format\anvil; use pocketmine\level\format\FullChunk; use pocketmine\level\format\mcregion\McRegion; use pocketmine\level\Level; +use pocketmine\nbt\NBT; use pocketmine\nbt\tag\Byte; use pocketmine\nbt\tag\ByteArray; use pocketmine\nbt\tag\Compound; +use pocketmine\network\protocol\FullChunkDataPacket; +use pocketmine\tile\Spawnable; +use pocketmine\utils\BinaryStream; use pocketmine\utils\ChunkException; + class Anvil extends McRegion{ /** @var RegionLoader[] */ @@ -66,7 +71,44 @@ class Anvil extends McRegion{ } public function requestChunkTask($x, $z){ - return new ChunkRequestTask($this->getLevel(), $this->getChunk($x, $z, true)); + $chunk = $this->getChunk($x, $z, false); + if(!($chunk instanceof Chunk)){ + throw new ChunkException("Invalid Chunk sent"); + } + + $tiles = ""; + + if(count($chunk->getTiles()) > 0){ + $nbt = new NBT(NBT::LITTLE_ENDIAN); + $list = []; + foreach($chunk->getTiles() as $tile){ + if($tile instanceof Spawnable){ + $list[] = $tile->getSpawnCompound(); + } + } + $nbt->setData($list); + $tiles = $nbt->write(); + } + + $extraData = new BinaryStream(); + $extraData->putLInt(count($chunk->getBlockExtraDataArray())); + foreach($chunk->getBlockExtraDataArray() as $key => $value){ + $extraData->putLInt($key); + $extraData->putLShort($value); + } + + $ordered = $chunk->getBlockIdArray() . + $chunk->getBlockDataArray() . + $chunk->getBlockSkyLightArray() . + $chunk->getBlockLightArray() . + pack("C*", ...$chunk->getHeightMapArray()) . + pack("N*", ...$chunk->getBiomeColorArray()) . + $extraData->getBuffer() . + $tiles; + + $this->getLevel()->chunkRequestCallback($x, $z, $ordered, FullChunkDataPacket::ORDER_LAYERED); + + return null; } /** diff --git a/src/pocketmine/level/format/anvil/Chunk.php b/src/pocketmine/level/format/anvil/Chunk.php index 5458e03b8..557db546e 100644 --- a/src/pocketmine/level/format/anvil/Chunk.php +++ b/src/pocketmine/level/format/anvil/Chunk.php @@ -34,6 +34,7 @@ use pocketmine\nbt\tag\IntArray; use pocketmine\nbt\tag\Long; use pocketmine\Player; use pocketmine\utils\Binary; +use pocketmine\utils\BinaryStream; class Chunk extends BaseChunk{ @@ -92,14 +93,27 @@ class Chunk extends BaseChunk{ } } - parent::__construct($level, (int) $this->nbt["xPos"], (int) $this->nbt["zPos"], $sections, $this->nbt->BiomeColors->getValue(), $this->nbt->HeightMap->getValue(), $this->nbt->Entities->getValue(), $this->nbt->TileEntities->getValue()); + $extraData = []; + + if(!isset($this->nbt->ExtraData) or !($this->nbt->ExtraData instanceof ByteArray)){ + $this->nbt->ExtraData = new ByteArray("ExtraData", Binary::writeInt(0)); + }else{ + $stream = new BinaryStream($this->nbt->ExtraData->getValue()); + $count = $stream->getInt(); + for($i = 0; $i < $count; ++$i){ + $key = $stream->getInt(); + $extraData[$key] = $stream->getShort(false); + } + } + + parent::__construct($level, (int) $this->nbt["xPos"], (int) $this->nbt["zPos"], $sections, $this->nbt->BiomeColors->getValue(), $this->nbt->HeightMap->getValue(), $this->nbt->Entities->getValue(), $this->nbt->TileEntities->getValue(), $extraData); if(isset($this->nbt->Biomes)){ $this->checkOldBiomes($this->nbt->Biomes->getValue()); unset($this->nbt->Biomes); } - unset($this->nbt->Sections); + unset($this->nbt->Sections, $this->nbt->ExtraData); } public function isLightPopulated(){ @@ -240,6 +254,16 @@ class Chunk extends BaseChunk{ $nbt->TileEntities = new Enum("TileEntities", $tiles); $nbt->TileEntities->setTagType(NBT::TAG_Compound); + + $extraData = new BinaryStream(); + $extraData->putInt(count($this->getBlockExtraDataArray())); + foreach($this->getBlockExtraDataArray() as $key => $value){ + $extraData->putInt($key); + $extraData->putShort($value); + } + + $nbt->ExtraData = new ByteArray("ExtraData", $extraData->getBuffer()); + $writer = new NBT(NBT::BIG_ENDIAN); $nbt->setName("Level"); $writer->setData(new Compound("", ["Level" => $nbt])); @@ -293,6 +317,16 @@ class Chunk extends BaseChunk{ $nbt->TileEntities = new Enum("TileEntities", $tiles); $nbt->TileEntities->setTagType(NBT::TAG_Compound); + + $extraData = new BinaryStream(); + $extraData->putInt(count($this->getBlockExtraDataArray())); + foreach($this->getBlockExtraDataArray() as $key => $value){ + $extraData->putInt($key); + $extraData->putShort($value); + } + + $nbt->ExtraData = new ByteArray("ExtraData", $extraData->getBuffer()); + $writer = new NBT(NBT::BIG_ENDIAN); $nbt->setName("Level"); $writer->setData(new Compound("", ["Level" => $nbt])); diff --git a/src/pocketmine/level/format/anvil/ChunkRequestTask.php b/src/pocketmine/level/format/anvil/ChunkRequestTask.php index 39ea4e1d0..2c7cd8c0a 100644 --- a/src/pocketmine/level/format/anvil/ChunkRequestTask.php +++ b/src/pocketmine/level/format/anvil/ChunkRequestTask.php @@ -26,8 +26,7 @@ use pocketmine\nbt\NBT; use pocketmine\scheduler\AsyncTask; use pocketmine\Server; use pocketmine\tile\Spawnable; -use pocketmine\utils\Binary; -use pocketmine\utils\ChunkException; + class ChunkRequestTask extends AsyncTask{ diff --git a/src/pocketmine/level/format/anvil/RegionLoader.php b/src/pocketmine/level/format/anvil/RegionLoader.php index 265e62889..4b13dc491 100644 --- a/src/pocketmine/level/format/anvil/RegionLoader.php +++ b/src/pocketmine/level/format/anvil/RegionLoader.php @@ -22,16 +22,7 @@ namespace pocketmine\level\format\anvil; use pocketmine\level\format\LevelProvider; -use pocketmine\nbt\NBT; -use pocketmine\nbt\tag\Byte; -use pocketmine\nbt\tag\ByteArray; -use pocketmine\nbt\tag\Compound; -use pocketmine\nbt\tag\Enum; -use pocketmine\nbt\tag\Int; -use pocketmine\nbt\tag\IntArray; -use pocketmine\nbt\tag\Long; -use pocketmine\utils\Binary; -use pocketmine\utils\MainLogger; + class RegionLoader extends \pocketmine\level\format\mcregion\RegionLoader{ diff --git a/src/pocketmine/level/format/generic/BaseFullChunk.php b/src/pocketmine/level/format/generic/BaseFullChunk.php index eca67d6f1..4a163eca0 100644 --- a/src/pocketmine/level/format/generic/BaseFullChunk.php +++ b/src/pocketmine/level/format/generic/BaseFullChunk.php @@ -26,10 +26,11 @@ use pocketmine\entity\Entity; use pocketmine\level\format\FullChunk; use pocketmine\level\format\LevelProvider; use pocketmine\level\generator\biome\Biome; +use pocketmine\level\Level; use pocketmine\nbt\tag\Compound; use pocketmine\Player; use pocketmine\tile\Tile; -use pocketmine\utils\Binary; + abstract class BaseFullChunk implements FullChunk{ @@ -59,6 +60,8 @@ abstract class BaseFullChunk implements FullChunk{ protected $NBTentities; + protected $extraData = []; + /** @var LevelProvider */ protected $provider; @@ -82,7 +85,7 @@ abstract class BaseFullChunk implements FullChunk{ * @param Compound[] $entities * @param Compound[] $tiles */ - protected function __construct($provider, $x, $z, $blocks, $data, $skyLight, $blockLight, array $biomeColors = [], array $heightMap = [], array $entities = [], array $tiles = []){ + protected function __construct($provider, $x, $z, $blocks, $data, $skyLight, $blockLight, array $biomeColors = [], array $heightMap = [], array $entities = [], array $tiles = [], array $extraData = []){ $this->provider = $provider; $this->x = (int) $x; $this->z = (int) $z; @@ -95,7 +98,7 @@ abstract class BaseFullChunk implements FullChunk{ if(count($biomeColors) === 256){ $this->biomeColors = $biomeColors; }else{ - $this->biomeColors = array_fill(0, 256, Binary::readInt("\xff\x00\x00\x00\x00")); + $this->biomeColors = array_fill(0, 256, 0); } if(count($heightMap) === 256){ @@ -104,6 +107,8 @@ abstract class BaseFullChunk implements FullChunk{ $this->heightMap = array_fill(0, 256, 127); } + $this->extraData = $extraData; + $this->NBTtiles = $tiles; $this->NBTentities = $entities; } @@ -254,6 +259,24 @@ abstract class BaseFullChunk implements FullChunk{ } } + public function getBlockExtraData($x, $y, $z){ + if(isset($this->extraData[$index = Level::chunkBlockHash($x, $y, $z)])){ + return $this->extraData[$index]; + } + + return 0; + } + + public function setBlockExtraData($x, $y, $z, $data){ + if($data === 0){ + unset($this->extraData[Level::chunkBlockHash($x, $y, $z)]); + }else{ + $this->extraData[Level::chunkBlockHash($x, $y, $z)] = $data; + } + + $this->setChanged(true); + } + public function populateSkyLight(){ for($z = 0; $z < 16; ++$z){ for($x = 0; $x < 16; ++$x){ @@ -336,6 +359,10 @@ abstract class BaseFullChunk implements FullChunk{ return $this->tiles; } + public function getBlockExtraDataArray(){ + return $this->extraData; + } + public function getTile($x, $y, $z){ $index = ($z << 12) | ($x << 8) | $y; return isset($this->tileList[$index]) ? $this->tileList[$index] : null; diff --git a/src/pocketmine/level/format/leveldb/Chunk.php b/src/pocketmine/level/format/leveldb/Chunk.php index ce9e31eba..d4f64d3fe 100644 --- a/src/pocketmine/level/format/leveldb/Chunk.php +++ b/src/pocketmine/level/format/leveldb/Chunk.php @@ -26,6 +26,7 @@ use pocketmine\level\format\LevelProvider; use pocketmine\nbt\NBT; use pocketmine\Player; use pocketmine\utils\Binary; +use pocketmine\utils\BinaryStream; class Chunk extends BaseFullChunk{ @@ -260,6 +261,7 @@ class Chunk extends BaseFullChunk{ $entities = null; $tiles = null; + $extraData = []; if($provider instanceof LevelDB){ $nbt = new NBT(NBT::LITTLE_ENDIAN); @@ -280,6 +282,16 @@ class Chunk extends BaseFullChunk{ $tiles = [$tiles]; } } + $tileData = $provider->getDatabase()->get(substr($data, 0, 8) . LevelDB::ENTRY_EXTRA_DATA); + if($tileData !== false and strlen($tileData) > 0){ + $stream = new BinaryStream($tileData); + $count = $stream->getInt(); + for($i = 0; $i < $count; ++$i){ + $key = $stream->getInt(); + $value = $stream->getShort(false); + $extraData[$key] = $value; + } + } } $chunk = new Chunk($provider instanceof LevelProvider ? $provider : LevelDB::class, $chunkX, $chunkZ, $chunkData, $entities, $tiles); @@ -341,6 +353,17 @@ class Chunk extends BaseFullChunk{ $provider->getDatabase()->delete($chunkIndex . LevelDB::ENTRY_TILES); } + if(count($this->getBlockExtraDataArray()) > 0){ + $extraData = new BinaryStream(); + $extraData->putInt(count($this->getBlockExtraDataArray())); + foreach($this->getBlockExtraDataArray() as $key => $value){ + $extraData->putInt($key); + $extraData->putShort($value); + } + $provider->getDatabase()->put($chunkIndex . LevelDB::ENTRY_EXTRA_DATA, $extraData->getBuffer()); + }else{ + $provider->getDatabase()->delete($chunkIndex . LevelDB::ENTRY_EXTRA_DATA); + } } diff --git a/src/pocketmine/level/format/leveldb/LevelDB.php b/src/pocketmine/level/format/leveldb/LevelDB.php index 3407606de..819f7fc7f 100644 --- a/src/pocketmine/level/format/leveldb/LevelDB.php +++ b/src/pocketmine/level/format/leveldb/LevelDB.php @@ -33,6 +33,7 @@ use pocketmine\nbt\tag\Long; use pocketmine\nbt\tag\String; use pocketmine\tile\Spawnable; use pocketmine\utils\Binary; +use pocketmine\utils\BinaryStream; use pocketmine\utils\ChunkException; use pocketmine\utils\LevelException; @@ -40,6 +41,7 @@ class LevelDB extends BaseLevelProvider{ const ENTRY_VERSION = "v"; const ENTRY_FLAGS = "f"; + const ENTRY_EXTRA_DATA = "4"; const ENTRY_TICKS = "3"; const ENTRY_ENTITIES = "2"; const ENTRY_TILES = "1"; @@ -156,12 +158,20 @@ class LevelDB extends BaseLevelProvider{ $heightmap = pack("C*", ...$chunk->getHeightMapArray()); $biomeColors = pack("N*", ...$chunk->getBiomeColorArray()); + $extraData = new BinaryStream(); + $extraData->putLInt(count($chunk->getBlockExtraDataArray())); + foreach($chunk->getBlockExtraDataArray() as $key => $value){ + $extraData->putLInt($key); + $extraData->putLShort($value); + } + $ordered = $chunk->getBlockIdArray() . $chunk->getBlockDataArray() . $chunk->getBlockSkyLightArray() . $chunk->getBlockLightArray() . $heightmap . $biomeColors . + $extraData->getBuffer() . $tiles; $this->getLevel()->chunkRequestCallback($x, $z, $ordered); diff --git a/src/pocketmine/level/format/mcregion/Chunk.php b/src/pocketmine/level/format/mcregion/Chunk.php index be2356af1..aee98c90d 100644 --- a/src/pocketmine/level/format/mcregion/Chunk.php +++ b/src/pocketmine/level/format/mcregion/Chunk.php @@ -33,6 +33,7 @@ use pocketmine\nbt\tag\IntArray; use pocketmine\nbt\tag\Long; use pocketmine\Player; use pocketmine\utils\Binary; +use pocketmine\utils\BinaryStream; class Chunk extends BaseFullChunk{ @@ -87,7 +88,20 @@ class Chunk extends BaseFullChunk{ $this->nbt->BlockLight = new ByteArray("BlockLight", $half); } - parent::__construct($level, $this->nbt["xPos"], $this->nbt["zPos"], $this->nbt->Blocks->getValue(), $this->nbt->Data->getValue(), $this->nbt->SkyLight->getValue(), $this->nbt->BlockLight->getValue(), $this->nbt->BiomeColors->getValue(), $this->nbt->HeightMap->getValue(), $this->nbt->Entities->getValue(), $this->nbt->TileEntities->getValue()); + $extraData = []; + + if(!isset($this->nbt->ExtraData) or !($this->nbt->ExtraData instanceof ByteArray)){ + $this->nbt->ExtraData = new ByteArray("ExtraData", Binary::writeInt(0)); + }else{ + $stream = new BinaryStream($this->nbt->ExtraData->getValue()); + $count = $stream->getInt(); + for($i = 0; $i < $count; ++$i){ + $key = $stream->getInt(); + $extraData[$key] = $stream->getShort(false); + } + } + + parent::__construct($level, $this->nbt["xPos"], $this->nbt["zPos"], $this->nbt->Blocks->getValue(), $this->nbt->Data->getValue(), $this->nbt->SkyLight->getValue(), $this->nbt->BlockLight->getValue(), $this->nbt->BiomeColors->getValue(), $this->nbt->HeightMap->getValue(), $this->nbt->Entities->getValue(), $this->nbt->TileEntities->getValue(), $extraData); if(isset($this->nbt->Biomes)){ $this->checkOldBiomes($this->nbt->Biomes->getValue()); @@ -395,6 +409,16 @@ class Chunk extends BaseFullChunk{ $nbt->TileEntities = new Enum("TileEntities", $tiles); $nbt->TileEntities->setTagType(NBT::TAG_Compound); + + $extraData = new BinaryStream(); + $extraData->putInt(count($this->getBlockExtraDataArray())); + foreach($this->getBlockExtraDataArray() as $key => $value){ + $extraData->putInt($key); + $extraData->putShort($value); + } + + $nbt->ExtraData = new ByteArray("ExtraData", $extraData->getBuffer()); + $writer = new NBT(NBT::BIG_ENDIAN); $nbt->setName("Level"); $writer->setData(new Compound("", ["Level" => $nbt])); diff --git a/src/pocketmine/level/format/mcregion/McRegion.php b/src/pocketmine/level/format/mcregion/McRegion.php index 525f70195..d30f2682b 100644 --- a/src/pocketmine/level/format/mcregion/McRegion.php +++ b/src/pocketmine/level/format/mcregion/McRegion.php @@ -31,9 +31,9 @@ use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\Int; use pocketmine\nbt\tag\Long; use pocketmine\nbt\tag\String; -use pocketmine\network\protocol\FullChunkDataPacket; use pocketmine\tile\Spawnable; -use pocketmine\utils\Binary; + +use pocketmine\utils\BinaryStream; use pocketmine\utils\ChunkException; class McRegion extends BaseLevelProvider{ @@ -133,17 +133,20 @@ class McRegion extends BaseLevelProvider{ $tiles = $nbt->write(); } - - - $heightmap = pack("C*", ...$chunk->getHeightMapArray()); - $biomeColors = pack("N*", ...$chunk->getBiomeColorArray()); + $extraData = new BinaryStream(); + $extraData->putLInt(count($chunk->getBlockExtraDataArray())); + foreach($chunk->getBlockExtraDataArray() as $key => $value){ + $extraData->putLInt($key); + $extraData->putLShort($value); + } $ordered = $chunk->getBlockIdArray() . $chunk->getBlockDataArray() . $chunk->getBlockSkyLightArray() . $chunk->getBlockLightArray() . - $heightmap . - $biomeColors . + pack("C*", ...$chunk->getHeightMapArray()) . + pack("N*", ...$chunk->getBiomeColorArray()) . + $extraData->getBuffer() . $tiles; $this->getLevel()->chunkRequestCallback($x, $z, $ordered); diff --git a/src/pocketmine/level/format/mcregion/RegionLoader.php b/src/pocketmine/level/format/mcregion/RegionLoader.php index 94099853e..c7a528874 100644 --- a/src/pocketmine/level/format/mcregion/RegionLoader.php +++ b/src/pocketmine/level/format/mcregion/RegionLoader.php @@ -23,14 +23,8 @@ namespace pocketmine\level\format\mcregion; use pocketmine\level\format\FullChunk; use pocketmine\level\format\LevelProvider; -use pocketmine\nbt\NBT; -use pocketmine\nbt\tag\Byte; -use pocketmine\nbt\tag\ByteArray; -use pocketmine\nbt\tag\Compound; -use pocketmine\nbt\tag\Enum; -use pocketmine\nbt\tag\Int; -use pocketmine\nbt\tag\IntArray; -use pocketmine\nbt\tag\Long; + + use pocketmine\utils\Binary; use pocketmine\utils\ChunkException; use pocketmine\utils\MainLogger; diff --git a/src/pocketmine/level/generator/GenerationTask.php b/src/pocketmine/level/generator/GenerationTask.php index fa042a13c..5aae87fce 100644 --- a/src/pocketmine/level/generator/GenerationTask.php +++ b/src/pocketmine/level/generator/GenerationTask.php @@ -21,14 +21,14 @@ namespace pocketmine\level\generator; -use pocketmine\block\Block; + use pocketmine\level\format\FullChunk; -use pocketmine\level\generator\biome\Biome; + use pocketmine\level\Level; use pocketmine\level\SimpleChunkManager; use pocketmine\scheduler\AsyncTask; use pocketmine\Server; -use pocketmine\utils\Random; + class GenerationTask extends AsyncTask{ diff --git a/src/pocketmine/level/generator/GeneratorRegisterTask.php b/src/pocketmine/level/generator/GeneratorRegisterTask.php index 27cf1acfe..38ba77404 100644 --- a/src/pocketmine/level/generator/GeneratorRegisterTask.php +++ b/src/pocketmine/level/generator/GeneratorRegisterTask.php @@ -22,12 +22,12 @@ namespace pocketmine\level\generator; use pocketmine\block\Block; -use pocketmine\level\format\FullChunk; + use pocketmine\level\generator\biome\Biome; use pocketmine\level\Level; use pocketmine\level\SimpleChunkManager; use pocketmine\scheduler\AsyncTask; -use pocketmine\Server; + use pocketmine\utils\Random; class GeneratorRegisterTask extends AsyncTask{ diff --git a/src/pocketmine/level/generator/GeneratorUnregisterTask.php b/src/pocketmine/level/generator/GeneratorUnregisterTask.php index dff150004..dbf060138 100644 --- a/src/pocketmine/level/generator/GeneratorUnregisterTask.php +++ b/src/pocketmine/level/generator/GeneratorUnregisterTask.php @@ -21,14 +21,11 @@ namespace pocketmine\level\generator; -use pocketmine\block\Block; -use pocketmine\level\format\FullChunk; -use pocketmine\level\generator\biome\Biome; + use pocketmine\level\Level; -use pocketmine\level\SimpleChunkManager; + use pocketmine\scheduler\AsyncTask; -use pocketmine\Server; -use pocketmine\utils\Random; + class GeneratorUnregisterTask extends AsyncTask{ diff --git a/src/pocketmine/level/generator/LightPopulationTask.php b/src/pocketmine/level/generator/LightPopulationTask.php index 7334e1f30..efe9d2644 100644 --- a/src/pocketmine/level/generator/LightPopulationTask.php +++ b/src/pocketmine/level/generator/LightPopulationTask.php @@ -21,14 +21,14 @@ namespace pocketmine\level\generator; -use pocketmine\block\Block; + use pocketmine\level\format\FullChunk; -use pocketmine\level\generator\biome\Biome; + use pocketmine\level\Level; -use pocketmine\level\SimpleChunkManager; + use pocketmine\scheduler\AsyncTask; use pocketmine\Server; -use pocketmine\utils\Random; + class LightPopulationTask extends AsyncTask{ diff --git a/src/pocketmine/level/generator/PopulationTask.php b/src/pocketmine/level/generator/PopulationTask.php index 6a9b6ed08..9e24e4e1b 100644 --- a/src/pocketmine/level/generator/PopulationTask.php +++ b/src/pocketmine/level/generator/PopulationTask.php @@ -21,14 +21,14 @@ namespace pocketmine\level\generator; -use pocketmine\block\Block; + use pocketmine\level\format\FullChunk; -use pocketmine\level\generator\biome\Biome; + use pocketmine\level\Level; use pocketmine\level\SimpleChunkManager; use pocketmine\scheduler\AsyncTask; use pocketmine\Server; -use pocketmine\utils\Random; + class PopulationTask extends AsyncTask{ diff --git a/src/pocketmine/level/generator/biome/Biome.php b/src/pocketmine/level/generator/biome/Biome.php index a4d5e1caf..2b38fd03b 100644 --- a/src/pocketmine/level/generator/biome/Biome.php +++ b/src/pocketmine/level/generator/biome/Biome.php @@ -47,6 +47,8 @@ abstract class Biome{ const SWAMP = 6; const RIVER = 7; + const HELL = 8; + const ICE_PLAINS = 12; @@ -88,12 +90,11 @@ abstract class Biome{ self::register(self::MOUNTAINS, new MountainsBiome()); self::register(self::FOREST, new ForestBiome()); self::register(self::TAIGA, new TaigaBiome()); - + self::register(self::SWAMP, new SwampBiome()); self::register(self::RIVER, new RiverBiome()); self::register(self::ICE_PLAINS, new IcePlainsBiome()); - self::register(self::SWAMP, new SwampBiome()); self::register(self::SMALL_MOUNTAINS, new SmallMountainsBiome()); diff --git a/src/pocketmine/level/generator/hell/HellBiome.php b/src/pocketmine/level/generator/hell/HellBiome.php new file mode 100644 index 000000000..5fb717e92 --- /dev/null +++ b/src/pocketmine/level/generator/hell/HellBiome.php @@ -0,0 +1,35 @@ +level = $level; + $this->random = $random; + $this->random->setSeed($this->level->getSeed()); + $this->noiseBase = new Simplex($this->random, 4, 1 / 4, 1 / 64); + $this->random->setSeed($this->level->getSeed()); + + /*$ores = new Ore(); + $ores->setOreTypes([ + new OreType(new CoalOre(), 20, 16, 0, 128), + new OreType(New IronOre(), 20, 8, 0, 64), + new OreType(new RedstoneOre(), 8, 7, 0, 16), + new OreType(new LapisOre(), 1, 6, 0, 32), + new OreType(new GoldOre(), 2, 8, 0, 32), + new OreType(new DiamondOre(), 1, 7, 0, 16), + new OreType(new Dirt(), 20, 32, 0, 128), + new OreType(new Gravel(), 10, 16, 0, 128) + ]); + $this->populators[] = $ores;*/ + } + + public function generateChunk($chunkX, $chunkZ){ + $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed()); + + $noise = Generator::getFastNoise3D($this->noiseBase, 16, 128, 16, 4, 8, 4, $chunkX * 16, 0, $chunkZ * 16); + + $chunk = $this->level->getChunk($chunkX, $chunkZ); + + for($x = 0; $x < 16; ++$x){ + for($z = 0; $z < 16; ++$z){ + + $biome = Biome::getBiome(Biome::HELL); + $chunk->setBiomeId($x, $z, $biome->getId()); + $color = [0, 0, 0]; + $bColor = $biome->getColor(); + $color[0] += (($bColor >> 16) ** 2); + $color[1] += ((($bColor >> 8) & 0xff) ** 2); + $color[2] += (($bColor & 0xff) ** 2); + + $chunk->setBiomeColor($x, $z, $color[0], $color[1], $color[2]); + + for($y = 0; $y < 128; ++$y){ + if($y === 0 or $y === 127){ + $chunk->setBlockId($x, $y, $z, Block::BEDROCK); + continue; + } + $noiseValue = (abs($this->emptyHeight - $y) / $this->emptyHeight) * $this->emptyAmplitude - $noise[$x][$z][$y]; + $noiseValue -= 1 - $this->density; + + if($noiseValue > 0){ + $chunk->setBlockId($x, $y, $z, Block::NETHERRACK); + }elseif($y <= $this->waterHeight){ + $chunk->setBlockId($x, $y, $z, Block::STILL_LAVA); + } + } + } + } + + foreach($this->generationPopulators as $populator){ + $populator->populate($this->level, $chunkX, $chunkZ, $this->random); + } + } + + public function populateChunk($chunkX, $chunkZ){ + $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed()); + foreach($this->populators as $populator){ + $populator->populate($this->level, $chunkX, $chunkZ, $this->random); + } + + $chunk = $this->level->getChunk($chunkX, $chunkZ); + $biome = Biome::getBiome($chunk->getBiomeId(7, 7)); + $biome->populateChunk($this->level, $chunkX, $chunkZ, $this->random); + } + + public function getSpawn(){ + return new Vector3(127.5, 128, 127.5); + } + +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/normal/Normal.php b/src/pocketmine/level/generator/normal/Normal.php index e7bbcb087..1cc445814 100644 --- a/src/pocketmine/level/generator/normal/Normal.php +++ b/src/pocketmine/level/generator/normal/Normal.php @@ -33,18 +33,16 @@ use pocketmine\block\RedstoneOre; use pocketmine\level\ChunkManager; use pocketmine\level\generator\biome\Biome; use pocketmine\level\generator\biome\BiomeSelector; -use pocketmine\level\generator\GenerationChunkManager; -use pocketmine\level\generator\GenerationManager; use pocketmine\level\generator\Generator; -use pocketmine\level\generator\noise\Perlin; + use pocketmine\level\generator\noise\Simplex; -use pocketmine\level\generator\normal\biome\NormalBiome; + use pocketmine\level\generator\object\OreType; use pocketmine\level\generator\populator\GroundCover; use pocketmine\level\generator\populator\Ore; use pocketmine\level\generator\populator\Populator; -use pocketmine\level\generator\populator\TallGrass; -use pocketmine\level\generator\populator\Tree; + + use pocketmine\level\Level; use pocketmine\math\Vector3 as Vector3; use pocketmine\utils\Random; diff --git a/src/pocketmine/level/generator/normal/biome/DesertBiome.php b/src/pocketmine/level/generator/normal/biome/DesertBiome.php index b0d8399ef..3ffd6d5dc 100644 --- a/src/pocketmine/level/generator/normal/biome/DesertBiome.php +++ b/src/pocketmine/level/generator/normal/biome/DesertBiome.php @@ -21,7 +21,7 @@ namespace pocketmine\level\generator\normal\biome; -use pocketmine\block\Block; + class DesertBiome extends SandyBiome{ diff --git a/src/pocketmine/level/generator/normal/biome/SmallMountainsBiome.php b/src/pocketmine/level/generator/normal/biome/SmallMountainsBiome.php index 9321d19a4..98a585ac5 100644 --- a/src/pocketmine/level/generator/normal/biome/SmallMountainsBiome.php +++ b/src/pocketmine/level/generator/normal/biome/SmallMountainsBiome.php @@ -21,8 +21,6 @@ namespace pocketmine\level\generator\normal\biome; -use pocketmine\level\generator\populator\TallGrass; -use pocketmine\level\generator\populator\Tree; class SmallMountainsBiome extends MountainsBiome{ diff --git a/src/pocketmine/level/particle/AngryVillagerParticle.php b/src/pocketmine/level/particle/AngryVillagerParticle.php new file mode 100644 index 000000000..e33a28c4d --- /dev/null +++ b/src/pocketmine/level/particle/AngryVillagerParticle.php @@ -0,0 +1,30 @@ +evid = 2001; + $pk->evid = LevelEventPacket::EVENT_PARTICLE_DESTROY; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; diff --git a/src/pocketmine/level/particle/DustParticle.php b/src/pocketmine/level/particle/DustParticle.php index e3de30907..0f0ad4e3b 100644 --- a/src/pocketmine/level/particle/DustParticle.php +++ b/src/pocketmine/level/particle/DustParticle.php @@ -25,6 +25,6 @@ use pocketmine\math\Vector3; class DustParticle extends GenericParticle{ public function __construct(Vector3 $pos, $r, $g, $b, $a = 255){ - parent::__construct($pos, 22, (($a & 0xff) << 24) | (($r & 0xff) << 16) | (($g & 0xff) << 8) | ($b & 0xff)); + parent::__construct($pos, Particle::TYPE_DUST, (($a & 0xff) << 24) | (($r & 0xff) << 16) | (($g & 0xff) << 8) | ($b & 0xff)); } } diff --git a/src/pocketmine/level/particle/EnchantParticle.php b/src/pocketmine/level/particle/EnchantParticle.php index a5c82daf2..643f071bc 100644 --- a/src/pocketmine/level/particle/EnchantParticle.php +++ b/src/pocketmine/level/particle/EnchantParticle.php @@ -25,6 +25,6 @@ use pocketmine\math\Vector3; class EnchantParticle extends GenericParticle{ public function __construct(Vector3 $pos){ - parent::__construct($pos, 23); + parent::__construct($pos, Particle::TYPE_MOB_SPELL); } } diff --git a/src/pocketmine/level/particle/EnchantmentTableParticle.php b/src/pocketmine/level/particle/EnchantmentTableParticle.php new file mode 100644 index 000000000..06910db87 --- /dev/null +++ b/src/pocketmine/level/particle/EnchantmentTableParticle.php @@ -0,0 +1,30 @@ +x, $pos->y, $pos->z); $this->text = $text; diff --git a/src/pocketmine/level/particle/GenericParticle.php b/src/pocketmine/level/particle/GenericParticle.php index f0daef681..c7c4e5035 100644 --- a/src/pocketmine/level/particle/GenericParticle.php +++ b/src/pocketmine/level/particle/GenericParticle.php @@ -37,7 +37,7 @@ class GenericParticle extends Particle{ public function encode(){ $pk = new LevelEventPacket; - $pk->evid = 0x4000 | $this->id; + $pk->evid = LevelEventPacket::EVENT_ADD_PARTICLE_MASK | $this->id; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; diff --git a/src/pocketmine/level/particle/HappyVillagerParticle.php b/src/pocketmine/level/particle/HappyVillagerParticle.php new file mode 100644 index 000000000..14ef5234e --- /dev/null +++ b/src/pocketmine/level/particle/HappyVillagerParticle.php @@ -0,0 +1,30 @@ +getId() << 16) | $item->getDamage()); + parent::__construct($pos, Particle::TYPE_ITEM_BREAK, ($item->getId() << 16) | $item->getDamage()); } } diff --git a/src/pocketmine/level/particle/LargeExplodeParticle.php b/src/pocketmine/level/particle/LargeExplodeParticle.php new file mode 100644 index 000000000..27bf263cd --- /dev/null +++ b/src/pocketmine/level/particle/LargeExplodeParticle.php @@ -0,0 +1,30 @@ +evid = 2004; + $pk->evid = LevelEventPacket::EVENT_PARTICLE_SPAWN; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; diff --git a/src/pocketmine/level/particle/Particle.php b/src/pocketmine/level/particle/Particle.php index 82323b08a..2adf99250 100644 --- a/src/pocketmine/level/particle/Particle.php +++ b/src/pocketmine/level/particle/Particle.php @@ -25,6 +25,39 @@ use pocketmine\math\Vector3; use pocketmine\network\protocol\DataPacket; abstract class Particle extends Vector3{ + + const TYPE_BUBBLE = 1; + const TYPE_CRITICAL = 2; + const TYPE_SMOKE = 3; + const TYPE_EXPLODE = 4; + const TYPE_WHITE_SMOKE = 5; + const TYPE_FLAME = 6; + const TYPE_LAVA = 7; + const TYPE_LARGE_SMOKE = 8; + const TYPE_REDSTONE = 9; + const TYPE_ITEM_BREAK = 10; + const TYPE_SNOWBALL_POOF = 11; + const TYPE_LARGE_EXPLODE = 12; + const TYPE_HUGE_EXPLODE = 13; + const TYPE_MOB_FLAME = 14; + const TYPE_HEART = 15; + const TYPE_TERRAIN = 16; + const TYPE_TOWN_AURA = 17; + const TYPE_PORTAL = 18; + const TYPE_WATER_SPLASH = 19; + const TYPE_WATER_WAKE = 20; + const TYPE_DRIP_WATER = 21; + const TYPE_DRIP_LAVA = 22; + const TYPE_DUST = 23; + const TYPE_MOB_SPELL = 24; + const TYPE_MOB_SPELL_AMBIENT = 25; + const TYPE_MOB_SPELL_INSTANTANEOUS = 26; + const TYPE_INK = 27; + const TYPE_SLIME = 28; + const TYPE_RAIN_SPLASH = 29; + const TYPE_VILLAGER_ANGRY = 30; + const TYPE_VILLAGER_HAPPY = 31; + const TYPE_ENCHANTMENT_TABLE = 32; /** * @return DataPacket|DataPacket[] diff --git a/src/pocketmine/level/particle/PortalParticle.php b/src/pocketmine/level/particle/PortalParticle.php index 95ce3b16d..216307ddf 100644 --- a/src/pocketmine/level/particle/PortalParticle.php +++ b/src/pocketmine/level/particle/PortalParticle.php @@ -25,6 +25,6 @@ use pocketmine\math\Vector3; class PortalParticle extends GenericParticle{ public function __construct(Vector3 $pos){ - parent::__construct($pos, 17); + parent::__construct($pos, Particle::TYPE_PORTAL); } } diff --git a/src/pocketmine/level/particle/RainSplashParticle.php b/src/pocketmine/level/particle/RainSplashParticle.php new file mode 100644 index 000000000..26a9fefcb --- /dev/null +++ b/src/pocketmine/level/particle/RainSplashParticle.php @@ -0,0 +1,30 @@ +getDamage() << 8) | $b->getId()); + parent::__construct($pos, Particle::TYPE_TERRAIN, ($b->getDamage() << 8) | $b->getId()); } } diff --git a/src/pocketmine/level/particle/WaterDripParticle.php b/src/pocketmine/level/particle/WaterDripParticle.php index 2ae566217..ad99c10d2 100644 --- a/src/pocketmine/level/particle/WaterDripParticle.php +++ b/src/pocketmine/level/particle/WaterDripParticle.php @@ -25,6 +25,6 @@ use pocketmine\math\Vector3; class WaterDripParticle extends GenericParticle{ public function __construct(Vector3 $pos){ - parent::__construct($pos, 20); + parent::__construct($pos, Particle::TYPE_DRIP_WATER); } } diff --git a/src/pocketmine/level/particle/WaterParticle.php b/src/pocketmine/level/particle/WaterParticle.php index 8a5682232..cfd1dee87 100644 --- a/src/pocketmine/level/particle/WaterParticle.php +++ b/src/pocketmine/level/particle/WaterParticle.php @@ -25,6 +25,6 @@ use pocketmine\math\Vector3; class WaterParticle extends GenericParticle{ public function __construct(Vector3 $pos){ - parent::__construct($pos, 19); + parent::__construct($pos, Particle::TYPE_WATER_WAKE); } } diff --git a/src/pocketmine/level/sound/AnvilBreakSound.php b/src/pocketmine/level/sound/AnvilBreakSound.php new file mode 100644 index 000000000..7418976cb --- /dev/null +++ b/src/pocketmine/level/sound/AnvilBreakSound.php @@ -0,0 +1,31 @@ + new Short("id", $item->getId()), + "Count" => new Byte("Count", $item->getCount()), + "Damage" => new Short("Damage", $item->getDamage()) + ]); + + if($slot !== null){ + $tag->Slot = new Byte("Slot", (int) $slot); + } + + if($item->hasCompoundTag()){ + $tag->tag = clone $item->getNamedTag(); + $tag->tag->setName("tag"); + } + + return $tag; + } + + /** + * @param Compound $tag + * @return Item + */ + public static function getItemHelper(Compound $tag){ + if(!isset($tag->id) or !isset($tag->Count)){ + return Item::get(0); + } + + $item = Item::get($tag->id->getValue(), !isset($tag->Damage) ? 0 : $tag->Damage->getValue(), $tag->Count->getValue()); + + if(isset($tag->tag) and $tag->tag instanceof Compound){ + $item->setNamedTag($tag->tag); + } + + return $item; + } + + public static function matchList(Enum $tag1, Enum $tag2){ + if($tag1->getName() !== $tag2->getName() or $tag1->getCount() !== $tag2->getCount()){ + return false; + } + + foreach($tag1 as $k => $v){ + if(!($v instanceof Tag)){ + continue; + } + + if(!isset($tag2->{$k}) or !($tag2->{$k} instanceof $v)){ + return false; + } + + if($v instanceof Compound){ + if(!self::matchTree($v, $tag2->{$k})){ + return false; + } + }elseif($v instanceof Enum){ + if(!self::matchList($v, $tag2->{$k})){ + return false; + } + }else{ + if($v->getValue() !== $tag2->{$k}->getValue()){ + return false; + } + } + } + + return true; + } + + public static function matchTree(Compound $tag1, Compound $tag2){ + if($tag1->getName() !== $tag2->getName() or $tag1->getCount() !== $tag2->getCount()){ + return false; + } + + foreach($tag1 as $k => $v){ + if(!($v instanceof Tag)){ + continue; + } + + if(!isset($tag2->{$k}) or !($tag2->{$k} instanceof $v)){ + return false; + } + + if($v instanceof Compound){ + if(!self::matchTree($v, $tag2->{$k})){ + return false; + } + }elseif($v instanceof Enum){ + if(!self::matchList($v, $tag2->{$k})){ + return false; + } + }else{ + if($v->getValue() !== $tag2->{$k}->getValue()){ + return false; + } + } + } + + return true; + } + + public static function parseJSON($data, &$offset = 0){ + $len = strlen($data); + for(; $offset < $len; ++$offset){ + $c = $data{$offset}; + if($c === "{"){ + ++$offset; + $data = self::parseCompound($data, $offset); + return new Compound("", $data); + }elseif($c !== " " and $c !== "\r" and $c !== "\n" and $c !== "\t"){ + throw new \Exception("Syntax error: unexpected '$c' at offset $offset"); + } + } + + return null; + } + + private static function parseList($str, &$offset = 0){ + $len = strlen($str); + + + $key = 0; + $value = null; + + $data = []; + + for(; $offset < $len; ++$offset){ + if($str{$offset - 1} === "]"){ + break; + }elseif($str{$offset} === "]"){ + ++$offset; + break; + } + + $value = self::readValue($str, $offset, $type); + + switch($type){ + case NBT::TAG_Byte: + $data[$key] = new Byte($key, $value); + break; + case NBT::TAG_Short: + $data[$key] = new Short($key, $value); + break; + case NBT::TAG_Int: + $data[$key] = new Int($key, $value); + break; + case NBT::TAG_Long: + $data[$key] = new Long($key, $value); + break; + case NBT::TAG_Float: + $data[$key] = new Float($key, $value); + break; + case NBT::TAG_Double: + $data[$key] = new Double($key, $value); + break; + case NBT::TAG_ByteArray: + $data[$key] = new ByteArray($key, $value); + break; + case NBT::TAG_String: + $data[$key] = new Byte($key, $value); + break; + case NBT::TAG_Enum: + $data[$key] = new Enum($key, $value); + break; + case NBT::TAG_Compound: + $data[$key] = new Compound($key, $value); + break; + case NBT::TAG_IntArray: + $data[$key] = new IntArray($key, $value); + break; + } + + $key++; + } + + return $data; + } + + private static function parseCompound($str, &$offset = 0){ + $len = strlen($str); + + $data = []; + + for(; $offset < $len; ++$offset){ + if($str{$offset - 1} === "}"){ + break; + }elseif($str{$offset} === "}"){ + ++$offset; + break; + } + + $key = self::readKey($str, $offset); + $value = self::readValue($str, $offset, $type); + + switch($type){ + case NBT::TAG_Byte: + $data[$key] = new Byte($key, $value); + break; + case NBT::TAG_Short: + $data[$key] = new Short($key, $value); + break; + case NBT::TAG_Int: + $data[$key] = new Int($key, $value); + break; + case NBT::TAG_Long: + $data[$key] = new Long($key, $value); + break; + case NBT::TAG_Float: + $data[$key] = new Float($key, $value); + break; + case NBT::TAG_Double: + $data[$key] = new Double($key, $value); + break; + case NBT::TAG_ByteArray: + $data[$key] = new ByteArray($key, $value); + break; + case NBT::TAG_String: + $data[$key] = new String($key, $value); + break; + case NBT::TAG_Enum: + $data[$key] = new Enum($key, $value); + break; + case NBT::TAG_Compound: + $data[$key] = new Compound($key, $value); + break; + case NBT::TAG_IntArray: + $data[$key] = new IntArray($key, $value); + break; + } + } + + return $data; + } + + private static function readValue($data, &$offset, &$type = null){ + $value = ""; + $type = null; + $inQuotes = false; + + $len = strlen($data); + for(; $offset < $len; ++$offset){ + $c = $data{$offset}; + + if(!$inQuotes and ($c === " " or $c === "\r" or $c === "\n" or $c === "\t" or $c === "," or $c === "}" or $c === "]")){ + if($c === "," or $c === "}" or $c === "]"){ + break; + } + }elseif($c === '"'){ + $inQuotes = !$inQuotes; + if($type === null){ + $type = self::TAG_String; + }elseif($inQuotes){ + throw new \Exception("Syntax error: invalid quote at offset $offset"); + } + }elseif($c === "\\"){ + $value .= isset($data{$offset + 1}) ? $data{$offset + 1} : ""; + ++$offset; + }elseif($c === "{" and !$inQuotes){ + if($value !== ""){ + throw new \Exception("Syntax error: invalid compound start at offset $offset"); + } + ++$offset; + $value = self::parseCompound($data, $offset); + $type = self::TAG_Compound; + break; + }elseif($c === "[" and !$inQuotes){ + if($value !== ""){ + throw new \Exception("Syntax error: invalid list start at offset $offset"); + } + ++$offset; + $value = self::parseList($data, $offset); + $type = self::TAG_Enum; + break; + }else{ + $value .= $c; + } + } + + if($value === ""){ + throw new \Exception("Syntax error: invalid empty value at offset $offset"); + } + + if($type === null and strlen($value) > 0){ + $value = trim($value); + $last = strtolower(substr($value, -1)); + $part = substr($value, 0, -1); + + if($last !== "b" and $last !== "s" and $last !== "l" and $last !== "f" and $last !== "d"){ + $part = $value; + $last = null; + } + + if($last !== "f" and $last !== "d" and ((string) ((int) $part)) === $part){ + if($last === "b"){ + $type = self::TAG_Byte; + }elseif($last === "s"){ + $type = self::TAG_Short; + }elseif($last === "l"){ + $type = self::TAG_Long; + }else{ + $type = self::TAG_Int; + } + $value = (int) $part; + }elseif(is_numeric($part)){ + if($last === "f" or $last === "d" or strpos($part, ".") !== false){ + if($last === "f"){ + $type = self::TAG_Float; + }elseif($last === "d"){ + $type = self::TAG_Double; + }else{ + $type = self::TAG_Float; + } + $value = (float) $part; + }else{ + if($last === "l"){ + $type = self::TAG_Long; + }else{ + $type = self::TAG_Int; + } + + $value = $part; + } + }else{ + $type = self::TAG_String; + } + } + + return $value; + } + + private static function readKey($data, &$offset){ + $key = ""; + + $len = strlen($data); + for(; $offset < $len; ++$offset){ + $c = $data{$offset}; + + if($c === ":"){ + ++$offset; + break; + }elseif($c !== " " and $c !== "\r" and $c !== "\n" and $c !== "\t"){ + $key .= $c; + } + } + + if($key === ""){ + throw new \Exception("Syntax error: invalid empty key at offset $offset"); + } + + return $key; + } + public function get($len){ if($len < 0){ $this->offset = strlen($this->buffer) - 1; @@ -119,6 +478,8 @@ class NBT{ */ public function write(){ $this->offset = 0; + $this->buffer = ""; + if($this->data instanceof Compound){ $this->writeTag($this->data); @@ -263,22 +624,36 @@ class NBT{ public function getArray(){ $data = []; - $this->toArray($data, $this->data); + self::toArray($data, $this->data); } - private function toArray(array &$data, Tag $tag){ + private static function toArray(array &$data, Tag $tag){ /** @var Compound[]|Enum[]|IntArray[] $tag */ foreach($tag as $key => $value){ if($value instanceof Compound or $value instanceof Enum or $value instanceof IntArray){ $data[$key] = []; - $this->toArray($data[$key], $value); + self::toArray($data[$key], $value); }else{ $data[$key] = $value->getValue(); } } } - private function fromArray(Tag $tag, array $data){ + public static function fromArrayGuesser($key, $value){ + if(is_int($value)){ + return new Int($key, $value); + }elseif(is_float($value)){ + return new Float($key, $value); + }elseif(is_string($value)){ + return new String($key, $value); + }elseif(is_bool($value)){ + return new Byte($key, $value ? 1 : 0); + } + + return null; + } + + private static function fromArray(Tag $tag, array $data, callable $guesser){ foreach($data as $key => $value){ if(is_array($value)){ $isNumeric = true; @@ -292,26 +667,19 @@ class NBT{ } } $tag{$key} = $isNumeric ? ($isIntArray ? new IntArray($key, []) : new Enum($key, [])) : new Compound($key, []); - $this->fromArray($tag->{$key}, $value); - }elseif(is_int($value)){ - $tag{$key} = new Int($key, $value); - }elseif(is_float($value)){ - $tag{$key} = new Float($key, $value); - }elseif(is_string($value)){ - if(Utils::printable($value) !== $value){ - $tag{$key} = new ByteArray($key, $value); - }else{ - $tag{$key} = new String($key, $value); + self::fromArray($tag->{$key}, $value, $guesser); + }else{ + $v = call_user_func($guesser, $key, $value); + if($v instanceof Tag){ + $tag{$key} = $v; } - }elseif(is_bool($value)){ - $tag{$key} = new Byte($key, $value ? 1 : 0); } } } - public function setArray(array $data){ + public function setArray(array $data, callable $guesser = null){ $this->data = new Compound("", []); - $this->fromArray($this->data, $data); + self::fromArray($this->data, $data, $guesser === null ? [self::class, "fromArrayGuesser"] : $guesser); } /** diff --git a/src/pocketmine/nbt/tag/Compound.php b/src/pocketmine/nbt/tag/Compound.php index 1bd680285..654f33aa1 100644 --- a/src/pocketmine/nbt/tag/Compound.php +++ b/src/pocketmine/nbt/tag/Compound.php @@ -32,14 +32,25 @@ class Compound extends NamedTag implements \ArrayAccess{ * @param NamedTag[] $value */ public function __construct($name = "", $value = []){ - $this->name = $name; + $this->__name = $name; foreach($value as $tag){ $this->{$tag->getName()} = $tag; } } + public function getCount(){ + $count = 0; + foreach($this as $tag){ + if($tag instanceof Tag){ + ++$count; + } + } + + return $count; + } + public function offsetExists($offset){ - return isset($this->{$offset}); + return isset($this->{$offset}) and $this->{$offset} instanceof Tag; } public function offsetGet($offset){ diff --git a/src/pocketmine/nbt/tag/Enum.php b/src/pocketmine/nbt/tag/Enum.php index 12e2a076b..30d4dd1be 100644 --- a/src/pocketmine/nbt/tag/Enum.php +++ b/src/pocketmine/nbt/tag/Enum.php @@ -31,7 +31,7 @@ class Enum extends NamedTag implements \ArrayAccess, \Countable{ private $tagType; public function __construct($name = "", $value = []){ - $this->name = $name; + $this->__name = $name; foreach($value as $k => $v){ $this->{$k} = $v; } @@ -48,6 +48,17 @@ class Enum extends NamedTag implements \ArrayAccess, \Countable{ return $value; } + public function getCount(){ + $count = 0; + foreach($this as $tag){ + if($tag instanceof Tag){ + ++$count; + } + } + + return $count; + } + public function offsetExists($offset){ return isset($this->{$offset}); } diff --git a/src/pocketmine/nbt/tag/NamedTag.php b/src/pocketmine/nbt/tag/NamedTag.php index 169b02c8d..723e174bc 100644 --- a/src/pocketmine/nbt/tag/NamedTag.php +++ b/src/pocketmine/nbt/tag/NamedTag.php @@ -24,24 +24,24 @@ namespace pocketmine\nbt\tag; abstract class NamedTag extends Tag{ - protected $name; + protected $__name; /** * @param string $name * @param bool|float|double|int|byte|short|array|Compound|Enum|string $value */ public function __construct($name = "", $value = null){ - $this->name = ($name === null or $name === false) ? "" : $name; + $this->__name = ($name === null or $name === false) ? "" : $name; if($value !== null){ $this->value = $value; } } public function getName(){ - return $this->name; + return $this->__name; } public function setName($name){ - $this->name = $name; + $this->__name = $name; } } \ No newline at end of file diff --git a/src/pocketmine/network/Network.php b/src/pocketmine/network/Network.php index b6264413d..49053ea95 100644 --- a/src/pocketmine/network/Network.php +++ b/src/pocketmine/network/Network.php @@ -36,12 +36,14 @@ use pocketmine\network\protocol\ContainerOpenPacket; use pocketmine\network\protocol\ContainerSetContentPacket; use pocketmine\network\protocol\ContainerSetDataPacket; use pocketmine\network\protocol\ContainerSetSlotPacket; +use pocketmine\network\protocol\CraftingDataPacket; +use pocketmine\network\protocol\CraftingEventPacket; use pocketmine\network\protocol\DataPacket; use pocketmine\network\protocol\DropItemPacket; use pocketmine\network\protocol\FullChunkDataPacket; use pocketmine\network\protocol\Info; use pocketmine\network\protocol\SetEntityLinkPacket; -use pocketmine\network\protocol\TileEntityDataPacket; +use pocketmine\network\protocol\BlockEntityDataPacket; use pocketmine\network\protocol\EntityEventPacket; use pocketmine\network\protocol\ExplodePacket; use pocketmine\network\protocol\HurtArmorPacket; @@ -55,8 +57,8 @@ use pocketmine\network\protocol\TextPacket; use pocketmine\network\protocol\MoveEntityPacket; use pocketmine\network\protocol\MovePlayerPacket; use pocketmine\network\protocol\PlayerActionPacket; -use pocketmine\network\protocol\PlayerArmorEquipmentPacket; -use pocketmine\network\protocol\PlayerEquipmentPacket; +use pocketmine\network\protocol\MobArmorEquipmentPacket; +use pocketmine\network\protocol\MobEquipmentPacket; use pocketmine\network\protocol\RemoveBlockPacket; use pocketmine\network\protocol\RemoveEntityPacket; use pocketmine\network\protocol\RemovePlayerPacket; @@ -69,25 +71,37 @@ use pocketmine\network\protocol\SetSpawnPositionPacket; use pocketmine\network\protocol\SetTimePacket; use pocketmine\network\protocol\StartGamePacket; use pocketmine\network\protocol\TakeItemEntityPacket; -use pocketmine\network\protocol\TileEventPacket; +use pocketmine\network\protocol\BlockEventPacket; use pocketmine\network\protocol\UpdateBlockPacket; use pocketmine\network\protocol\UseItemPacket; +use pocketmine\network\protocol\SetPlayerGameTypePacket; +use pocketmine\network\protocol\PlayerListPacket; use pocketmine\Player; use pocketmine\Server; +use pocketmine\utils\Binary; use pocketmine\utils\MainLogger; class Network{ public static $BATCH_THRESHOLD = 512; + /** @deprecated */ const CHANNEL_NONE = 0; + /** @deprecated */ const CHANNEL_PRIORITY = 1; //Priority channel, only to be used when it matters + /** @deprecated */ const CHANNEL_WORLD_CHUNKS = 2; //Chunk sending + /** @deprecated */ const CHANNEL_MOVEMENT = 3; //Movement sending + /** @deprecated */ const CHANNEL_BLOCKS = 4; //Block updates or explosions + /** @deprecated */ const CHANNEL_WORLD_EVENTS = 5; //Entity, level or tile entity events + /** @deprecated */ const CHANNEL_ENTITY_SPAWNING = 6; //Entity spawn/despawn channel + /** @deprecated */ const CHANNEL_TEXT = 7; //Chat and other text stuff + /** @deprecated */ const CHANNEL_END = 31; /** @var \SplFixedArray */ @@ -219,14 +233,22 @@ class Network{ $offset = 0; try{ while($offset < $len){ - if(($pk = $this->getPacket(ord($str{$offset++}))) !== null){ + $pkLen = Binary::readInt(substr($str, $offset, 4)); + $offset += 4; + + $buf = substr($str, $offset, $pkLen); + $offset += $pkLen; + + if(($pk = $this->getPacket(ord($buf{0}))) !== null){ if($pk::NETWORK_ID === Info::BATCH_PACKET){ throw new \InvalidStateException("Invalid BatchPacket inside BatchPacket"); } - $pk->setBuffer($str, $offset); + + $pk->setBuffer($buf, 1); + $pk->decode(); $p->handleDataPacket($pk); - $offset += $pk->getOffset(); + if($pk->getOffset() <= 0){ return; } @@ -287,6 +309,7 @@ class Network{ $this->registerPacket(ProtocolInfo::LOGIN_PACKET, LoginPacket::class); $this->registerPacket(ProtocolInfo::PLAY_STATUS_PACKET, PlayStatusPacket::class); $this->registerPacket(ProtocolInfo::DISCONNECT_PACKET, DisconnectPacket::class); + $this->registerPacket(ProtocolInfo::BATCH_PACKET, BatchPacket::class); $this->registerPacket(ProtocolInfo::TEXT_PACKET, TextPacket::class); $this->registerPacket(ProtocolInfo::SET_TIME_PACKET, SetTimePacket::class); $this->registerPacket(ProtocolInfo::START_GAME_PACKET, StartGamePacket::class); @@ -303,10 +326,10 @@ class Network{ $this->registerPacket(ProtocolInfo::ADD_PAINTING_PACKET, AddPaintingPacket::class); $this->registerPacket(ProtocolInfo::EXPLODE_PACKET, ExplodePacket::class); $this->registerPacket(ProtocolInfo::LEVEL_EVENT_PACKET, LevelEventPacket::class); - $this->registerPacket(ProtocolInfo::TILE_EVENT_PACKET, TileEventPacket::class); + $this->registerPacket(ProtocolInfo::BLOCK_EVENT_PACKET, BlockEventPacket::class); $this->registerPacket(ProtocolInfo::ENTITY_EVENT_PACKET, EntityEventPacket::class); - $this->registerPacket(ProtocolInfo::PLAYER_EQUIPMENT_PACKET, PlayerEquipmentPacket::class); - $this->registerPacket(ProtocolInfo::PLAYER_ARMOR_EQUIPMENT_PACKET, PlayerArmorEquipmentPacket::class); + $this->registerPacket(ProtocolInfo::MOB_EQUIPMENT_PACKET, MobEquipmentPacket::class); + $this->registerPacket(ProtocolInfo::MOB_ARMOR_EQUIPMENT_PACKET, MobArmorEquipmentPacket::class); $this->registerPacket(ProtocolInfo::INTERACT_PACKET, InteractPacket::class); $this->registerPacket(ProtocolInfo::USE_ITEM_PACKET, UseItemPacket::class); $this->registerPacket(ProtocolInfo::PLAYER_ACTION_PACKET, PlayerActionPacket::class); @@ -324,10 +347,13 @@ class Network{ $this->registerPacket(ProtocolInfo::CONTAINER_SET_SLOT_PACKET, ContainerSetSlotPacket::class); $this->registerPacket(ProtocolInfo::CONTAINER_SET_DATA_PACKET, ContainerSetDataPacket::class); $this->registerPacket(ProtocolInfo::CONTAINER_SET_CONTENT_PACKET, ContainerSetContentPacket::class); + $this->registerPacket(ProtocolInfo::CRAFTING_DATA_PACKET, CraftingDataPacket::class); + $this->registerPacket(ProtocolInfo::CRAFTING_EVENT_PACKET, CraftingEventPacket::class); $this->registerPacket(ProtocolInfo::ADVENTURE_SETTINGS_PACKET, AdventureSettingsPacket::class); - $this->registerPacket(ProtocolInfo::TILE_ENTITY_DATA_PACKET, TileEntityDataPacket::class); + $this->registerPacket(ProtocolInfo::BLOCK_ENTITY_DATA_PACKET, BlockEntityDataPacket::class); $this->registerPacket(ProtocolInfo::FULL_CHUNK_DATA_PACKET, FullChunkDataPacket::class); $this->registerPacket(ProtocolInfo::SET_DIFFICULTY_PACKET, SetDifficultyPacket::class); - $this->registerPacket(ProtocolInfo::BATCH_PACKET, BatchPacket::class); + $this->registerPacket(ProtocolInfo::SET_PLAYER_GAMETYPE_PACKET, SetPlayerGameTypePacket::class); + $this->registerPacket(ProtocolInfo::PLAYER_LIST_PACKET, PlayerListPacket::class); } } diff --git a/src/pocketmine/network/protocol/AddPlayerPacket.php b/src/pocketmine/network/protocol/AddPlayerPacket.php index 5e3154875..afa1eb833 100644 --- a/src/pocketmine/network/protocol/AddPlayerPacket.php +++ b/src/pocketmine/network/protocol/AddPlayerPacket.php @@ -31,7 +31,7 @@ use pocketmine\utils\Binary; class AddPlayerPacket extends DataPacket{ const NETWORK_ID = Info::ADD_PLAYER_PACKET; - public $clientID; + public $uuid; public $username; public $eid; public $x; @@ -43,19 +43,15 @@ class AddPlayerPacket extends DataPacket{ public $pitch; public $yaw; public $item; - public $meta; public $metadata; - public $slim = false; - public $skin = null; - public function decode(){ } public function encode(){ $this->reset(); - $this->putLong($this->clientID); + $this->putUUID($this->uuid); $this->putString($this->username); $this->putLong($this->eid); $this->putFloat($this->x); @@ -67,10 +63,8 @@ class AddPlayerPacket extends DataPacket{ $this->putFloat($this->yaw); $this->putFloat($this->yaw); //TODO headrot $this->putFloat($this->pitch); - $this->putShort($this->item); - $this->putShort($this->meta); - $this->putByte($this->slim ? 1 : 0); - $this->putString($this->skin); + $this->putSlot($this->item); + $meta = Binary::writeMetadata($this->metadata); $this->put($meta); } diff --git a/src/pocketmine/network/protocol/TileEntityDataPacket.php b/src/pocketmine/network/protocol/BlockEntityDataPacket.php similarity index 84% rename from src/pocketmine/network/protocol/TileEntityDataPacket.php rename to src/pocketmine/network/protocol/BlockEntityDataPacket.php index 853d50934..668da2eea 100644 --- a/src/pocketmine/network/protocol/TileEntityDataPacket.php +++ b/src/pocketmine/network/protocol/BlockEntityDataPacket.php @@ -2,11 +2,11 @@ /* * - * ____ _ _ __ __ _ __ __ ____ - * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \ + * ____ _ _ __ __ _ __ __ ____ + * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) | - * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/ - * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_| + * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/ + * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_| * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,7 +15,7 @@ * * @author PocketMine Team * @link http://www.pocketmine.net/ - * + * * */ @@ -24,8 +24,8 @@ namespace pocketmine\network\protocol; #include -class TileEntityDataPacket extends DataPacket{ - const NETWORK_ID = Info::TILE_ENTITY_DATA_PACKET; +class BlockEntityDataPacket extends DataPacket{ + const NETWORK_ID = Info::BLOCK_ENTITY_DATA_PACKET; public $x; public $y; @@ -34,7 +34,7 @@ class TileEntityDataPacket extends DataPacket{ public function decode(){ $this->x = $this->getInt(); - $this->y = $this->getByte(); + $this->y = $this->getInt(); $this->z = $this->getInt(); $this->namedtag = $this->get(true); } @@ -42,7 +42,7 @@ class TileEntityDataPacket extends DataPacket{ public function encode(){ $this->reset(); $this->putInt($this->x); - $this->putByte($this->y); + $this->putInt($this->y); $this->putInt($this->z); $this->put($this->namedtag); } diff --git a/src/pocketmine/network/protocol/TileEventPacket.php b/src/pocketmine/network/protocol/BlockEventPacket.php similarity index 89% rename from src/pocketmine/network/protocol/TileEventPacket.php rename to src/pocketmine/network/protocol/BlockEventPacket.php index b46d2b5ec..1399cfc4d 100644 --- a/src/pocketmine/network/protocol/TileEventPacket.php +++ b/src/pocketmine/network/protocol/BlockEventPacket.php @@ -2,11 +2,11 @@ /* * - * ____ _ _ __ __ _ __ __ ____ - * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \ + * ____ _ _ __ __ _ __ __ ____ + * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) | - * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/ - * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_| + * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/ + * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_| * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,7 +15,7 @@ * * @author PocketMine Team * @link http://www.pocketmine.net/ - * + * * */ @@ -24,8 +24,8 @@ namespace pocketmine\network\protocol; #include -class TileEventPacket extends DataPacket{ - const NETWORK_ID = Info::TILE_EVENT_PACKET; +class BlockEventPacket extends DataPacket{ + const NETWORK_ID = Info::BLOCK_EVENT_PACKET; public $x; public $y; @@ -46,4 +46,4 @@ class TileEventPacket extends DataPacket{ $this->putInt($this->case2); } -} \ No newline at end of file +} diff --git a/src/pocketmine/network/protocol/ContainerSetContentPacket.php b/src/pocketmine/network/protocol/ContainerSetContentPacket.php index 108008965..ebeabd20d 100644 --- a/src/pocketmine/network/protocol/ContainerSetContentPacket.php +++ b/src/pocketmine/network/protocol/ContainerSetContentPacket.php @@ -30,7 +30,6 @@ class ContainerSetContentPacket extends DataPacket{ const SPECIAL_INVENTORY = 0; const SPECIAL_ARMOR = 0x78; const SPECIAL_CREATIVE = 0x79; - const SPECIAL_CRAFTING = 0x7a; public $windowid; public $slots = []; diff --git a/src/pocketmine/network/protocol/ContainerSetSlotPacket.php b/src/pocketmine/network/protocol/ContainerSetSlotPacket.php index 244aba979..43c4d1c34 100644 --- a/src/pocketmine/network/protocol/ContainerSetSlotPacket.php +++ b/src/pocketmine/network/protocol/ContainerSetSlotPacket.php @@ -30,12 +30,14 @@ class ContainerSetSlotPacket extends DataPacket{ public $windowid; public $slot; + public $hotbarSlot; /** @var Item */ public $item; public function decode(){ $this->windowid = $this->getByte(); $this->slot = $this->getShort(); + $this->hotboarSlot = $this->getShort(); $this->item = $this->getSlot(); } @@ -43,6 +45,7 @@ class ContainerSetSlotPacket extends DataPacket{ $this->reset(); $this->putByte($this->windowid); $this->putShort($this->slot); + $this->putShort($this->hotbarSlot); $this->putSlot($this->item); } diff --git a/src/pocketmine/network/protocol/CraftingDataPacket.php b/src/pocketmine/network/protocol/CraftingDataPacket.php new file mode 100644 index 000000000..e43f766e9 --- /dev/null +++ b/src/pocketmine/network/protocol/CraftingDataPacket.php @@ -0,0 +1,171 @@ + + + +use pocketmine\inventory\FurnaceRecipe; +use pocketmine\inventory\ShapedRecipe; +use pocketmine\inventory\ShapelessRecipe; +use pocketmine\item\enchantment\Enchantment; +use pocketmine\item\enchantment\EnchantmentList; +use pocketmine\utils\BinaryStream; + +class CraftingDataPacket extends DataPacket{ + const NETWORK_ID = Info::CRAFTING_DATA_PACKET; + + const ENTRY_SHAPELESS = 0; + const ENTRY_SHAPED = 1; + const ENTRY_FURNACE = 2; + const ENTRY_FURNACE_DATA = 3; + const ENTRY_ENCHANT_LIST = 4; + + /** @var object[] */ + public $entries = []; + public $cleanRecipes = false; + + private static function writeEntry($entry, BinaryStream $stream){ + if($entry instanceof ShapelessRecipe){ + return self::writeShapelessRecipe($entry, $stream); + }elseif($entry instanceof ShapedRecipe){ + return self::writeShapedRecipe($entry, $stream); + }elseif($entry instanceof FurnaceRecipe){ + return self::writeFurnaceRecipe($entry, $stream); + }elseif($entry instanceof EnchantmentList){ + return self::writeEnchantList($entry, $stream); + } + + return -1; + } + + private static function writeShapelessRecipe(ShapelessRecipe $recipe, BinaryStream $stream){ + $stream->putInt($recipe->getIngredientCount()); + foreach($recipe->getIngredientList() as $item){ + $stream->putSlot($item); + } + + $stream->putInt(1); + $stream->putSlot($recipe->getResult()); + + $stream->putUUID($recipe->getId()); + + return CraftingDataPacket::ENTRY_SHAPELESS; + } + + private static function writeShapedRecipe(ShapedRecipe $recipe, BinaryStream $stream){ + $stream->putInt($recipe->getWidth()); + $stream->putInt($recipe->getHeight()); + + for($z = 0; $z < $recipe->getHeight(); ++$z){ + for($x = 0; $x < $recipe->getWidth(); ++$x){ + $stream->putSlot($recipe->getIngredient($x, $z)); + } + } + + $stream->putInt(1); + $stream->putSlot($recipe->getResult()); + + $stream->putUUID($recipe->getId()); + + return CraftingDataPacket::ENTRY_SHAPED; + } + + private static function writeFurnaceRecipe(FurnaceRecipe $recipe, BinaryStream $stream){ + if($recipe->getInput()->getDamage() !== 0){ //Data recipe + $stream->putInt(($recipe->getInput()->getId() << 16) | ($recipe->getInput()->getDamage())); + $stream->putSlot($recipe->getResult()); + + return CraftingDataPacket::ENTRY_FURNACE_DATA; + }else{ + $stream->putInt($recipe->getInput()->getId()); + $stream->putSlot($recipe->getResult()); + + return CraftingDataPacket::ENTRY_FURNACE; + } + } + + private static function writeEnchantList(EnchantmentList $list, BinaryStream $stream){ + + $stream->putByte($list->getSize()); + for($i = 0; $i < $list->getSize(); ++$i){ + $entry = $list->getSlot($i); + $stream->putInt($entry->getCost()); + $stream->putByte(count($entry->getEnchantments())); + foreach($entry->getEnchantments() as $enchantment){ + $stream->putInt($enchantment->getId()); + $stream->putInt($enchantment->getLevel()); + } + $stream->putString($entry->getRandomName()); + } + + return CraftingDataPacket::ENTRY_ENCHANT_LIST; + } + + public function addShapelessRecipe(ShapelessRecipe $recipe){ + $this->entries[] = $recipe; + } + + public function addShapedRecipe(ShapedRecipe $recipe){ + $this->entries[] = $recipe; + } + + public function addFurnaceRecipe(FurnaceRecipe $recipe){ + $this->entries[] = $recipe; + } + + public function addEnchantList(EnchantmentList $list){ + $this->entries[] = $list; + } + + public function clean(){ + $this->entries = []; + return parent::clean(); + } + + public function decode(){ + + } + + public function encode(){ + $this->reset(); + $this->putInt(count($this->entries)); + + $writer = new BinaryStream(); + foreach($this->entries as $d){ + $entryType = self::writeEntry($d, $writer); + if($entryType >= 0){ + $this->putInt($entryType); + $this->putInt(strlen($writer->getBuffer())); + $this->put($writer->getBuffer()); + }else{ + $this->putInt(-1); + $this->putInt(0); + } + + $writer->reset(); + } + + $this->putByte($this->cleanRecipes ? 1 : 0); + } + +} diff --git a/src/pocketmine/network/protocol/CraftingEventPacket.php b/src/pocketmine/network/protocol/CraftingEventPacket.php new file mode 100644 index 000000000..eb3abc612 --- /dev/null +++ b/src/pocketmine/network/protocol/CraftingEventPacket.php @@ -0,0 +1,62 @@ + + + +class CraftingEventPacket extends DataPacket{ + const NETWORK_ID = Info::CRAFTING_EVENT_PACKET; + + public $windowId; + public $type; + public $id; + public $input = []; + public $output = []; + + public function clean(){ + $this->input = []; + $this->output = []; + return parent::clean(); + } + + public function decode(){ + $this->windowId = $this->getByte(); + $this->type = $this->getInt(); + $this->id = $this->getUUID(); + + $size = $this->getInt(); + for($i = 0; $i < $size and $i < 128; ++$i){ + $this->input[] = $this->getSlot(); + } + + $size = $this->getInt(); + for($i = 0; $i < $size and $i < 128; ++$i){ + $this->output[] = $this->getSlot(); + } + } + + public function encode(){ + + } + +} diff --git a/src/pocketmine/network/protocol/DataPacket.php b/src/pocketmine/network/protocol/DataPacket.php index 5c67267ba..b71aa84a3 100644 --- a/src/pocketmine/network/protocol/DataPacket.php +++ b/src/pocketmine/network/protocol/DataPacket.php @@ -24,18 +24,18 @@ namespace pocketmine\network\protocol; #include #ifndef COMPILE -use pocketmine\utils\Binary; + #endif -use pocketmine\item\Item; + +use pocketmine\utils\BinaryStream; +use pocketmine\utils\Utils; -abstract class DataPacket extends \stdClass{ +abstract class DataPacket extends BinaryStream{ const NETWORK_ID = 0; - public $offset = 0; - public $buffer = ""; public $isEncoded = false; private $channel = 0; @@ -47,11 +47,14 @@ abstract class DataPacket extends \stdClass{ abstract public function decode(); - protected function reset(){ + public function reset(){ $this->buffer = chr($this::NETWORK_ID); $this->offset = 0; } + /** + * @deprecated This adds extra overhead on the network, so its usage is now discouraged. It was a test for the viability of this. + */ public function setChannel($channel){ $this->channel = (int) $channel; return $this; @@ -61,141 +64,25 @@ abstract class DataPacket extends \stdClass{ return $this->channel; } - public function setBuffer($buffer = null, $offset = 0){ - $this->buffer = $buffer; - $this->offset = (int) $offset; - } - - public function getOffset(){ - return $this->offset; - } - - public function getBuffer(){ - return $this->buffer; - } - - protected function get($len){ - if($len < 0){ - $this->offset = strlen($this->buffer) - 1; - return ""; - }elseif($len === true){ - return substr($this->buffer, $this->offset); - } - - return $len === 1 ? $this->buffer{$this->offset++} : substr($this->buffer, ($this->offset += $len) - $len, $len); - } - - protected function put($str){ - $this->buffer .= $str; - } - - protected function getLong(){ - return Binary::readLong($this->get(8)); - } - - protected function putLong($v){ - $this->buffer .= Binary::writeLong($v); - } - - protected function getInt(){ - return Binary::readInt($this->get(4)); - } - - protected function putInt($v){ - $this->buffer .= Binary::writeInt($v); - } - - protected function getShort($signed = true){ - return $signed ? Binary::readSignedShort($this->get(2)) : Binary::readShort($this->get(2)); - } - - protected function putShort($v){ - $this->buffer .= Binary::writeShort($v); - } - - protected function getFloat(){ - return Binary::readFloat($this->get(4)); - } - - protected function putFloat($v){ - $this->buffer .= Binary::writeFloat($v); - } - - protected function getTriad(){ - return Binary::readTriad($this->get(3)); - } - - protected function putTriad($v){ - $this->buffer .= Binary::writeTriad($v); - } - - - protected function getLTriad(){ - return Binary::readLTriad($this->get(3)); - } - - protected function putLTriad($v){ - $this->buffer .= Binary::writeLTriad($v); - } - - protected function getByte(){ - return ord($this->buffer{$this->offset++}); - } - - protected function putByte($v){ - $this->buffer .= chr($v); - } - - protected function getDataArray($len = 10){ - $data = []; - for($i = 1; $i <= $len and !$this->feof(); ++$i){ - $data[] = $this->get($this->getTriad()); - } - - return $data; - } - - protected function putDataArray(array $data = []){ - foreach($data as $v){ - $this->putTriad(strlen($v)); - $this->put($v); - } - } - - protected function getSlot(){ - $id = $this->getShort(); - $cnt = $this->getByte(); - - return Item::get( - $id, - $this->getShort(), - $cnt - ); - } - - protected function putSlot(Item $item){ - $this->putShort($item->getId()); - $this->putByte($item->getCount()); - $this->putShort($item->getDamage()); - } - - protected function getString(){ - return $this->get($this->getShort()); - } - - protected function putString($v){ - $this->putShort(strlen($v)); - $this->put($v); - } - - protected function feof(){ - return !isset($this->buffer{$this->offset}); - } - public function clean(){ $this->buffer = null; $this->isEncoded = false; $this->offset = 0; return $this; } -} \ No newline at end of file + + public function __debugInfo(){ + $data = []; + foreach($this as $k => $v){ + if($k === "buffer"){ + $data[$k] = bin2hex($v); + }elseif(is_string($v) or (is_object($v) and method_exists($v, "__toString"))){ + $data[$k] = Utils::printable((string) $v); + }else{ + $data[$k] = $v; + } + } + + return $data; + } +} diff --git a/src/pocketmine/network/protocol/DropItemPacket.php b/src/pocketmine/network/protocol/DropItemPacket.php index afdd0310f..3b1b709c1 100644 --- a/src/pocketmine/network/protocol/DropItemPacket.php +++ b/src/pocketmine/network/protocol/DropItemPacket.php @@ -27,13 +27,11 @@ namespace pocketmine\network\protocol; class DropItemPacket extends DataPacket{ const NETWORK_ID = Info::DROP_ITEM_PACKET; - public $eid; - public $unknown; + public $type; public $item; public function decode(){ - $this->eid = $this->getLong(); - $this->unknown = $this->getByte(); + $this->type = $this->getByte(); $this->item = $this->getSlot(); } diff --git a/src/pocketmine/network/protocol/EntityEventPacket.php b/src/pocketmine/network/protocol/EntityEventPacket.php index f41bc6589..53aba2365 100644 --- a/src/pocketmine/network/protocol/EntityEventPacket.php +++ b/src/pocketmine/network/protocol/EntityEventPacket.php @@ -43,6 +43,8 @@ class EntityEventPacket extends DataPacket{ const AMBIENT_SOUND = 16; const RESPAWN = 17; + //TODO add new events + public $eid; public $event; diff --git a/src/pocketmine/network/protocol/FullChunkDataPacket.php b/src/pocketmine/network/protocol/FullChunkDataPacket.php index 619485212..994c386da 100644 --- a/src/pocketmine/network/protocol/FullChunkDataPacket.php +++ b/src/pocketmine/network/protocol/FullChunkDataPacket.php @@ -26,9 +26,13 @@ namespace pocketmine\network\protocol; class FullChunkDataPacket extends DataPacket{ const NETWORK_ID = Info::FULL_CHUNK_DATA_PACKET; + + const ORDER_COLUMNS = 0; + const ORDER_LAYERED = 1; public $chunkX; public $chunkZ; + public $order = self::ORDER_COLUMNS; public $data; public function decode(){ @@ -39,8 +43,9 @@ class FullChunkDataPacket extends DataPacket{ $this->reset(); $this->putInt($this->chunkX); $this->putInt($this->chunkZ); + $this->putByte($this->order); $this->putInt(strlen($this->data)); $this->put($this->data); } -} \ No newline at end of file +} diff --git a/src/pocketmine/network/protocol/Info.php b/src/pocketmine/network/protocol/Info.php index c7fa20853..3f0bdd7d6 100644 --- a/src/pocketmine/network/protocol/Info.php +++ b/src/pocketmine/network/protocol/Info.php @@ -30,66 +30,72 @@ interface Info{ /** * Actual Minecraft: PE protocol version */ - const CURRENT_PROTOCOL = 27; - - const LOGIN_PACKET = 0x82; - const PLAY_STATUS_PACKET = 0x83; - - const DISCONNECT_PACKET = 0x84; - - const TEXT_PACKET = 0x85; - const SET_TIME_PACKET = 0x86; - - const START_GAME_PACKET = 0x87; - - const ADD_PLAYER_PACKET = 0x88; - const REMOVE_PLAYER_PACKET = 0x89; - - const ADD_ENTITY_PACKET = 0x8a; - const REMOVE_ENTITY_PACKET = 0x8b; - const ADD_ITEM_ENTITY_PACKET = 0x8c; - const TAKE_ITEM_ENTITY_PACKET = 0x8d; - - const MOVE_ENTITY_PACKET = 0x8e; - const MOVE_PLAYER_PACKET = 0x8f; - - const REMOVE_BLOCK_PACKET = 0x90; - const UPDATE_BLOCK_PACKET = 0x91; - - const ADD_PAINTING_PACKET = 0x92; - - const EXPLODE_PACKET = 0x93; - - const LEVEL_EVENT_PACKET = 0x94; - const TILE_EVENT_PACKET = 0x95; - const ENTITY_EVENT_PACKET = 0x96; - const MOB_EFFECT_PACKET = 0x97; - - const PLAYER_EQUIPMENT_PACKET = 0x98; - const PLAYER_ARMOR_EQUIPMENT_PACKET = 0x99; - const INTERACT_PACKET = 0x9a; - const USE_ITEM_PACKET = 0x9b; - const PLAYER_ACTION_PACKET = 0x9c; - const HURT_ARMOR_PACKET = 0x9d; - const SET_ENTITY_DATA_PACKET = 0x9e; - const SET_ENTITY_MOTION_PACKET = 0x9f; - const SET_ENTITY_LINK_PACKET = 0xa0; - const SET_HEALTH_PACKET = 0xa1; - const SET_SPAWN_POSITION_PACKET = 0xa2; - const ANIMATE_PACKET = 0xa3; - const RESPAWN_PACKET = 0xa4; - const DROP_ITEM_PACKET = 0xa5; - const CONTAINER_OPEN_PACKET = 0xa6; - const CONTAINER_CLOSE_PACKET = 0xa7; - const CONTAINER_SET_SLOT_PACKET = 0xa8; - const CONTAINER_SET_DATA_PACKET = 0xa9; - const CONTAINER_SET_CONTENT_PACKET = 0xaa; - //const CONTAINER_ACK_PACKET = 0xab; - const ADVENTURE_SETTINGS_PACKET = 0xac; - const TILE_ENTITY_DATA_PACKET = 0xad; - //const PLAYER_INPUT_PACKET = 0xae; - const FULL_CHUNK_DATA_PACKET = 0xaf; - const SET_DIFFICULTY_PACKET = 0xb0; - const BATCH_PACKET = 0xb1; + const CURRENT_PROTOCOL = 38; + const LOGIN_PACKET = 0x8f; + const PLAY_STATUS_PACKET = 0x90; + const DISCONNECT_PACKET = 0x91; + const BATCH_PACKET = 0x92; + const TEXT_PACKET = 0x93; + const SET_TIME_PACKET = 0x94; + const START_GAME_PACKET = 0x95; + const ADD_PLAYER_PACKET = 0x96; + const REMOVE_PLAYER_PACKET = 0x97; + const ADD_ENTITY_PACKET = 0x98; + const REMOVE_ENTITY_PACKET = 0x99; + const ADD_ITEM_ENTITY_PACKET = 0x9a; + const TAKE_ITEM_ENTITY_PACKET = 0x9b; + const MOVE_ENTITY_PACKET = 0x9c; + const MOVE_PLAYER_PACKET = 0x9d; + const REMOVE_BLOCK_PACKET = 0x9e; + const UPDATE_BLOCK_PACKET = 0x9f; + const ADD_PAINTING_PACKET = 0xa0; + const EXPLODE_PACKET = 0xa1; + const LEVEL_EVENT_PACKET = 0xa2; + const BLOCK_EVENT_PACKET = 0xa3; + const ENTITY_EVENT_PACKET = 0xa4; + const MOB_EFFECT_PACKET = 0xa5; + const UPDATE_ATTRIBUTES_PACKET = 0xa6; + const MOB_EQUIPMENT_PACKET = 0xa7; + const MOB_ARMOR_EQUIPMENT_PACKET = 0xa8; + const INTERACT_PACKET = 0xa9; + const USE_ITEM_PACKET = 0xaa; + const PLAYER_ACTION_PACKET = 0xab; + const HURT_ARMOR_PACKET = 0xac; + const SET_ENTITY_DATA_PACKET = 0xad; + const SET_ENTITY_MOTION_PACKET = 0xae; + const SET_ENTITY_LINK_PACKET = 0xaf; + const SET_HEALTH_PACKET = 0xb0; + const SET_SPAWN_POSITION_PACKET = 0xb1; + const ANIMATE_PACKET = 0xb2; + const RESPAWN_PACKET = 0xb3; + const DROP_ITEM_PACKET = 0xb4; + const CONTAINER_OPEN_PACKET = 0xb5; + const CONTAINER_CLOSE_PACKET = 0xb6; + const CONTAINER_SET_SLOT_PACKET = 0xb7; + const CONTAINER_SET_DATA_PACKET = 0xb8; + const CONTAINER_SET_CONTENT_PACKET = 0xb9; + const CRAFTING_DATA_PACKET = 0xba; + const CRAFTING_EVENT_PACKET = 0xbb; + const ADVENTURE_SETTINGS_PACKET = 0xbc; + const BLOCK_ENTITY_DATA_PACKET = 0xbd; + //const PLAYER_INPUT_PACKET = 0xbe; + const FULL_CHUNK_DATA_PACKET = 0xbf; + const SET_DIFFICULTY_PACKET = 0xc0; + //const CHANGE_DIMENSION_PACKET = 0xc1; + const SET_PLAYER_GAMETYPE_PACKET = 0xc2; + const PLAYER_LIST_PACKET = 0xc3; + //const TELEMETRY_EVENT_PACKET = 0xc4; + //const SPAWN_EXPERIENCE_ORB_PACKET = 0xc5 } + + + + + + + + + + + diff --git a/src/pocketmine/network/protocol/LevelEventPacket.php b/src/pocketmine/network/protocol/LevelEventPacket.php index d34bc3225..b8b59f150 100644 --- a/src/pocketmine/network/protocol/LevelEventPacket.php +++ b/src/pocketmine/network/protocol/LevelEventPacket.php @@ -27,6 +27,45 @@ namespace pocketmine\network\protocol; class LevelEventPacket extends DataPacket{ const NETWORK_ID = Info::LEVEL_EVENT_PACKET; + const EVENT_SOUND_CLICK = 1000; + const EVENT_SOUND_CLICK_FAIL = 1001; + const EVENT_SOUND_SHOOT = 1002; + const EVENT_SOUND_DOOR = 1003; + const EVENT_SOUND_FIZZ = 1004; + + const EVENT_SOUND_GHAST = 1007; + const EVENT_SOUND_GHAST_SHOOT = 1008; + const EVENT_SOUND_BLAZE_SHOOT = 1009; + + const EVENT_SOUND_DOOR_BUMP = 1010; + const EVENT_SOUND_DOOR_CRASH = 1012; + + const EVENT_SOUND_BAT_FLY = 1015; + const EVENT_SOUND_ZOMBIE_INFECT = 1016; + const EVENT_SOUND_ZOMBIE_HEAL = 1017; + const EVENT_SOUND_ENDERMAN_TELEPORT = 1018; + + const EVENT_SOUND_ANVIL_BREAK = 1020; + const EVENT_SOUND_ANVIL_USE = 1021; + const EVENT_SOUND_ANVIL_FALL = 1022; + + const EVENT_PARTICLE_SHOOT = 2000; + const EVENT_PARTICLE_DESTROY = 2001; + const EVENT_PARTICLE_SPLASH = 2002; + const EVENT_PARTICLE_EYE_DESPAWN = 2003; + const EVENT_PARTICLE_SPAWN = 2004; + + const EVENT_START_RAIN = 3001; + const EVENT_START_THUNDER = 3002; + const EVENT_STOP_RAIN = 3003; + const EVENT_STOP_THUNDER = 3004; + + const EVENT_SET_DATA = 4000; + + const EVENT_PLAYERS_SLEEPING = 9800; + + const EVENT_ADD_PARTICLE_MASK = 0x4000; + public $evid; public $x; public $y; diff --git a/src/pocketmine/network/protocol/LoginPacket.php b/src/pocketmine/network/protocol/LoginPacket.php index 2d5151282..6b8079f19 100644 --- a/src/pocketmine/network/protocol/LoginPacket.php +++ b/src/pocketmine/network/protocol/LoginPacket.php @@ -32,19 +32,27 @@ class LoginPacket extends DataPacket{ public $protocol2; public $clientId; - public $slim = false; + public $clientUUID; + public $serverAddress; + public $clientSecret; + + public $skinName; public $skin = null; public function decode(){ $this->username = $this->getString(); $this->protocol1 = $this->getInt(); $this->protocol2 = $this->getInt(); - $this->clientId = $this->getInt(); - if($this->protocol1 < 21){ //New fields! + if($this->protocol1 < Info::CURRENT_PROTOCOL){ //New fields! $this->setBuffer(null, 0); //Skip batch packet handling return; } - $this->slim = $this->getByte() > 0; + $this->clientId = $this->getLong(); + $this->clientUUID = $this->getUUID(); + $this->serverAddress = $this->getString(); + $this->clientSecret = $this->getString(); + + $this->skinName = $this->getString(); $this->skin = $this->getString(); } diff --git a/src/pocketmine/network/protocol/PlayerArmorEquipmentPacket.php b/src/pocketmine/network/protocol/MobArmorEquipmentPacket.php similarity index 70% rename from src/pocketmine/network/protocol/PlayerArmorEquipmentPacket.php rename to src/pocketmine/network/protocol/MobArmorEquipmentPacket.php index 7df9f98b2..0fb13e577 100644 --- a/src/pocketmine/network/protocol/PlayerArmorEquipmentPacket.php +++ b/src/pocketmine/network/protocol/MobArmorEquipmentPacket.php @@ -24,27 +24,27 @@ namespace pocketmine\network\protocol; #include -class PlayerArmorEquipmentPacket extends DataPacket{ - const NETWORK_ID = Info::PLAYER_ARMOR_EQUIPMENT_PACKET; +class MobArmorEquipmentPacket extends DataPacket{ + const NETWORK_ID = Info::MOB_ARMOR_EQUIPMENT_PACKET; public $eid; public $slots = []; public function decode(){ $this->eid = $this->getLong(); - $this->slots[0] = $this->getByte(); - $this->slots[1] = $this->getByte(); - $this->slots[2] = $this->getByte(); - $this->slots[3] = $this->getByte(); + $this->slots[0] = $this->getSlot(); + $this->slots[1] = $this->getSlot(); + $this->slots[2] = $this->getSlot(); + $this->slots[3] = $this->getSlot(); } public function encode(){ $this->reset(); $this->putLong($this->eid); - $this->putByte($this->slots[0]); - $this->putByte($this->slots[1]); - $this->putByte($this->slots[2]); - $this->putByte($this->slots[3]); + $this->putSlot($this->slots[0]); + $this->putSlot($this->slots[1]); + $this->putSlot($this->slots[2]); + $this->putSlot($this->slots[3]); } } diff --git a/src/pocketmine/network/protocol/PlayerEquipmentPacket.php b/src/pocketmine/network/protocol/MobEquipmentPacket.php similarity index 82% rename from src/pocketmine/network/protocol/PlayerEquipmentPacket.php rename to src/pocketmine/network/protocol/MobEquipmentPacket.php index 32ab69160..fa897b06b 100644 --- a/src/pocketmine/network/protocol/PlayerEquipmentPacket.php +++ b/src/pocketmine/network/protocol/MobEquipmentPacket.php @@ -24,19 +24,17 @@ namespace pocketmine\network\protocol; #include -class PlayerEquipmentPacket extends DataPacket{ - const NETWORK_ID = Info::PLAYER_EQUIPMENT_PACKET; +class MobEquipmentPacket extends DataPacket{ + const NETWORK_ID = Info::MOB_EQUIPMENT_PACKET; public $eid; public $item; - public $meta; public $slot; public $selectedSlot; public function decode(){ $this->eid = $this->getLong(); - $this->item = $this->getShort(); - $this->meta = $this->getShort(); + $this->item = $this->getSlot(); $this->slot = $this->getByte(); $this->selectedSlot = $this->getByte(); } @@ -44,8 +42,7 @@ class PlayerEquipmentPacket extends DataPacket{ public function encode(){ $this->reset(); $this->putLong($this->eid); - $this->putShort($this->item); - $this->putShort($this->meta); + $this->putSlot($this->item); $this->putByte($this->slot); $this->putByte($this->selectedSlot); } diff --git a/src/pocketmine/network/protocol/PlayerActionPacket.php b/src/pocketmine/network/protocol/PlayerActionPacket.php index b44061a16..c4186dcb4 100644 --- a/src/pocketmine/network/protocol/PlayerActionPacket.php +++ b/src/pocketmine/network/protocol/PlayerActionPacket.php @@ -27,6 +27,21 @@ namespace pocketmine\network\protocol; class PlayerActionPacket extends DataPacket{ const NETWORK_ID = Info::PLAYER_ACTION_PACKET; + const ACTION_START_BREAK = 0; + const ACTION_ABORT_BREAK = 1; + const ACTION_STOP_BREAK = 2; + + + const ACTION_RELEASE_ITEM = 5; + const ACTION_STOP_SLEEPING = 6; + const ACTION_RESPAWN = 7; + const ACTION_JUMP = 8; + const ACTION_START_SPRINT = 9; + const ACTION_STOP_SPRINT = 10; + const ACTION_START_SNEAK = 11; + const ACTION_STOP_SNEAK = 12; + const ACTION_DIMENSION_CHANGE = 13; + public $eid; public $action; public $x; diff --git a/src/pocketmine/network/protocol/PlayerListPacket.php b/src/pocketmine/network/protocol/PlayerListPacket.php new file mode 100644 index 000000000..c9f65a664 --- /dev/null +++ b/src/pocketmine/network/protocol/PlayerListPacket.php @@ -0,0 +1,64 @@ + + + +class PlayerListPacket extends DataPacket{ + const NETWORK_ID = Info::PLAYER_LIST_PACKET; + + const TYPE_ADD = 0; + const TYPE_REMOVE = 1; + + //REMOVE: UUID, ADD: UUID, entity id, name, isSlim, skin + /** @var array[] */ + public $entries = []; + public $type; + + public function clean(){ + $this->entries = []; + return parent::clean(); + } + + public function decode(){ + + } + + public function encode(){ + $this->reset(); + $this->putByte($this->type); + $this->putInt(count($this->entries)); + foreach($this->entries as $d){ + if($this->type === self::TYPE_ADD){ + $this->putUUID($d[0]); + $this->putLong($d[1]); + $this->putString($d[2]); + $this->putString($d[3]); + $this->putString($d[4]); + }else{ + $this->putUUID($d[0]); + } + } + } + +} diff --git a/src/pocketmine/network/protocol/RemovePlayerPacket.php b/src/pocketmine/network/protocol/RemovePlayerPacket.php index ef903f604..0e8bfa1cd 100644 --- a/src/pocketmine/network/protocol/RemovePlayerPacket.php +++ b/src/pocketmine/network/protocol/RemovePlayerPacket.php @@ -28,7 +28,7 @@ class RemovePlayerPacket extends DataPacket{ const NETWORK_ID = Info::REMOVE_PLAYER_PACKET; public $eid; - public $clientID; + public $clientId; public function decode(){ @@ -37,7 +37,7 @@ class RemovePlayerPacket extends DataPacket{ public function encode(){ $this->reset(); $this->putLong($this->eid); - $this->putLong($this->clientID); + $this->putUUID($this->clientId); } } diff --git a/src/pocketmine/network/protocol/SetPlayerGameTypePacket.php b/src/pocketmine/network/protocol/SetPlayerGameTypePacket.php new file mode 100644 index 000000000..46e0ad72e --- /dev/null +++ b/src/pocketmine/network/protocol/SetPlayerGameTypePacket.php @@ -0,0 +1,41 @@ + + + +class SetPlayerGameTypePacket extends DataPacket{ + const NETWORK_ID = Info::SET_PLAYER_GAMETYPE_PACKET; + + public $gamemode; + + public function decode(){ + + } + + public function encode(){ + $this->reset(); + $this->putInt($this->gamemode); + } + +} diff --git a/src/pocketmine/network/protocol/SetSpawnPositionPacket.php b/src/pocketmine/network/protocol/SetSpawnPositionPacket.php index 14090b671..cf9571bb7 100644 --- a/src/pocketmine/network/protocol/SetSpawnPositionPacket.php +++ b/src/pocketmine/network/protocol/SetSpawnPositionPacket.php @@ -28,8 +28,8 @@ class SetSpawnPositionPacket extends DataPacket{ const NETWORK_ID = Info::SET_SPAWN_POSITION_PACKET; public $x; - public $z; public $y; + public $z; public function decode(){ @@ -38,8 +38,8 @@ class SetSpawnPositionPacket extends DataPacket{ public function encode(){ $this->reset(); $this->putInt($this->x); + $this->putInt($this->y); $this->putInt($this->z); - $this->putByte($this->y); } -} \ No newline at end of file +} diff --git a/src/pocketmine/network/protocol/SetTimePacket.php b/src/pocketmine/network/protocol/SetTimePacket.php index 841f55b8e..09b070e87 100644 --- a/src/pocketmine/network/protocol/SetTimePacket.php +++ b/src/pocketmine/network/protocol/SetTimePacket.php @@ -39,7 +39,7 @@ class SetTimePacket extends DataPacket{ public function encode(){ $this->reset(); $this->putInt((int) (($this->time / Level::TIME_FULL) * 19200)); - $this->putByte($this->started == true ? 0x80 : 0x00); + $this->putByte($this->started ? 1 : 0); } -} \ No newline at end of file +} diff --git a/src/pocketmine/network/protocol/StartGamePacket.php b/src/pocketmine/network/protocol/StartGamePacket.php index fe1b92a70..28df2ec1c 100644 --- a/src/pocketmine/network/protocol/StartGamePacket.php +++ b/src/pocketmine/network/protocol/StartGamePacket.php @@ -28,6 +28,7 @@ class StartGamePacket extends DataPacket{ const NETWORK_ID = Info::START_GAME_PACKET; public $seed; + public $dimension; public $generator; public $gamemode; public $eid; @@ -45,6 +46,7 @@ class StartGamePacket extends DataPacket{ public function encode(){ $this->reset(); $this->putInt($this->seed); + $this->putByte($this->dimension); $this->putInt($this->generator); $this->putInt($this->gamemode); $this->putLong($this->eid); @@ -54,6 +56,7 @@ class StartGamePacket extends DataPacket{ $this->putFloat($this->x); $this->putFloat($this->y); $this->putFloat($this->z); + $this->putByte(0); } } diff --git a/src/pocketmine/network/protocol/TextPacket.php b/src/pocketmine/network/protocol/TextPacket.php index fcd994717..a0ac6c3cb 100644 --- a/src/pocketmine/network/protocol/TextPacket.php +++ b/src/pocketmine/network/protocol/TextPacket.php @@ -32,6 +32,7 @@ class TextPacket extends DataPacket{ const TYPE_TRANSLATION = 2; const TYPE_POPUP = 3; const TYPE_TIP = 4; + const TYPE_SYSTEM = 5; public $type; public $source; @@ -41,11 +42,12 @@ class TextPacket extends DataPacket{ public function decode(){ $this->type = $this->getByte(); switch($this->type){ + case self::TYPE_POPUP: case self::TYPE_CHAT: $this->source = $this->getString(); case self::TYPE_RAW: - case self::TYPE_POPUP: case self::TYPE_TIP: + case self::TYPE_SYSTEM: $this->message = $this->getString(); break; @@ -62,11 +64,12 @@ class TextPacket extends DataPacket{ $this->reset(); $this->putByte($this->type); switch($this->type){ + case self::TYPE_POPUP: case self::TYPE_CHAT: $this->putString($this->source); case self::TYPE_RAW: - case self::TYPE_POPUP: case self::TYPE_TIP: + case self::TYPE_SYSTEM: $this->putString($this->message); break; diff --git a/src/pocketmine/network/protocol/UpdateAttributesPacket.php b/src/pocketmine/network/protocol/UpdateAttributesPacket.php new file mode 100644 index 000000000..46bc3cf6e --- /dev/null +++ b/src/pocketmine/network/protocol/UpdateAttributesPacket.php @@ -0,0 +1,56 @@ + + + +use pocketmine\entity\Attribute; + +class UpdateAttributesPacket extends DataPacket{ + const NETWORK_ID = Info::UPDATE_ATTRIBUTES_PACKET; + + + public $entityId; + /** @var Attribute[] */ + public $entries = []; + + public function decode(){ + + } + + public function encode(){ + $this->reset(); + + $this->putLong($this->entityId); + + $this->putShort(count($this->entries)); + + foreach($this->entries as $entry){ + $this->putFloat($entry->getMinValue()); + $this->putFloat($entry->getMaxValue()); + $this->putFloat($entry->getValue()); + $this->putString($entry->getName()); + } + } + +} diff --git a/src/pocketmine/network/protocol/UseItemPacket.php b/src/pocketmine/network/protocol/UseItemPacket.php index 316d879e9..325158c83 100644 --- a/src/pocketmine/network/protocol/UseItemPacket.php +++ b/src/pocketmine/network/protocol/UseItemPacket.php @@ -32,8 +32,6 @@ class UseItemPacket extends DataPacket{ public $z; public $face; public $item; - public $meta; - public $eid; public $fx; public $fy; public $fz; @@ -46,15 +44,14 @@ class UseItemPacket extends DataPacket{ $this->y = $this->getInt(); $this->z = $this->getInt(); $this->face = $this->getByte(); - $this->item = $this->getShort(); - $this->meta = $this->getShort(); - $this->eid = $this->getLong(); $this->fx = $this->getFloat(); $this->fy = $this->getFloat(); $this->fz = $this->getFloat(); $this->posX = $this->getFloat(); $this->posY = $this->getFloat(); $this->posZ = $this->getFloat(); + + $this->item = $this->getSlot(); } public function encode(){ diff --git a/src/pocketmine/permission/DefaultPermissions.php b/src/pocketmine/permission/DefaultPermissions.php index 5fa6b5740..b4bcebe7f 100644 --- a/src/pocketmine/permission/DefaultPermissions.php +++ b/src/pocketmine/permission/DefaultPermissions.php @@ -103,6 +103,7 @@ abstract class DefaultPermissions{ self::registerPermission(new Permission(self::ROOT . ".command.say", "Allows the user to talk as the console", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.give", "Allows the user to give items to players", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.effect", "Allows the user to give/take potion effects", Permission::DEFAULT_OP), $commands); + self::registerPermission(new Permission(self::ROOT . ".command.enchant", "Allows the user to enchant items", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.particle", "Allows the user to create particle effects", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.teleport", "Allows the user to teleport players", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.kick", "Allows the user to kick players", Permission::DEFAULT_OP), $commands); diff --git a/src/pocketmine/scheduler/SendUsageTask.php b/src/pocketmine/scheduler/SendUsageTask.php index d303872aa..fbd285afa 100644 --- a/src/pocketmine/scheduler/SendUsageTask.php +++ b/src/pocketmine/scheduler/SendUsageTask.php @@ -25,6 +25,7 @@ use pocketmine\network\protocol\Info; use pocketmine\Server; use pocketmine\utils\Utils; use pocketmine\utils\VersionString; +use pocketmine\utils\UUID; class SendUsageTask extends AsyncTask{ @@ -39,9 +40,9 @@ class SendUsageTask extends AsyncTask{ $endpoint = "http://" . $server->getProperty("anonymous-statistics.host", "stats.pocketmine.net") . "/"; $data = []; - $data["uniqueServerId"] = $server->getServerUniqueId(); - $data["uniqueMachineId"] = Utils::getMachineUniqueId(); - $data["uniqueRequestId"] = Utils::dataToUUID($server->getServerUniqueId(), microtime(true)); + $data["uniqueServerId"] = $server->getServerUniqueId()->toString(); + $data["uniqueMachineId"] = Utils::getMachineUniqueId()->toString(); + $data["uniqueRequestId"] = UUID::fromData($server->getServerUniqueId(), microtime(true))->toString(); switch($type){ case self::TYPE_OPEN: @@ -101,13 +102,13 @@ class SendUsageTask extends AsyncTask{ //This anonymizes the user ids so they cannot be reversed to the original foreach($playerList as $k => $v){ - $playerList[$k] = Utils::dataToUUID($v); + $playerList[$k] = md5($v); } $players = []; foreach($server->getOnlinePlayers() as $p){ if($p->isOnline()){ - $players[] = Utils::dataToUUID($p->getUniqueId()); + $players[] = md5($p->getUniqueId()->toBinary()); } } diff --git a/src/pocketmine/tile/Chest.php b/src/pocketmine/tile/Chest.php index 6a26b46e3..69f4892aa 100644 --- a/src/pocketmine/tile/Chest.php +++ b/src/pocketmine/tile/Chest.php @@ -28,14 +28,14 @@ use pocketmine\item\Item; use pocketmine\level\format\FullChunk; use pocketmine\math\Vector3; use pocketmine\nbt\NBT; -use pocketmine\nbt\tag\Byte; + use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\Enum; use pocketmine\nbt\tag\Int; -use pocketmine\nbt\tag\Short; + use pocketmine\nbt\tag\String; -class Chest extends Spawnable implements InventoryHolder, Container{ +class Chest extends Spawnable implements InventoryHolder, Container, Nameable{ /** @var ChestInventory */ protected $inventory; @@ -47,7 +47,7 @@ class Chest extends Spawnable implements InventoryHolder, Container{ $this->inventory = new ChestInventory($this); if(!isset($this->namedtag->Items) or !($this->namedtag->Items instanceof Enum)){ - $this->namedtag->Items = new Enum("Inventory", []); + $this->namedtag->Items = new Enum("Items", []); $this->namedtag->Items->setTagType(NBT::TAG_Compound); } @@ -91,8 +91,8 @@ class Chest extends Spawnable implements InventoryHolder, Container{ */ protected function getSlotIndex($index){ foreach($this->namedtag->Items as $i => $slot){ - if($slot["Slot"] === $index){ - return $i; + if((int) $slot["Slot"] === (int) $index){ + return (int) $i; } } @@ -111,7 +111,7 @@ class Chest extends Spawnable implements InventoryHolder, Container{ if($i < 0){ return Item::get(Item::AIR, 0, 0); }else{ - return Item::get($this->namedtag->Items[$i]["id"], $this->namedtag->Items[$i]["Damage"], $this->namedtag->Items[$i]["Count"]); + return NBT::getItemHelper($this->namedtag->Items[$i]); } } @@ -126,12 +126,7 @@ class Chest extends Spawnable implements InventoryHolder, Container{ public function setItem($index, Item $item){ $i = $this->getSlotIndex($index); - $d = new Compound("", [ - new Byte("Count", $item->getCount()), - new Byte("Slot", $index), - new Short("id", $item->getId()), - new Short("Damage", $item->getDamage()), - ]); + $d = NBT::putItemHelper($item, $index); if($item->getId() === Item::AIR or $item->getCount() <= 0){ if($i >= 0){ @@ -187,6 +182,23 @@ class Chest extends Spawnable implements InventoryHolder, Container{ } } + public function getName(){ + return isset($this->namedtag->CustomName) ? $this->namedtag->CustomName->getValue() : "Chest"; + } + + public function hasName(){ + return isset($this->namedtag->CustomName); + } + + public function setName($str){ + if($str === ""){ + unset($this->namedtag->CustomName); + return; + } + + $this->namedtag->CustomName = new String("CustomName", $str); + } + public function isPaired(){ if(!isset($this->namedtag->pairx) or !isset($this->namedtag->pairz)){ return false; @@ -253,7 +265,7 @@ class Chest extends Spawnable implements InventoryHolder, Container{ public function getSpawnCompound(){ if($this->isPaired()){ - return new Compound("", [ + $c = new Compound("", [ new String("id", Tile::CHEST), new Int("x", (int) $this->x), new Int("y", (int) $this->y), @@ -262,12 +274,18 @@ class Chest extends Spawnable implements InventoryHolder, Container{ new Int("pairz", (int) $this->namedtag["pairz"]) ]); }else{ - return new Compound("", [ + $c = new Compound("", [ new String("id", Tile::CHEST), new Int("x", (int) $this->x), new Int("y", (int) $this->y), new Int("z", (int) $this->z) ]); } + + if($this->hasName()){ + $c->CustomName = $this->namedtag->CustomName; + } + + return $c; } -} \ No newline at end of file +} diff --git a/src/pocketmine/tile/EnchantTable.php b/src/pocketmine/tile/EnchantTable.php new file mode 100644 index 000000000..a94ae40bc --- /dev/null +++ b/src/pocketmine/tile/EnchantTable.php @@ -0,0 +1,62 @@ +namedtag->CustomName) ? $this->namedtag->CustomName->getValue() : "Enchanting Table"; + } + + public function hasName(){ + return isset($this->namedtag->CustomName); + } + + public function setName($str){ + if($str === ""){ + unset($this->namedtag->CustomName); + return; + } + + $this->namedtag->CustomName = new String("CustomName", $str); + } + + public function getSpawnCompound(){ + $c = new Compound("", [ + new String("id", Tile::ENCHANT_TABLE), + new Int("x", (int) $this->x), + new Int("y", (int) $this->y), + new Int("z", (int) $this->z) + ]); + + if($this->hasName()){ + $c->CustomName = $this->namedtag->CustomName; + } + + return $c; + } +} diff --git a/src/pocketmine/tile/Furnace.php b/src/pocketmine/tile/Furnace.php index 8eaf3d8f3..6da32496c 100644 --- a/src/pocketmine/tile/Furnace.php +++ b/src/pocketmine/tile/Furnace.php @@ -30,14 +30,15 @@ use pocketmine\inventory\InventoryHolder; use pocketmine\item\Item; use pocketmine\level\format\FullChunk; use pocketmine\nbt\NBT; -use pocketmine\nbt\tag\Byte; + use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\Enum; use pocketmine\nbt\tag\Short; +use pocketmine\nbt\tag\String; use pocketmine\network\Network; use pocketmine\network\protocol\ContainerSetDataPacket; -class Furnace extends Tile implements InventoryHolder, Container{ +class Furnace extends Tile implements InventoryHolder, Container, Nameable{ /** @var FurnaceInventory */ protected $inventory; @@ -69,6 +70,23 @@ class Furnace extends Tile implements InventoryHolder, Container{ } } + public function getName(){ + return isset($this->namedtag->CustomName) ? $this->namedtag->CustomName->getValue() : "Furnace"; + } + + public function hasName(){ + return isset($this->namedtag->CustomName); + } + + public function setName($str){ + if($str === ""){ + unset($this->namedtag->CustomName); + return; + } + + $this->namedtag->CustomName = new String("CustomName", $str); + } + public function close(){ if($this->closed === false){ foreach($this->getInventory()->getViewers() as $player){ @@ -120,7 +138,7 @@ class Furnace extends Tile implements InventoryHolder, Container{ if($i < 0){ return Item::get(Item::AIR, 0, 0); }else{ - return Item::get($this->namedtag->Items[$i]["id"], $this->namedtag->Items[$i]["Damage"], $this->namedtag->Items[$i]["Count"]); + return NBT::getItemHelper($this->namedtag->Items[$i]); } } @@ -135,12 +153,7 @@ class Furnace extends Tile implements InventoryHolder, Container{ public function setItem($index, Item $item){ $i = $this->getSlotIndex($index); - $d = new Compound("", [ - new Byte("Count", $item->getCount()), - new Byte("Slot", $index), - new Short("id", $item->getId()), - new Short("Damage", $item->getDamage()), - ]); + $d = NBT::putItemHelper($item, $index); if($item->getId() === Item::AIR or $item->getCount() <= 0){ if($i >= 0){ @@ -203,7 +216,7 @@ class Furnace extends Tile implements InventoryHolder, Container{ $raw = $this->inventory->getSmelting(); $product = $this->inventory->getResult(); $smelt = $this->server->getCraftingManager()->matchFurnaceRecipe($raw); - $canSmelt = ($smelt instanceof FurnaceRecipe and $raw->getCount() > 0 and (($smelt->getResult()->equals($product, true) and $product->getCount() < $product->getMaxStackSize()) or $product->getId() === Item::AIR)); + $canSmelt = ($smelt instanceof FurnaceRecipe and $raw->getCount() > 0 and (($smelt->getResult()->equals($product) and $product->getCount() < $product->getMaxStackSize()) or $product->getId() === Item::AIR)); if($this->namedtag["BurnTime"] <= 0 and $canSmelt and $fuel->getFuelTime() !== null and $fuel->getCount() > 0){ $this->checkFuel($fuel); @@ -256,13 +269,13 @@ class Furnace extends Tile implements InventoryHolder, Container{ $pk->windowid = $windowId; $pk->property = 0; //Smelting $pk->value = floor($this->namedtag["CookTime"]); - $player->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $player->dataPacket($pk); $pk = new ContainerSetDataPacket(); $pk->windowid = $windowId; $pk->property = 1; //Fire icon $pk->value = $this->namedtag["BurnTicks"]; - $player->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $player->dataPacket($pk); } } diff --git a/src/pocketmine/tile/Nameable.php b/src/pocketmine/tile/Nameable.php new file mode 100644 index 000000000..77fc29e54 --- /dev/null +++ b/src/pocketmine/tile/Nameable.php @@ -0,0 +1,42 @@ +setData($this->getSpawnCompound()); - $pk = new TileEntityDataPacket(); + $pk = new BlockEntityDataPacket(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->namedtag = $nbt->write(); - $player->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); + $player->dataPacket($pk); return true; } diff --git a/src/pocketmine/tile/Tile.php b/src/pocketmine/tile/Tile.php index 32252094e..689236f66 100644 --- a/src/pocketmine/tile/Tile.php +++ b/src/pocketmine/tile/Tile.php @@ -21,7 +21,6 @@ /** * All the Tile classes and related classes - * TODO: Add Nether Reactor tile */ namespace pocketmine\tile; @@ -39,6 +38,11 @@ abstract class Tile extends Position{ const SIGN = "Sign"; const CHEST = "Chest"; const FURNACE = "Furnace"; + const FLOWER_POT = "FlowerPot"; + const MOB_SPAWNER = "MobSpawner"; + const SKULL = "Skull"; + const BREWING_STAND = "Cauldron"; + const ENCHANT_TABLE = "EnchantTable"; public static $tileCount = 1; diff --git a/src/pocketmine/utils/BinaryStream.php b/src/pocketmine/utils/BinaryStream.php new file mode 100644 index 000000000..d4ec2a96e --- /dev/null +++ b/src/pocketmine/utils/BinaryStream.php @@ -0,0 +1,252 @@ + + +#ifndef COMPILE + +#endif + +use pocketmine\item\Item; + + +class BinaryStream extends \stdClass{ + + public $offset; + public $buffer; + + public function __construct($buffer = "", $offset = 0){ + $this->buffer = $buffer; + $this->offset = $offset; + } + + public function reset(){ + $this->buffer = ""; + $this->offset = 0; + } + + public function setBuffer($buffer = null, $offset = 0){ + $this->buffer = $buffer; + $this->offset = (int) $offset; + } + + public function getOffset(){ + return $this->offset; + } + + public function getBuffer(){ + return $this->buffer; + } + + public function get($len){ + if($len < 0){ + $this->offset = strlen($this->buffer) - 1; + return ""; + }elseif($len === true){ + return substr($this->buffer, $this->offset); + } + + return $len === 1 ? $this->buffer{$this->offset++} : substr($this->buffer, ($this->offset += $len) - $len, $len); + } + + public function put($str){ + $this->buffer .= $str; + } + + public function getLong(){ + return Binary::readLong($this->get(8)); + } + + public function putLong($v){ + $this->buffer .= Binary::writeLong($v); + } + + public function getInt(){ + return Binary::readInt($this->get(4)); + } + + public function putInt($v){ + $this->buffer .= Binary::writeInt($v); + } + + public function getLLong(){ + return Binary::readLLong($this->get(8)); + } + + public function putLLong($v){ + $this->buffer .= Binary::writeLLong($v); + } + + public function getLInt(){ + return Binary::readLInt($this->get(4)); + } + + public function putLInt($v){ + $this->buffer .= Binary::writeLInt($v); + } + + public function getSignedShort(){ + return Binary::readSignedShort($this->get(2)); + } + + public function putShort($v){ + $this->buffer .= Binary::writeShort($v); + } + + public function getShort(){ + return Binary::readShort($this->get(2)); + } + + public function putSignedShort($v){ + $this->buffer .= Binary::writeShort($v); + } + + public function getFloat(){ + return Binary::readFloat($this->get(4)); + } + + public function putFloat($v){ + $this->buffer .= Binary::writeFloat($v); + } + + public function getLShort($signed = true){ + return $signed ? Binary::readSignedLShort($this->get(2)) : Binary::readLShort($this->get(2)); + } + + public function putLShort($v){ + $this->buffer .= Binary::writeLShort($v); + } + + public function getLFloat(){ + return Binary::readLFloat($this->get(4)); + } + + public function putLFloat($v){ + $this->buffer .= Binary::writeLFloat($v); + } + + + public function getTriad(){ + return Binary::readTriad($this->get(3)); + } + + public function putTriad($v){ + $this->buffer .= Binary::writeTriad($v); + } + + + public function getLTriad(){ + return Binary::readLTriad($this->get(3)); + } + + public function putLTriad($v){ + $this->buffer .= Binary::writeLTriad($v); + } + + public function getByte(){ + return ord($this->buffer{$this->offset++}); + } + + public function putByte($v){ + $this->buffer .= chr($v); + } + + public function getDataArray($len = 10){ + $data = []; + for($i = 1; $i <= $len and !$this->feof(); ++$i){ + $data[] = $this->get($this->getTriad()); + } + + return $data; + } + + public function putDataArray(array $data = []){ + foreach($data as $v){ + $this->putTriad(strlen($v)); + $this->put($v); + } + } + + public function getUUID(){ + return UUID::fromBinary($this->get(16)); + } + + public function putUUID(UUID $uuid){ + $this->put($uuid->toBinary()); + } + + public function getSlot(){ + $id = $this->getSignedShort(); + + if($id <= 0){ + return Item::get(0, 0, 0); + } + + $cnt = $this->getByte(); + + $data = $this->getShort(); + + $nbtLen = $this->getShort(); + + $nbt = ""; + + if($nbtLen > 0){ + $nbt = $this->get($nbtLen); + } + + return Item::get( + $id, + $data, + $cnt, + $nbt + ); + } + + public function putSlot(Item $item){ + if($item->getId() === 0){ + $this->putShort(0); + return; + } + + $this->putShort($item->getId()); + $this->putByte($item->getCount()); + $this->putShort($item->getDamage() === null ? -1 : $item->getDamage()); + $nbt = $item->getCompoundTag(); + $this->putShort(strlen($nbt)); + $this->put($nbt); + + } + + public function getString(){ + return $this->get($this->getShort()); + } + + public function putString($v){ + $this->putShort(strlen($v)); + $this->put($v); + } + + public function feof(){ + return !isset($this->buffer{$this->offset}); + } +} diff --git a/src/pocketmine/utils/Terminal.php b/src/pocketmine/utils/Terminal.php index b34351bb0..8669d8cd4 100644 --- a/src/pocketmine/utils/Terminal.php +++ b/src/pocketmine/utils/Terminal.php @@ -55,7 +55,7 @@ abstract class Terminal{ if(isset($opts["disable-ansi"])){ self::$formattingCodes = false; }else{ - self::$formattingCodes = ((Utils::getOS() !== "win" and getenv("TERM") != "" and (!function_exists("posix_ttyname") or posix_ttyname(STDOUT) !== false)) or isset($opts["enable-ansi"])); + self::$formattingCodes = ((Utils::getOS() !== "win" and getenv("TERM") != "" and (!function_exists("posix_ttyname") or !defined("STDOUT") or posix_ttyname(STDOUT) !== false)) or isset($opts["enable-ansi"])); } } diff --git a/src/pocketmine/utils/UUID.php b/src/pocketmine/utils/UUID.php new file mode 100644 index 000000000..717aca96d --- /dev/null +++ b/src/pocketmine/utils/UUID.php @@ -0,0 +1,105 @@ +parts[0] = (int) $part1; + $this->parts[1] = (int) $part2; + $this->parts[2] = (int) $part3; + $this->parts[3] = (int) $part4; + + $this->version = $version === null ? ($this->parts[1] & 0xf000) >> 12 : (int) $version; + } + + public function getVersion(){ + return $this->version; + } + + public function equals(UUID $uuid){ + 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]; + } + + /** + * Creates an UUID from an hexadecimal representation + * + * @param string $uuid + * @param int $version + * @return UUID + */ + public static function fromString($uuid, $version = null){ + return self::fromBinary(hex2bin(str_replace("-", "", trim($uuid))), $version); + } + + /** + * Creates an UUID from a binary representation + * + * @param string $uuid + * @param int $version + * @return UUID + */ + public static function fromBinary($uuid, $version = null){ + if(strlen($uuid) !== 16){ + throw new \InvalidArgumentException("Must have exactly 16 bytes"); + } + + return new UUID(Binary::readInt(substr($uuid, 0, 4)), Binary::readInt(substr($uuid, 4, 4)), Binary::readInt(substr($uuid, 8, 4)), Binary::readInt(substr($uuid, 12, 4)), $version); + } + + /** + * Creates an UUIDv3 from binary data or list of binary data + * + * @param string ...$data + * @return UUID + */ + public static function fromData(...$data){ + $hash = hash("md5", implode($data), true); + + return self::fromBinary($hash, 3); + } + + public static function fromRandom(){ + return self::fromData(Binary::writeInt(time()), Binary::writeShort(getmypid()), Binary::writeShort(getmyuid()), Binary::writeInt(mt_rand(-0x7fffffff, 0x7fffffff)), Binary::writeInt(mt_rand(-0x7fffffff, 0x7fffffff))); + } + + public function toBinary(){ + return Binary::writeInt($this->parts[0]) . Binary::writeInt($this->parts[1]) . Binary::writeInt($this->parts[2]) . Binary::writeInt($this->parts[3]); + } + + public function toString(){ + $hex = bin2hex(self::toBinary()); + + //xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx 8-4-4-12 + if($this->version !== null){ + return substr($hex, 0, 8) . "-" . substr($hex, 8, 4) . "-" . hexdec($this->version) . substr($hex, 13, 3) . "-8" . substr($hex, 17, 3) . "-" . substr($hex, 20, 12); + } + return substr($hex, 0, 8) . "-" . substr($hex, 8, 4) . "-" . substr($hex, 12, 4) . "-" . substr($hex, 16, 4) . "-" . substr($hex, 20, 12); + } + + public function __toString(){ + return $this->toString(); + } +} \ No newline at end of file diff --git a/src/pocketmine/utils/Utils.php b/src/pocketmine/utils/Utils.php index 8caa74358..b8f99cdff 100644 --- a/src/pocketmine/utils/Utils.php +++ b/src/pocketmine/utils/Utils.php @@ -49,14 +49,23 @@ class Utils{ } } + /** + * @deprecated + */ public static function randomUUID(){ return Utils::toUUID(Binary::writeInt(time()) . Binary::writeShort(getmypid()) . Binary::writeShort(getmyuid()) . Binary::writeInt(mt_rand(-0x7fffffff, 0x7fffffff)) . Binary::writeInt(mt_rand(-0x7fffffff, 0x7fffffff)), 2); } + /** + * @deprecated + */ public static function dataToUUID(...$params){ return Utils::toUUID(hash("md5", implode($params), true), 3); } + /** + * @deprecated + */ public static function toUUID($data, $version = 2, $fixed = "8"){ if(strlen($data) !== 16){ throw new \InvalidArgumentException("Data must be 16 bytes"); @@ -76,7 +85,7 @@ class Utils{ * * @param string $extra optional, additional data to identify the machine * - * @return string + * @return UUID */ public static function getMachineUniqueId($extra = ""){ if(self::$serverUniqueId !== null and $extra === ""){ @@ -127,7 +136,7 @@ class Utils{ $data .= $ext . ":" . phpversion($ext); } - $uuid = Utils::dataToUUID($machine, $data); + $uuid = UUID::fromData($machine, $data); if($extra === ""){ self::$serverUniqueId = $uuid; diff --git a/src/raklib b/src/raklib index ab3ff4420..d7da0c554 160000 --- a/src/raklib +++ b/src/raklib @@ -1 +1 @@ -Subproject commit ab3ff4420f6dea3371bfc410bbb783159cd0560b +Subproject commit d7da0c5549215c7616a6fc103299afb0aeac88e7