Updated some packets for 0.12, UUIDs, other stuff!

This commit is contained in:
Shoghi Cervantes 2015-08-02 01:22:36 +02:00
parent 4258e22c02
commit 5621ab0c49
27 changed files with 951 additions and 380 deletions

View File

@ -25,6 +25,7 @@ use pocketmine\event\server\LowMemoryEvent;
use pocketmine\event\Timings;
use pocketmine\scheduler\GarbageCollectionTask;
use pocketmine\utils\Utils;
use pocketmine\utils\UUID;
class MemoryManager{
@ -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
];

View File

@ -127,6 +127,7 @@ use pocketmine\tile\Spawnable;
use pocketmine\tile\Tile;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
use pocketmine\utils\UUID;
use raklib\Binary;
/**
@ -161,6 +162,8 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
protected $sendIndex = 0;
private $clientSecret;
/** @var Vector3 */
public $speed = null;
@ -182,7 +185,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
public $creationTime = 0;
protected $randomClientId;
protected $uuid;
protected $lastMovement = 0;
/** @var Vector3 */
@ -253,8 +255,8 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
return $this->randomClientId;
}
public function getUniqueId(){
return $this->uuid;
public function getClientSecret(){
return $this->clientSecretId;
}
public function isBanned(){
@ -352,7 +354,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()]);
}
/**
@ -362,7 +364,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);
}
@ -373,7 +375,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);
}
@ -510,7 +512,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);
}
@ -560,12 +563,15 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
*/
public function setDisplayName($name){
$this->displayName = $name;
if($this->spawned){
$this->server->updatePlayerListData($this->getUniqueId(), $this->getId(), $this->getDisplayName(), $isSlim, $str);
}
}
public function setSkin($str, $isSlim = false){
parent::setSkin($str, $isSlim);
if($this->spawned){
$this->respawnToAll();
$this->server->updatePlayerListData($this->getUniqueId(), $this->getId(), $this->getDisplayName(), $isSlim, $str);
}
}
@ -621,18 +627,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]);
}
@ -1580,6 +1574,192 @@ 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(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);
}
/**
* Handles a Minecraft packet
* TODO: Separate all of this in handlers
@ -1624,7 +1804,9 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
$this->randomClientId = $packet->clientId;
$this->loginData = ["clientId" => $packet->clientId, "loginData" => null];
$this->uuid = Utils::dataToUUID($this->randomClientId, $this->iusername, $this->getAddress());
$this->uuid = $packet->clientUUID;
$this->clientSecret = $packet->clientSecret;
$this->rawUUID = $this->uuid->toBinary();
if(count($this->server->getOnlinePlayers()) > $this->server->getMaxPlayers() and $this->kick("disconnectionScreen.serverFull", false)){
break;
@ -1687,164 +1869,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(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:
@ -1873,7 +1898,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;
}
@ -2243,7 +2268,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:
@ -2527,136 +2552,138 @@ 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:
//TODO HACK
$this->server->getLogger()->warning("CRAFTING NOT YET IMPLEMENTED!");
break;
if(count($packet->slots) < 9){
$this->inventory->sendContents($this);
break;
}
foreach($packet->slots as $i => $item){
/** @var Item $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);
}
}
$result = $packet->slots[9];
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){
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;
}
}
}
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;
}
$canCraft = true;
$used = array_fill(0, $this->inventory->getSize(), 0);
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]++;
break;
}
}
if($slot === -1){
$canCraft = false;
break;
}
}
if(!$canCraft){
$this->inventory->sendContents($this);
break;
}
foreach($used as $slot => $count){
if($count === 0){
continue;
}
$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);
}
$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;
if($i < 9 and $item->getId() > 0){
$item->setCount(1);
}
}
$result = $packet->slots[9];
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){
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;
}
}
}
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;
}
$canCraft = true;
$used = array_fill(0, $this->inventory->getSize(), 0);
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]++;
break;
}
}
if($slot === -1){
$canCraft = false;
break;
}
}
if(!$canCraft){
$this->inventory->sendContents($this);
break;
}
foreach($used as $slot => $count){
if($count === 0){
continue;
}
$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);
}
$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:
@ -2761,7 +2788,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){
@ -2918,6 +2945,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() != ""){
@ -3217,12 +3248,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);
}

View File

@ -53,6 +53,8 @@ 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\Item;
use pocketmine\lang\BaseLang;
use pocketmine\level\format\anvil\Anvil;
@ -80,7 +82,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;
@ -111,6 +115,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 +254,9 @@ class Server{
/** @var Player[] */
private $players = [];
/** @var Player[] */
private $playerList = [];
private $identifiers = [];
/** @var Level[] */
@ -705,7 +713,7 @@ class Server{
* @return Player[]
*/
public function getOnlinePlayers(){
return $this->players;
return $this->playerList;
}
public function addRecipe(Recipe $recipe){
@ -2311,8 +2319,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 +2331,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->getUniqueId(), $player->getDisplayName(), $player->isSkinSlim(), $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->setChannel(Network::CHANNEL_ENTITY_SPAWNING));
}
}
public function updatePlayerListData(UUID $uuid, $entityId, $name, $isSlim, $skinData, array $players = null){
$pk = new PlayerListPacket();
$pk->type = PlayerListPacket::TYPE_ADD;
$pk->entries[] = [$uuid, $entityId, $name, $isSlim, $skinData];
Server::broadcastPacket($players === null ? $this->playerList : $players, $pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING));
}
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->setChannel(Network::CHANNEL_ENTITY_SPAWNING));
}
public function sendFullPlayerListData(Player $p){
$pk = new PlayerListPacket();
$pk->type = PlayerListPacket::TYPE_ADD;
foreach($this->playerList as $player){
$pk->entries[] = [$player->getUniqueId(), $player->getUniqueId(), $player->getDisplayName(), $player->isSkinSlim(), $player->getSkinData()];
}
$p->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING));
}
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->setChannel(Network::CHANNEL_WORLD_EVENTS));
}
private function checkTickUpdates($currentTick, $tickTime){
foreach($this->players as $p){
if(!$p->loggedIn and ($tickTime - $p->creationTime) >= 10){
@ -2370,7 +2441,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()){

View File

@ -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,6 +47,11 @@ 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;
@ -62,6 +68,20 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
return $this->isSlim;
}
/**
* @return UUID|null
*/
public function getUniqueId(){
return $this->uuid;
}
/**
* @return string
*/
public function getRawUniqueId(){
return $this->rawUUID;
}
/**
* @param string $str
* @param bool $isSlim
@ -94,6 +114,8 @@ 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->uuid = UUID::fromData($this->getId(), $this->getSkinData(), $this->getNameTag());
}
if(isset($this->namedtag->Inventory) and $this->namedtag->Inventory instanceof Enum){
@ -198,8 +220,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->isSlim, $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;
@ -219,13 +246,18 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
$player->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING));
$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->eid = $this->getUniqueId();
$pk->clientID = $this->getId();
$player->dataPacket($pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING));
unset($this->hasSpawned[$player->getLoaderId()]);

View File

@ -26,6 +26,7 @@ use pocketmine\block\Stone;
use pocketmine\block\Wood;
use pocketmine\block\Wood2;
use pocketmine\item\Item;
use pocketmine\utils\UUID;
class CraftingManager{
@ -38,6 +39,8 @@ class CraftingManager{
/** @var FurnaceRecipe[] */
public $furnaceRecipes = [];
private static $RECIPE_COUNT = 0;
public function __construct(){
$this->registerStonecutter();
@ -519,6 +522,8 @@ class CraftingManager{
}elseif($recipe instanceof FurnaceRecipe){
$this->registerFurnaceRecipe($recipe);
}
$recipe->setId(UUID::fromData(++self::$RECIPE_COUNT, $recipe->getResult()->getId(), $recipe->getResult()->getDamage(), $recipe->getResult()->getCount(), $recipe->getResult()->getCompoundTag()));
}
}

View File

@ -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
*/

View File

@ -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,7 +126,7 @@ 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();
@ -315,7 +315,7 @@ class PlayerInventory extends BaseInventory{
}
}
$pk = new PlayerArmorEquipmentPacket();
$pk = new MobArmorEquipmentPacket();
$pk->eid = $this->getHolder()->getId();
$pk->slots = $slots;
$pk->encode();
@ -372,7 +372,7 @@ class PlayerInventory extends BaseInventory{
}
}
$pk = new PlayerArmorEquipmentPacket();
$pk = new MobArmorEquipmentPacket();
$pk->eid = $this->getHolder()->getId();
$pk->slots = $slots;
$pk->encode();

View File

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

View File

@ -23,11 +23,14 @@ namespace pocketmine\inventory;
use pocketmine\item\Item;
use pocketmine\Server;
use pocketmine\utils\UUID;
class ShapedRecipe implements Recipe{
/** @var Item */
private $output;
private $id = null;
/** @var string[] */
private $rows = [];
@ -61,6 +64,22 @@ class ShapedRecipe implements Recipe{
$this->output = clone $result;
}
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

View File

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

View File

@ -1729,7 +1729,7 @@ class Level implements ChunkManager, Metadatable{
"Text4" => new String("Text4", "")
]));
if($player !== null){
$tile->namedtag->Creator = new String("Creator", $player->getUniqueId());
$tile->namedtag->Creator = new String("Creator", $player->getRawUniqueId());
}
}
$item->setCount($item->getCount() - 1);

View File

@ -36,6 +36,8 @@ 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;
@ -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;
@ -72,6 +74,7 @@ use pocketmine\network\protocol\TakeItemEntityPacket;
use pocketmine\network\protocol\TileEventPacket;
use pocketmine\network\protocol\UpdateBlockPacket;
use pocketmine\network\protocol\UseItemPacket;
use pocketmine\network\protocol\PlayerListPacket;
use pocketmine\Player;
use pocketmine\Server;
use pocketmine\utils\Binary;
@ -296,6 +299,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);
@ -314,8 +318,8 @@ class Network{
$this->registerPacket(ProtocolInfo::LEVEL_EVENT_PACKET, LevelEventPacket::class);
$this->registerPacket(ProtocolInfo::TILE_EVENT_PACKET, TileEventPacket::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);
@ -333,10 +337,12 @@ 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::FULL_CHUNK_DATA_PACKET, FullChunkDataPacket::class);
$this->registerPacket(ProtocolInfo::SET_DIFFICULTY_PACKET, SetDifficultyPacket::class);
$this->registerPacket(ProtocolInfo::BATCH_PACKET, BatchPacket::class);
$this->registerPacket(ProtocolInfo::PLAYER_LIST_PACKET, PlayerListPacket::class);
}
}

View File

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

View File

@ -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 = [];

View File

@ -0,0 +1,122 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\network\protocol;
#include <rules/DataPacket.h>
use pocketmine\inventory\FurnaceRecipe;
use pocketmine\inventory\ShapedRecipe;
use pocketmine\inventory\ShapelessRecipe;
use pocketmine\utils\Binary;
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 = 4;
/** @var object[] */
public $entries = [];
public $cleanRecipes = false;
public function writeEntry($entry){
if($entry instanceof ShapelessRecipe){
$this->writeShapelessRecipe($entry);
}elseif($entry instanceof ShapedRecipe){
$this->writeShapedRecipe($entry);
}elseif($entry instanceof FurnaceRecipe){
$this->writeFurnaceRecipe($entry);
}
}
private function writeShapelessRecipe(ShapelessRecipe $recipe){
$this->putInt(CraftingDataPacket::ENTRY_SHAPELESS);
$this->putInt($recipe->getIngredientCount());
foreach($recipe->getIngredientList() as $item){
$this->putSlot($item);
}
$this->putInt(1);
$this->putSlot($recipe->getResult());
}
private function writeShapedRecipe(ShapedRecipe $recipe){
$this->putInt(CraftingDataPacket::ENTRY_SHAPED);
}
private function writeFurnaceRecipe(FurnaceRecipe $recipe){
if($recipe->getInput()->getDamage() !== 0){ //Data recipe
$this->putInt(CraftingDataPacket::ENTRY_FURNACE_DATA);
$this->putInt(($recipe->getInput()->getId() << 16) | ($recipe->getInput()->getDamage()));
$this->putSlot($recipe->getResult());
}else{
$this->putInt(CraftingDataPacket::ENTRY_FURNACE);
$this->putInt($recipe->getInput()->getId());
$this->putSlot($recipe->getResult());
}
}
private function writeEnchant(){
$entry = Binary::writeInt(CraftingDataPacket::ENTRY_ENCHANT);
//TODO
}
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 addEnchant(){
//TODO
}
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){
}
}
}

View File

@ -0,0 +1,62 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\network\protocol;
#include <rules/DataPacket.h>
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(){
}
}

View File

@ -28,6 +28,7 @@ use pocketmine\utils\Binary;
#endif
use pocketmine\item\Item;
use pocketmine\utils\UUID;
abstract class DataPacket extends \stdClass{
@ -162,10 +163,18 @@ abstract class DataPacket extends \stdClass{
}
}
protected function getUUID(){
return UUID::fromBinary($this->get(16));
}
protected function putUUID(UUID $uuid){
$this->put($uuid->toBinary());
}
protected function getSlot(){
$id = $this->getShort(false);
$id = $this->getShort(true);
if($id == 0xffff){
if($id <= 0){
return Item::get(0, 0, 0);
}
@ -191,7 +200,7 @@ abstract class DataPacket extends \stdClass{
protected function putSlot(Item $item){
if($item->getId() === 0){
$this->putShort(0xffff);
$this->putShort(0);
return;
}

View File

@ -43,6 +43,8 @@ class EntityEventPacket extends DataPacket{
const AMBIENT_SOUND = 16;
const RESPAWN = 17;
//TODO add new events
public $eid;
public $event;

View File

@ -30,7 +30,7 @@ interface Info{
/**
* Actual Minecraft: PE protocol version
*/
const CURRENT_PROTOCOL = 28;
const CURRENT_PROTOCOL = 30;
const LOGIN_PACKET = 0x82;
const PLAY_STATUS_PACKET = 0x83;
@ -56,8 +56,8 @@ interface Info{
const ENTITY_EVENT_PACKET = 0x97;
const MOB_EFFECT_PACKET = 0x98;
const UPDATE_ATTRIBUTES_PACKET = 0x99;
const PLAYER_EQUIPMENT_PACKET = 0x9a;
const PLAYER_ARMOR_EQUIPMENT_PACKET = 0x9b;
const MOB_EQUIPMENT_PACKET = 0x9a;
const MOB_ARMOR_EQUIPMENT_PACKET = 0x9b;
const INTERACT_PACKET = 0x9c;
const USE_ITEM_PACKET = 0x9d;
const PLAYER_ACTION_PACKET = 0x9e;
@ -75,14 +75,17 @@ interface Info{
const CONTAINER_SET_SLOT_PACKET = 0xaa;
const CONTAINER_SET_DATA_PACKET = 0xab;
const CONTAINER_SET_CONTENT_PACKET = 0xac;
const ADVENTURE_SETTINGS_PACKET = 0xad;
const TILE_ENTITY_DATA_PACKET = 0xae;
//const PLAYER_INPUT_PACKET = 0xaf;
const FULL_CHUNK_DATA_PACKET = 0xb0;
const SET_DIFFICULTY_PACKET = 0xb1;
//const CHANGE_DIMENSION_PACKET = 0xb2;
//const SET_PLAYER_GAMETYPE_PACKET = 0xb3;
//const PLAYER_LIST_PACKET = 0xb4;
const CRAFTING_DATA_PACKET = 0xad;
const CRAFTING_EVENT_PACKET = 0xae;
const ADVENTURE_SETTINGS_PACKET = 0xaf;
const TILE_ENTITY_DATA_PACKET = 0xb0;
//const PLAYER_INPUT_PACKET = 0xb1;
const FULL_CHUNK_DATA_PACKET = 0xb2;
const SET_DIFFICULTY_PACKET = 0xb3;
//const CHANGE_DIMENSION_PACKET = 0xb4;
//const SET_PLAYER_GAMETYPE_PACKET = 0xb5;
const PLAYER_LIST_PACKET = 0xb6;
//const TELEMETRY_EVENT_PACKET = 0xb7;
}

View File

@ -32,6 +32,10 @@ class LoginPacket extends DataPacket{
public $protocol2;
public $clientId;
public $clientUUID;
public $serverAddress;
public $clientSecret;
public $slim = false;
public $skin = null;
@ -39,11 +43,15 @@ class LoginPacket extends DataPacket{
$this->username = $this->getString();
$this->protocol1 = $this->getInt();
$this->protocol2 = $this->getInt();
if($this->protocol1 < 22){ //New fields!
if($this->protocol1 < Info::CURRENT_PROTOCOL){ //New fields!
$this->setBuffer(null, 0); //Skip batch packet handling
return;
}
$this->clientId = $this->getLong();
$this->clientUUID = $this->getUUID();
$this->serverAddress = $this->getString();
$this->clientSecret = $this->getString();
$this->slim = $this->getByte() > 0;
$this->skin = $this->getString();
}

View File

@ -24,27 +24,27 @@ namespace pocketmine\network\protocol;
#include <rules/DataPacket.h>
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]);
}
}

View File

@ -24,19 +24,17 @@ namespace pocketmine\network\protocol;
#include <rules/DataPacket.h>
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);
}

View File

@ -0,0 +1,64 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\network\protocol;
#include <rules/DataPacket.h>
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->putByte($d[3] ? 1 : 0);
$this->putString($d[4]);
}else{
$this->putUUID($d[0]);
}
}
}
}

View File

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

View File

@ -101,13 +101,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());
}
}

View File

@ -0,0 +1,105 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\utils;
class UUID{
private $parts = [0, 0, 0, 0];
private $version = null;
public function __construct($part1 = 0, $part2 = 0, $part3 = 0, $part4 = 0, $version = null){
$this->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();
}
}

View File

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