diff --git a/.gitignore b/.gitignore index b08db6710..17d9ae8d1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ timings/* server.properties /pocketmine.yml memoryDump_*/* +resource_packs/ # Common IDEs .idea/ diff --git a/src/pocketmine/CrashDump.php b/src/pocketmine/CrashDump.php index 38188dc38..5bfc6b569 100644 --- a/src/pocketmine/CrashDump.php +++ b/src/pocketmine/CrashDump.php @@ -21,7 +21,7 @@ namespace pocketmine; -use pocketmine\network\protocol\Info; +use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\plugin\PluginBase; use pocketmine\plugin\PluginLoadOrder; use pocketmine\utils\Utils; @@ -226,7 +226,7 @@ class CrashDump{ $this->data["general"] = []; $this->data["general"]["version"] = $version->get(false); $this->data["general"]["build"] = $version->getBuild(); - $this->data["general"]["protocol"] = Info::CURRENT_PROTOCOL; + $this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL; $this->data["general"]["api"] = \pocketmine\API_VERSION; $this->data["general"]["git"] = \pocketmine\GIT_COMMIT; $this->data["general"]["raklib"] = RakLib::VERSION; @@ -235,7 +235,7 @@ class CrashDump{ $this->data["general"]["zend"] = zend_version(); $this->data["general"]["php_os"] = PHP_OS; $this->data["general"]["os"] = Utils::getOS(); - $this->addLine("PocketMine-MP version: " . $version->get(false) . " #" . $version->getBuild() . " [Protocol " . Info::CURRENT_PROTOCOL . "; API " . API_VERSION . "]"); + $this->addLine("PocketMine-MP version: " . $version->get(false) . " #" . $version->getBuild() . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "; API " . API_VERSION . "]"); $this->addLine("Git commit: " . GIT_COMMIT); $this->addLine("uname -a: " . php_uname("a")); $this->addLine("PHP Version: " . phpversion()); diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index ac2a05e82..27f71bef3 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -102,41 +102,101 @@ use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\LongTag; use pocketmine\nbt\tag\ShortTag; use pocketmine\nbt\tag\StringTag; -use pocketmine\network\protocol\AdventureSettingsPacket; -use pocketmine\network\protocol\AnimatePacket; -use pocketmine\network\protocol\AvailableCommandsPacket; -use pocketmine\network\protocol\BatchPacket; -use pocketmine\network\protocol\ChunkRadiusUpdatedPacket; -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\InteractPacket; -use pocketmine\network\protocol\MovePlayerPacket; -use pocketmine\network\protocol\PlayerActionPacket; -use pocketmine\network\protocol\PlayStatusPacket; -use pocketmine\network\protocol\ResourcePacksInfoPacket; -use pocketmine\network\protocol\RespawnPacket; -use pocketmine\network\protocol\SetEntityMotionPacket; -use pocketmine\network\protocol\SetHealthPacket; -use pocketmine\network\protocol\SetPlayerGameTypePacket; -use pocketmine\network\protocol\SetSpawnPositionPacket; -use pocketmine\network\protocol\SetTimePacket; -use pocketmine\network\protocol\StartGamePacket; -use pocketmine\network\protocol\TakeItemEntityPacket; -use pocketmine\network\protocol\TextPacket; -use pocketmine\network\protocol\TransferPacket; -use pocketmine\network\protocol\UpdateAttributesPacket; -use pocketmine\network\protocol\UpdateBlockPacket; +use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\AddEntityPacket; +use pocketmine\network\mcpe\protocol\AddHangingEntityPacket; +use pocketmine\network\mcpe\protocol\AddItemEntityPacket; +use pocketmine\network\mcpe\protocol\AddItemPacket; +use pocketmine\network\mcpe\protocol\AddPaintingPacket; +use pocketmine\network\mcpe\protocol\AddPlayerPacket; +use pocketmine\network\mcpe\protocol\AdventureSettingsPacket; +use pocketmine\network\mcpe\protocol\AnimatePacket; +use pocketmine\network\mcpe\protocol\AvailableCommandsPacket; +use pocketmine\network\mcpe\protocol\BlockEntityDataPacket; +use pocketmine\network\mcpe\protocol\BlockEventPacket; +use pocketmine\network\mcpe\protocol\BlockPickRequestPacket; +use pocketmine\network\mcpe\protocol\ChangeDimensionPacket; +use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket; +use pocketmine\network\mcpe\protocol\ClientboundMapItemDataPacket; +use pocketmine\network\mcpe\protocol\ClientToServerHandshakePacket; +use pocketmine\network\mcpe\protocol\CommandBlockUpdatePacket; +use pocketmine\network\mcpe\protocol\CommandStepPacket; +use pocketmine\network\mcpe\protocol\ContainerClosePacket; +use pocketmine\network\mcpe\protocol\ContainerOpenPacket; +use pocketmine\network\mcpe\protocol\ContainerSetContentPacket; +use pocketmine\network\mcpe\protocol\ContainerSetDataPacket; +use pocketmine\network\mcpe\protocol\ContainerSetSlotPacket; +use pocketmine\network\mcpe\protocol\CraftingDataPacket; +use pocketmine\network\mcpe\protocol\CraftingEventPacket; +use pocketmine\network\mcpe\protocol\DataPacket; +use pocketmine\network\mcpe\protocol\DisconnectPacket; +use pocketmine\network\mcpe\protocol\DropItemPacket; +use pocketmine\network\mcpe\protocol\EntityEventPacket; +use pocketmine\network\mcpe\protocol\ExplodePacket; +use pocketmine\network\mcpe\protocol\FullChunkDataPacket; +use pocketmine\network\mcpe\protocol\HurtArmorPacket; +use pocketmine\network\mcpe\protocol\InteractPacket; +use pocketmine\network\mcpe\protocol\InventoryActionPacket; +use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelSoundEventPacket; +use pocketmine\network\mcpe\protocol\LoginPacket; +use pocketmine\network\mcpe\protocol\MapInfoRequestPacket; +use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket; +use pocketmine\network\mcpe\protocol\MobEffectPacket; +use pocketmine\network\mcpe\protocol\MobEquipmentPacket; +use pocketmine\network\mcpe\protocol\MoveEntityPacket; +use pocketmine\network\mcpe\protocol\MovePlayerPacket; +use pocketmine\network\mcpe\protocol\PlayerActionPacket; +use pocketmine\network\mcpe\protocol\PlayerFallPacket; +use pocketmine\network\mcpe\protocol\PlayerInputPacket; +use pocketmine\network\mcpe\protocol\PlayerListPacket; +use pocketmine\network\mcpe\protocol\PlaySoundPacket; +use pocketmine\network\mcpe\protocol\PlayStatusPacket; +use pocketmine\network\mcpe\protocol\ProtocolInfo; +use pocketmine\network\mcpe\protocol\RemoveBlockPacket; +use pocketmine\network\mcpe\protocol\RemoveEntityPacket; +use pocketmine\network\mcpe\protocol\ReplaceItemInSlotPacket; +use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket; +use pocketmine\network\mcpe\protocol\ResourcePackChunkDataPacket; +use pocketmine\network\mcpe\protocol\ResourcePackChunkRequestPacket; +use pocketmine\network\mcpe\protocol\ResourcePackClientResponsePacket; +use pocketmine\network\mcpe\protocol\ResourcePackDataInfoPacket; +use pocketmine\network\mcpe\protocol\ResourcePacksInfoPacket; +use pocketmine\network\mcpe\protocol\ResourcePackStackPacket; +use pocketmine\network\mcpe\protocol\RespawnPacket; +use pocketmine\network\mcpe\protocol\RiderJumpPacket; +use pocketmine\network\mcpe\protocol\ServerToClientHandshakePacket; +use pocketmine\network\mcpe\protocol\SetCommandsEnabledPacket; +use pocketmine\network\mcpe\protocol\SetDifficultyPacket; +use pocketmine\network\mcpe\protocol\SetEntityDataPacket; +use pocketmine\network\mcpe\protocol\SetEntityLinkPacket; +use pocketmine\network\mcpe\protocol\SetEntityMotionPacket; +use pocketmine\network\mcpe\protocol\SetHealthPacket; +use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket; +use pocketmine\network\mcpe\protocol\SetSpawnPositionPacket; +use pocketmine\network\mcpe\protocol\SetTimePacket; +use pocketmine\network\mcpe\protocol\SetTitlePacket; +use pocketmine\network\mcpe\protocol\ShowCreditsPacket; +use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket; +use pocketmine\network\mcpe\protocol\StartGamePacket; +use pocketmine\network\mcpe\protocol\StopSoundPacket; +use pocketmine\network\mcpe\protocol\TakeItemEntityPacket; +use pocketmine\network\mcpe\protocol\TextPacket; +use pocketmine\network\mcpe\protocol\TransferPacket; +use pocketmine\network\mcpe\protocol\UnknownPacket; +use pocketmine\network\mcpe\protocol\UpdateAttributesPacket; +use pocketmine\network\mcpe\protocol\UpdateBlockPacket; +use pocketmine\network\mcpe\protocol\UpdateTradePacket; +use pocketmine\network\mcpe\protocol\UseItemPacket; use pocketmine\network\SourceInterface; use pocketmine\permission\PermissibleBase; use pocketmine\permission\PermissionAttachment; use pocketmine\plugin\Plugin; +use pocketmine\resourcepacks\ResourcePack; use pocketmine\tile\ItemFrame; use pocketmine\tile\Spawnable; +use pocketmine\tile\Tile; use pocketmine\utils\TextFormat; use pocketmine\utils\UUID; @@ -144,7 +204,7 @@ use pocketmine\utils\UUID; /** * Main class that handles networking, recovery, and packet sending to the server part */ -class Player extends Human implements CommandSender, InventoryHolder, ChunkLoader, IPlayer{ +class Player extends Human implements CommandSender, InventoryHolder, ChunkLoader, IPlayer, NetworkSession{ const SURVIVAL = 0; const CREATIVE = 1; @@ -152,6 +212,28 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade const SPECTATOR = 3; const VIEW = Player::SPECTATOR; + /** + * Checks a supplied username and checks it is valid. + * @param string $name + * + * @return bool + */ + public static function isValidUserName(string $name) : bool{ + $lname = strtolower($name); + $len = strlen($name); + return $lname !== "rcon" and $lname !== "console" and $len >= 1 and $len <= 16 and preg_match("/[^A-Za-z0-9_]/", $name) === 0; + } + + /** + * Checks the length of a supplied skin bitmap and returns whether the length is valid. + * @param string $skin + * + * @return bool + */ + public static function isValidSkin(string $skin) : bool{ + return strlen($skin) === 64 * 64 * 4 or strlen($skin) === 64 * 32 * 4; + } + /** @var SourceInterface */ protected $interface; @@ -159,6 +241,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade public $playedBefore; public $spawned = false; public $loggedIn = false; + public $joined = false; public $gamemode; public $lastBreak; @@ -818,9 +901,14 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk->z = $pos->z; $this->dataPacket($pk); - $pk = new PlayStatusPacket(); - $pk->status = PlayStatusPacket::PLAYER_SPAWN; - $this->dataPacket($pk); + $this->sendPlayStatus(PlayStatusPacket::PLAYER_SPAWN); + + 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); + } $this->server->getPluginManager()->callEvent($ev = new PlayerJoinEvent($this, new TranslationContainer(TextFormat::YELLOW . "%multiplayer.player.joined", [ @@ -858,6 +946,8 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk->z = $pos->z; $this->dataPacket($pk); } + + $this->joined = true; } protected function orderChunks(){ @@ -989,6 +1079,11 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade return false; } + //Basic safety restriction. TODO: improve this + if(!$this->loggedIn and !$packet->canBeSentBeforeLogin()){ + throw new \InvalidArgumentException("Attempted to send " . get_class($packet) . " to " . $this->getName() . " too early"); + } + $timings = Timings::getSendDataPacketTimings($packet); $timings->startTiming(); @@ -1022,6 +1117,11 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade return false; } + //Basic safety restriction. TODO: improve this + if(!$this->loggedIn and !$packet->canBeSentBeforeLogin()){ + throw new \InvalidArgumentException("Attempted to send " . get_class($packet) . " to " . $this->getName() . " too early"); + } + $timings = Timings::getSendDataPacketTimings($packet); $timings->startTiming(); $this->server->getPluginManager()->callEvent($ev = new DataPacketSendEvent($this, $packet)); @@ -1219,7 +1319,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade public function sendSettings(){ $pk = new AdventureSettingsPacket(); $pk->flags = 0; - $pk->worldImmutable = $this->isAdventure(); + $pk->worldImmutable = $this->isSpectator(); $pk->autoJump = $this->autoJump; $pk->allowFlight = $this->allowFlight; $pk->noClip = $this->isSpectator(); @@ -1466,14 +1566,19 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->teleport($ev->getTo()); }else{ $this->level->addEntityMovement($this->x >> 4, $this->z >> 4, $this->getId(), $this->x, $this->y + $this->getEyeHeight(), $this->z, $this->yaw, $this->pitch, $this->yaw); + + $distance = $from->distance($to); + + //TODO: check swimming (adds 0.015 exhaustion in MCPE) + if($this->isSprinting()){ + $this->exhaust(0.1 * $distance, PlayerExhaustEvent::CAUSE_SPRINTING); + }else{ + $this->exhaust(0.01 * $distance, PlayerExhaustEvent::CAUSE_WALKING); + } } } } - if(!$this->isSpectator()){ - $this->checkNearEntities($tickDiff); - } - $this->speed = ($to->subtract($from))->divide($tickDiff); }elseif($distanceSquared == 0){ $this->speed = new Vector3(0, 0, 0); @@ -1504,12 +1609,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if(parent::setMotion($mot)){ if($this->chunk !== null){ $this->level->addEntityMotion($this->chunk->getX(), $this->chunk->getZ(), $this->getId(), $this->motionX, $this->motionY, $this->motionZ); - $pk = new SetEntityMotionPacket(); - $pk->eid = $this->id; - $pk->motionX = $mot->x; - $pk->motionY = $mot->y; - $pk->motionZ = $mot->z; - $this->dataPacket($pk); } if($this->motionY > 0){ @@ -1569,28 +1668,33 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->processMovement($tickDiff); $this->entityBaseTick($tickDiff); - if(!$this->isSpectator() and $this->speed !== null){ - if($this->onGround){ - if($this->inAirTicks !== 0){ - $this->startAirTicks = 5; - } - $this->inAirTicks = 0; - }else{ - if(!$this->allowFlight and $this->inAirTicks > 10 and !$this->isSleeping() and !$this->isImmobile()){ - $expectedVelocity = (-$this->gravity) / $this->drag - ((-$this->gravity) / $this->drag) * exp(-$this->drag * ($this->inAirTicks - $this->startAirTicks)); - $diff = ($this->speed->y - $expectedVelocity) ** 2; + if(!$this->isSpectator()){ + $this->checkNearEntities($tickDiff); - if(!$this->hasEffect(Effect::JUMP) and $diff > 0.6 and $expectedVelocity < $this->speed->y and !$this->server->getAllowFlight()){ - if($this->inAirTicks < 100){ - $this->setMotion(new Vector3(0, $expectedVelocity, 0)); - }elseif($this->kick("Flying is not enabled on this server")){ - $this->timings->stopTiming(); - return false; + if($this->speed !== null){ + if($this->onGround){ + if($this->inAirTicks !== 0){ + $this->startAirTicks = 5; + } + $this->inAirTicks = 0; + }else{ + if(!$this->allowFlight and $this->inAirTicks > 10 and !$this->isSleeping() and !$this->isImmobile()){ + $expectedVelocity = (-$this->gravity) / $this->drag - ((-$this->gravity) / $this->drag) * exp(-$this->drag * ($this->inAirTicks - $this->startAirTicks)); + $diff = ($this->speed->y - $expectedVelocity) ** 2; + + if(!$this->hasEffect(Effect::JUMP) and $diff > 0.6 and $expectedVelocity < $this->speed->y and !$this->server->getAllowFlight()){ + if($this->inAirTicks < 100){ + $this->setMotion(new Vector3(0, $expectedVelocity, 0)); + }elseif($this->kick("Flying is not enabled on this server")){ + $this->timings->stopTiming(); + + return false; + } } } - } - ++$this->inAirTicks; + ++$this->inAirTicks; + } } } } @@ -1599,16 +1703,21 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->timings->stopTiming(); - //TODO: remove this workaround (broken client MCPE 1.0.0) - if(count($this->messageQueue) > 0){ - $pk = new TextPacket(); - $pk->type = TextPacket::TYPE_RAW; - $pk->message = implode("\n", $this->messageQueue); - $this->dataPacket($pk); - $this->messageQueue = []; + return true; + } + + public function doFoodTick(int $tickDiff = 1){ + if($this->isSurvival()){ + parent::doFoodTick($tickDiff); + } + } + + public function exhaust(float $amount, int $cause = PlayerExhaustEvent::CAUSE_CUSTOM) : float{ + if($this->isSurvival()){ + return parent::exhaust($amount, $cause); } - return true; + return 0.0; } public function checkNetwork(){ @@ -1642,29 +1751,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade return ($dot1 - $dot) >= -$maxDiff; } - public function onPlayerPreLogin(){ - //TODO: implement auth - $this->tryAuthenticate(); - } - public function tryAuthenticate(){ - $pk = new PlayStatusPacket(); - $pk->status = PlayStatusPacket::LOGIN_SUCCESS; - $this->dataPacket($pk); - //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($this->iusername)){ @@ -1677,74 +1764,73 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade 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 $p->iusername === $this->iusername){ 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); - $this->playedBefore = ($nbt["lastPlayed"] - $nbt["firstPlayed"]) > 1; // microtime(true) - microtime(true) may have less than one millisecond difference - if(!isset($nbt->NameTag)){ - $nbt->NameTag = new StringTag("NameTag", $this->username); + $this->namedtag = $this->server->getOfflinePlayerData($this->username); + + $this->playedBefore = ($this->namedtag["lastPlayed"] - $this->namedtag["firstPlayed"]) > 1; // microtime(true) - microtime(true) may have less than one millisecond difference + if(!isset($this->namedtag->NameTag)){ + $this->namedtag->NameTag = new StringTag("NameTag", $this->username); }else{ - $nbt["NameTag"] = $this->username; + $this->namedtag["NameTag"] = $this->username; } - $this->gamemode = $nbt["playerGameType"] & 0x03; + $this->gamemode = $this->namedtag["playerGameType"] & 0x03; if($this->server->getForceGamemode()){ $this->gamemode = $this->server->getGamemode(); - $nbt->playerGameType = new IntTag("playerGameType", $this->gamemode); + $this->namedtag->playerGameType = new IntTag("playerGameType", $this->gamemode); } $this->allowFlight = (bool) ($this->gamemode & 0x01); - if(($level = $this->server->getLevelByName($nbt["Level"])) === null){ + if(($level = $this->server->getLevelByName($this->namedtag["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; + $this->namedtag["Level"] = $this->level->getName(); + $this->namedtag["Pos"][0] = $this->level->getSpawnLocation()->x; + $this->namedtag["Pos"][1] = $this->level->getSpawnLocation()->y; + $this->namedtag["Pos"][2] = $this->level->getSpawnLocation()->z; }else{ $this->setLevel($level); } - if(!($nbt instanceof CompoundTag)){ - $this->close($this->getLeaveMessage(), "Invalid data"); - - return; - } - $this->achievements = []; /** @var ByteTag $achievement */ - foreach($nbt->Achievements as $achievement){ + foreach($this->namedtag->Achievements as $achievement){ $this->achievements[$achievement->getName()] = $achievement->getValue() > 0 ? true : false; } - $nbt->lastPlayed = new LongTag("lastPlayed", floor(microtime(true) * 1000)); + $this->namedtag->lastPlayed = new LongTag("lastPlayed", floor(microtime(true) * 1000)); if($this->server->getAutoSave()){ - $this->server->saveOfflinePlayerData($this->username, $nbt, true); + $this->server->saveOfflinePlayerData($this->username, $this->namedtag, true); } - parent::__construct($this->level, $nbt); - $this->loggedIn = true; - $this->server->addOnlinePlayer($this); + $this->sendPlayStatus(PlayStatusPacket::LOGIN_SUCCESS); + $this->loggedIn = true; + + $pk = new ResourcePacksInfoPacket(); + $manager = $this->server->getResourceManager(); + $pk->resourcePackEntries = $manager->getResourceStack(); + $pk->mustAccept = $manager->resourcePacksRequired(); + $this->dataPacket($pk); + } + + protected function completeLoginSequence(){ + parent::__construct($this->level, $this->namedtag); $this->server->getPluginManager()->callEvent($ev = new PlayerLoginEvent($this, "Plugin reason")); if($ev->isCancelled()){ $this->close($this->getLeaveMessage(), $ev->getKickMessage()); @@ -1752,8 +1838,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade return; } - $this->dataPacket(new ResourcePacksInfoPacket()); - if(!$this->hasValidSpawnPosition() and isset($this->namedtag->SpawnLevel) and ($level = $this->server->getLevelByName($this->namedtag["SpawnLevel"])) instanceof Level){ $this->spawnPosition = new WeakPosition($this->namedtag["SpawnX"], $this->namedtag["SpawnY"], $this->namedtag["SpawnZ"], $level); } @@ -1815,15 +1899,1460 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->forceMovement = $this->teleportPosition = $this->getPosition(); + $this->server->addOnlinePlayer($this); + $this->server->onPlayerLogin($this); } + public function handleLogin(LoginPacket $packet) : bool{ + if($this->loggedIn){ + return false; + } + + $this->username = TextFormat::clean($packet->username); + $this->displayName = $this->username; + $this->iusername = strtolower($this->username); + $this->setDataProperty(self::DATA_NAMETAG, self::DATA_TYPE_STRING, $this->username, false); + + if(count($this->server->getOnlinePlayers()) >= $this->server->getMaxPlayers() and $this->kick("disconnectionScreen.serverFull", false)){ + return true; + } + + if($packet->protocol !== ProtocolInfo::CURRENT_PROTOCOL){ + if($packet->protocol < ProtocolInfo::CURRENT_PROTOCOL){ + $message = "disconnectionScreen.outdatedClient"; + $this->sendPlayStatus(PlayStatusPacket::LOGIN_FAILED_CLIENT, true); + }else{ + $message = "disconnectionScreen.outdatedServer"; + $this->sendPlayStatus(PlayStatusPacket::LOGIN_FAILED_SERVER, true); + } + $this->close("", $message, false); + + return true; + } + + $this->randomClientId = $packet->clientId; + + $this->uuid = UUID::fromString($packet->clientUUID); + $this->rawUUID = $this->uuid->toBinary(); + + if(!Player::isValidUserName($packet->username)){ + $this->close("", "disconnectionScreen.invalidName"); + return true; + } + + if(!Player::isValidSkin($packet->skin)){ + $this->close("", "disconnectionScreen.invalidSkin"); + return true; + } + + $this->setSkin($packet->skin, $packet->skinId); + + $this->server->getPluginManager()->callEvent($ev = new PlayerPreLoginEvent($this, "Plugin reason")); + if($ev->isCancelled()){ + $this->close("", $ev->getKickMessage()); + + return true; + } + + //TODO: add JWT verification, add encryption + + $this->processLogin(); + + return true; + } + + public function handlePlayStatus(PlayStatusPacket $packet) : bool{ + return false; + } + + public function sendPlayStatus(int $status, bool $immediate = false){ + $pk = new PlayStatusPacket(); + $pk->status = $status; + if($immediate){ + $this->directDataPacket($pk); + }else{ + $this->dataPacket($pk); + } + } + + public function handleServerToClientHandshake(ServerToClientHandshakePacket $packet) : bool{ + return false; + } + + public function handleClientToServerHandshake(ClientToServerHandshakePacket $packet) : bool{ + return false; //TODO + } + + public function handleDisconnect(DisconnectPacket $packet) : bool{ + return false; + } + + public function handleResourcePacksInfo(ResourcePacksInfoPacket $packet) : bool{ + return false; + } + + public function handleResourcePackStack(ResourcePackStackPacket $packet) : bool{ + return false; + } + + public function handleResourcePackClientResponse(ResourcePackClientResponsePacket $packet) : bool{ + switch($packet->status){ + case ResourcePackClientResponsePacket::STATUS_REFUSED: + //TODO: add lang strings for this + $this->close("", "You must accept resource packs to join this server.", true); + break; + case ResourcePackClientResponsePacket::STATUS_SEND_PACKS: + $manager = $this->server->getResourceManager(); + foreach($packet->packIds as $uuid){ + $pack = $manager->getPackById($uuid); + if(!($pack instanceof ResourcePack)){ + //Client requested a resource pack but we don't have it available on the server + $this->close("", "disconnectionScreen.resourcePack", true); + $this->server->getLogger()->debug("Got a resource pack request for unknown pack with UUID " . $uuid . ", available packs: " . implode(", ", $manager->getPackIdList())); + return false; + } + + $pk = new ResourcePackDataInfoPacket(); + $pk->packId = $pack->getPackId(); + $pk->maxChunkSize = 1048576; //1MB + $pk->chunkCount = $pack->getPackSize() / $pk->maxChunkSize; + $pk->compressedPackSize = $pack->getPackSize(); + $pk->sha256 = $pack->getSha256(); + $this->dataPacket($pk); + } + + break; + case ResourcePackClientResponsePacket::STATUS_HAVE_ALL_PACKS: + $pk = new ResourcePackStackPacket(); + $manager = $this->server->getResourceManager(); + $pk->resourcePackStack = $manager->getResourceStack(); + $pk->mustAccept = $manager->resourcePacksRequired(); + $this->dataPacket($pk); + break; + case ResourcePackClientResponsePacket::STATUS_COMPLETED: + $this->completeLoginSequence(); + break; + default: + return false; + } + + return true; + } + + public function handleText(TextPacket $packet) : bool{ + if($this->spawned === false or !$this->isAlive()){ + return true; + } + + $this->craftingType = 0; + if($packet->type === TextPacket::TYPE_CHAT){ + $packet->message = TextFormat::clean($packet->message, $this->removeFormat); + foreach(explode("\n", $packet->message) as $message){ + if(trim($message) != "" and strlen($message) <= 255 and $this->messageCounter-- > 0){ + if(substr($message, 0, 2) === "./"){ //Command (./ = fast hack for old plugins post 0.16) + $message = substr($message, 1); + } + + $ev = new PlayerCommandPreprocessEvent($this, $message); + + if(mb_strlen($ev->getMessage(), "UTF-8") > 320){ + $ev->setCancelled(); + } + $this->server->getPluginManager()->callEvent($ev); + + if($ev->isCancelled()){ + break; + } + + if(substr($ev->getMessage(), 0, 1) === "/"){ + Timings::$playerCommandTimer->startTiming(); + $this->server->dispatchCommand($ev->getPlayer(), substr($ev->getMessage(), 1)); + Timings::$playerCommandTimer->stopTiming(); + }else{ + $this->server->getPluginManager()->callEvent($ev = new PlayerChatEvent($this, $ev->getMessage())); + if(!$ev->isCancelled()){ + $this->server->broadcastMessage($this->getServer()->getLanguage()->translateString($ev->getFormat(), [$ev->getPlayer()->getDisplayName(), $ev->getMessage()]), $ev->getRecipients()); + } + } + } + } + } + + return true; + } + + public function handleSetTime(SetTimePacket $packet) : bool{ + return false; + } + + public function handleStartGame(StartGamePacket $packet) : bool{ + return false; + } + + public function handleAddPlayer(AddPlayerPacket $packet) : bool{ + return false; + } + + public function handleAddEntity(AddEntityPacket $packet) : bool{ + return false; + } + + public function handleRemoveEntity(RemoveEntityPacket $packet) : bool{ + return false; + } + + public function handleAddItemEntity(AddItemEntityPacket $packet) : bool{ + return false; + } + + public function handleAddHangingEntity(AddHangingEntityPacket $packet) : bool{ + return false; + } + + public function handleTakeItemEntity(TakeItemEntityPacket $packet) : bool{ + return false; + } + + public function handleMoveEntity(MoveEntityPacket $packet) : bool{ + return false; + } + + public function handleMovePlayer(MovePlayerPacket $packet) : bool{ + $newPos = new Vector3($packet->x, $packet->y - $this->getEyeHeight(), $packet->z); + + $revert = false; + if(!$this->isAlive() or $this->spawned !== true){ + $revert = true; + $this->forceMovement = new Vector3($this->x, $this->y, $this->z); + } + + if($this->teleportPosition !== null or ($this->forceMovement instanceof Vector3 and ($newPos->distanceSquared($this->forceMovement) > 0.1 or $revert))){ + $this->sendPosition($this->forceMovement, $packet->yaw, $packet->pitch, MovePlayerPacket::MODE_RESET); + }else{ + $packet->yaw %= 360; + $packet->pitch %= 360; + + if($packet->yaw < 0){ + $packet->yaw += 360; + } + + $this->setRotation($packet->yaw, $packet->pitch); + $this->newPosition = $newPos; + $this->forceMovement = null; + } + + return true; + } + + public function handleRiderJump(RiderJumpPacket $packet) : bool{ + return false; + } + + public function handleRemoveBlock(RemoveBlockPacket $packet) : bool{ + if($this->spawned === false or !$this->isAlive()){ + return true; + } + + $this->craftingType = 0; + + $vector = new Vector3($packet->x, $packet->y, $packet->z); + + $item = $this->inventory->getItemInHand(); + $oldItem = clone $item; + + if($this->canInteract($vector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 6) and $this->level->useBreakOn($vector, $item, $this, true)){ + if($this->isSurvival()){ + if(!$item->equals($oldItem) or $item->getCount() !== $oldItem->getCount()){ + $this->inventory->setItemInHand($item); + $this->inventory->sendHeldItem($this->hasSpawned); + } + + $this->exhaust(0.025, PlayerExhaustEvent::CAUSE_MINING); + } + return true; + } + + $this->inventory->sendContents($this); + $target = $this->level->getBlock($vector); + $tile = $this->level->getTile($vector); + + $this->level->sendBlocks([$this], [$target], UpdateBlockPacket::FLAG_ALL_PRIORITY); + + $this->inventory->sendHeldItem($this); + + if($tile instanceof Spawnable){ + $tile->spawnTo($this); + } + + return true; + } + + public function handleUpdateBlock(UpdateBlockPacket $packet) : bool{ + return false; + } + + public function handleAddPainting(AddPaintingPacket $packet) : bool{ + return false; + } + + public function handleExplode(ExplodePacket $packet) : bool{ + return false; + } + + public function handleLevelSoundEvent(LevelSoundEventPacket $packet) : bool{ + //TODO: add events so plugins can change this + $this->getLevel()->addChunkPacket($this->chunk->getX(), $this->chunk->getZ(), $packet); + return true; + } + + public function handleLevelEvent(LevelEventPacket $packet) : bool{ + return false; + } + + public function handleBlockEvent(BlockEventPacket $packet) : bool{ + return false; + } + + public function handleEntityEvent(EntityEventPacket $packet) : bool{ + if($this->spawned === false or !$this->isAlive()){ + return true; + } + $this->craftingType = 0; + + $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); //TODO: check if this should be true + + switch($packet->event){ + case EntityEventPacket::USE_ITEM: //Eating + $slot = $this->inventory->getItemInHand(); + + if($slot->canBeConsumed()){ + $ev = new PlayerItemConsumeEvent($this, $slot); + if(!$slot->canBeConsumedBy($this)){ + $ev->setCancelled(); + } + $this->server->getPluginManager()->callEvent($ev); + if(!$ev->isCancelled()){ + $slot->onConsume($this); + }else{ + $this->inventory->sendContents($this); + } + } + break; + default: + return false; + } + + return true; + } + + public function handleMobEffect(MobEffectPacket $packet) : bool{ + return false; + } + + public function handleUpdateAttributes(UpdateAttributesPacket $packet) : bool{ + return false; + } + + public function handleMobEquipment(MobEquipmentPacket $packet) : bool{ + if($this->spawned === false or !$this->isAlive()){ + return true; + } + + if($packet->inventorySlot === 255){ + $packet->inventorySlot = -1; //Cleared slot + }else{ + $packet->inventorySlot -= 9; //Get real inventory slot + $item = $this->inventory->getItem($packet->inventorySlot); + + if(!$item->equals($packet->item)){ + $this->server->getLogger()->debug("Tried to equip " . $packet->item . " but have " . $item . " in target slot"); + $this->inventory->sendContents($this); + return false; + } + } + + $this->inventory->equipItem($packet->hotbarSlot, $packet->inventorySlot); + + $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); + + return true; + } + + public function handleMobArmorEquipment(MobArmorEquipmentPacket $packet) : bool{ + return false; + } + + public function handleInteract(InteractPacket $packet) : bool{ + if($this->spawned === false or !$this->isAlive()){ + return true; + } + + $this->craftingType = 0; + + $target = $this->level->getEntity($packet->target); + + $cancelled = false; + switch($packet->action){ + case InteractPacket::ACTION_LEFT_CLICK: //Attack + if($target instanceof Player and $this->server->getConfigBoolean("pvp", true) === false){ + $cancelled = true; + } + + if($target instanceof Entity and $this->getGamemode() !== Player::VIEW and $this->isAlive() and $target->isAlive()){ + if($target instanceof DroppedItem or $target instanceof Arrow){ + $this->kick("Attempting to attack an invalid entity"); + $this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()])); + break; + } + + $item = $this->inventory->getItemInHand(); + $damageTable = [ + Item::WOODEN_SWORD => 4, + Item::GOLD_SWORD => 4, + Item::STONE_SWORD => 5, + Item::IRON_SWORD => 6, + Item::DIAMOND_SWORD => 7, + + Item::WOODEN_AXE => 3, + Item::GOLD_AXE => 3, + Item::STONE_AXE => 3, + Item::IRON_AXE => 5, + Item::DIAMOND_AXE => 6, + + Item::WOODEN_PICKAXE => 2, + Item::GOLD_PICKAXE => 2, + Item::STONE_PICKAXE => 3, + Item::IRON_PICKAXE => 4, + Item::DIAMOND_PICKAXE => 5, + + Item::WOODEN_SHOVEL => 1, + Item::GOLD_SHOVEL => 1, + Item::STONE_SHOVEL => 2, + Item::IRON_SHOVEL => 3, + Item::DIAMOND_SHOVEL => 4, + ]; + + $damage = [ + EntityDamageEvent::MODIFIER_BASE => $damageTable[$item->getId()] ?? 1, + ]; + + if(!$this->canInteract($target, 8)){ + $cancelled = true; + }elseif($target instanceof Player){ + if(($target->getGamemode() & 0x01) > 0){ + break; + }elseif($this->server->getConfigBoolean("pvp") !== true or $this->server->getDifficulty() === 0){ + $cancelled = true; + } + + $armorValues = [ + Item::LEATHER_CAP => 1, + Item::LEATHER_TUNIC => 3, + Item::LEATHER_PANTS => 2, + Item::LEATHER_BOOTS => 1, + Item::CHAIN_HELMET => 1, + Item::CHAIN_CHESTPLATE => 5, + Item::CHAIN_LEGGINGS => 4, + Item::CHAIN_BOOTS => 1, + Item::GOLD_HELMET => 1, + Item::GOLD_CHESTPLATE => 5, + Item::GOLD_LEGGINGS => 3, + Item::GOLD_BOOTS => 1, + Item::IRON_HELMET => 2, + Item::IRON_CHESTPLATE => 6, + Item::IRON_LEGGINGS => 5, + Item::IRON_BOOTS => 2, + Item::DIAMOND_HELMET => 3, + Item::DIAMOND_CHESTPLATE => 8, + Item::DIAMOND_LEGGINGS => 6, + Item::DIAMOND_BOOTS => 3, + ]; + $points = 0; + foreach($target->getInventory()->getArmorContents() as $index => $i){ + if(isset($armorValues[$i->getId()])){ + $points += $armorValues[$i->getId()]; + } + } + + $damage[EntityDamageEvent::MODIFIER_ARMOR] = -floor($damage[EntityDamageEvent::MODIFIER_BASE] * $points * 0.04); + } + + $ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $damage); + if($cancelled){ + $ev->setCancelled(); + } + + $target->attack($ev->getFinalDamage(), $ev); + + if($ev->isCancelled()){ + if($item->isTool() and $this->isSurvival()){ + $this->inventory->sendContents($this); + } + break; + } + + if($this->isSurvival()){ + if($item->isTool()){ + if($item->useOn($target) and $item->getDamage() >= $item->getMaxDurability()){ + $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1)); + }else{ + $this->inventory->setItemInHand($item); + } + } + + $this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK); + } + } + break; + case InteractPacket::ACTION_RIGHT_CLICK: + case InteractPacket::ACTION_LEAVE_VEHICLE: + case InteractPacket::ACTION_MOUSEOVER: + break; //TODO: handle these + default: + $this->server->getLogger()->debug("Unhandled/unknown interaction type " . $packet->action . "received from ". $this->getName()); + return false; + } + + return true; + } + + public function handleBlockPickRequest(BlockPickRequestPacket $packet) : bool{ + $tile = $this->getLevel()->getTile($this->temporalVector->setComponents($packet->tileX, $packet->tileY, $packet->tileZ)); + if($tile instanceof Tile){ //TODO: check if the held item matches the target tile + $nbt = $tile->getCleanedNBT(); + if($nbt instanceof CompoundTag){ + $item = $this->inventory->getItemInHand(); + $item->setCustomBlockData($nbt); + $item->setLore(["+(DATA)"]); + $this->inventory->setItemInHand($item); + } + + return true; + } + return false; + } + + public function handleUseItem(UseItemPacket $packet) : bool{ + if($this->spawned === false or !$this->isAlive()){ + return true; + } + + $blockVector = new Vector3($packet->x, $packet->y, $packet->z); + + $this->craftingType = 0; + + if($packet->face >= 0 and $packet->face <= 5){ //Use Block, place + $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); + + if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13) or $this->isSpectator()){ + }elseif($this->isCreative()){ + $item = $this->inventory->getItemInHand(); + if($this->level->useItemOn($blockVector, $item, $packet->face, $packet->fx, $packet->fy, $packet->fz, $this) === true){ + return true; + } + }elseif(!$this->inventory->getItemInHand()->equals($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) or $item->getCount() !== $oldItem->getCount()){ + $this->inventory->setItemInHand($item); + $this->inventory->sendHeldItem($this->hasSpawned); + } + return true; + } + } + + $this->inventory->sendHeldItem($this); + + if($blockVector->distanceSquared($this) > 10000){ + return true; + } + $target = $this->level->getBlock($blockVector); + $block = $target->getSide($packet->face); + + $this->level->sendBlocks([$this], [$target, $block], UpdateBlockPacket::FLAG_ALL_PRIORITY); + return true; + }elseif($packet->face === -1){ + $aimPos = new Vector3( + -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI), + -sin($this->pitch / 180 * M_PI), + cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI) + ); + + if($this->isCreative()){ + $item = $this->inventory->getItemInHand(); + }elseif(!$this->inventory->getItemInHand()->equals($packet->item)){ + $this->inventory->sendHeldItem($this); + return true; + }else{ + $item = $this->inventory->getItemInHand(); + } + + $ev = new PlayerInteractEvent($this, $item, $aimPos, $packet->face, PlayerInteractEvent::RIGHT_CLICK_AIR); + + $this->server->getPluginManager()->callEvent($ev); + + if($ev->isCancelled()){ + $this->inventory->sendHeldItem($this); + return true; + } + + if($item->getId() === Item::SNOWBALL){ + $nbt = new CompoundTag("", [ + "Pos" => new ListTag("Pos", [ + new DoubleTag("", $this->x), + new DoubleTag("", $this->y + $this->getEyeHeight()), + new DoubleTag("", $this->z) + ]), + "Motion" => new ListTag("Motion", [ + new DoubleTag("", $aimPos->x), + new DoubleTag("", $aimPos->y), + new DoubleTag("", $aimPos->z) + ]), + "Rotation" => new ListTag("Rotation", [ + new FloatTag("", $this->yaw), + new FloatTag("", $this->pitch) + ]), + ]); + + $f = 1.5; + $snowball = Entity::createEntity("Snowball", $this->getLevel(), $nbt, $this); + $snowball->setMotion($snowball->getMotion()->multiply($f)); + if($this->isSurvival()){ + $item->setCount($item->getCount() - 1); + $this->inventory->setItemInHand($item->getCount() > 0 ? $item : Item::get(Item::AIR)); + } + if($snowball instanceof Projectile){ + $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($snowball)); + if($projectileEv->isCancelled()){ + $snowball->kill(); + }else{ + $snowball->spawnToAll(); + $this->level->addSound(new LaunchSound($this), $this->getViewers()); + } + }else{ + $snowball->spawnToAll(); + } + } + + $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, true); + $this->startAction = $this->server->getTick(); + } + + return true; + } + + public function handlePlayerAction(PlayerActionPacket $packet) : bool{ + if($this->spawned === false or (!$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN and $packet->action !== PlayerActionPacket::ACTION_DIMENSION_CHANGE)){ + return true; + } + + $packet->eid = $this->id; + $pos = new Vector3($packet->x, $packet->y, $packet->z); + + switch($packet->action){ + case PlayerActionPacket::ACTION_START_BREAK: + if($this->lastBreak !== PHP_INT_MAX or $pos->distanceSquared($this) > 10000){ + break; + } + $target = $this->level->getBlock($pos); + $ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $target, $packet->face, $target->getId() === 0 ? PlayerInteractEvent::LEFT_CLICK_AIR : PlayerInteractEvent::LEFT_CLICK_BLOCK); + $this->getServer()->getPluginManager()->callEvent($ev); + if($ev->isCancelled()){ + $this->inventory->sendHeldItem($this); + break; + } + $block = $target->getSide($packet->face); + if($block->getId() === Block::FIRE){ + $this->level->setBlock($block, new Air()); + break; + } + $this->lastBreak = microtime(true); + break; + case PlayerActionPacket::ACTION_ABORT_BREAK: + $this->lastBreak = PHP_INT_MAX; + break; + case PlayerActionPacket::ACTION_STOP_BREAK: + 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(); + if($this->isSurvival() and !$this->inventory->contains(Item::get(Item::ARROW, 0, 1))){ + $this->inventory->sendContents($this); + break; + } + + $nbt = new CompoundTag("", [ + "Pos" => new ListTag("Pos", [ + new DoubleTag("", $this->x), + new DoubleTag("", $this->y + $this->getEyeHeight()), + new DoubleTag("", $this->z) + ]), + "Motion" => new ListTag("Motion", [ + new DoubleTag("", -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)), + new DoubleTag("", -sin($this->pitch / 180 * M_PI)), + new DoubleTag("", cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)) + ]), + "Rotation" => new ListTag("Rotation", [ + new FloatTag("", $this->yaw), + new FloatTag("", $this->pitch) + ]), + "Fire" => new ShortTag("Fire", $this->isOnFire() ? 45 * 60 : 0) + ]); + + $diff = ($this->server->getTick() - $this->startAction); + $p = $diff / 20; + $f = min((($p ** 2) + $p * 2) / 3, 1) * 2; + $ev = new EntityShootBowEvent($this, $bow, Entity::createEntity("Arrow", $this->getLevel(), $nbt, $this, $f == 2 ? true : false), $f); + + if($f < 0.1 or $diff < 5){ + $ev->setCancelled(); + } + + $this->server->getPluginManager()->callEvent($ev); + + if($ev->isCancelled()){ + $ev->getProjectile()->kill(); + $this->inventory->sendContents($this); + }else{ + $ev->getProjectile()->setMotion($ev->getProjectile()->getMotion()->multiply($ev->getForce())); + if($this->isSurvival()){ + $this->inventory->removeItem(Item::get(Item::ARROW, 0, 1)); + $bow->setDamage($bow->getDamage() + 1); + if($bow->getDamage() >= 385){ + $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 0)); + }else{ + $this->inventory->setItemInHand($bow); + } + } + if($ev->getProjectile() instanceof Projectile){ + $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($ev->getProjectile())); + if($projectileEv->isCancelled()){ + $ev->getProjectile()->kill(); + }else{ + $ev->getProjectile()->spawnToAll(); + $this->level->addSound(new LaunchSound($this), $this->getViewers()); + } + }else{ + $ev->getProjectile()->spawnToAll(); + } + } + } + }elseif($this->inventory->getItemInHand()->getId() === Item::BUCKET and $this->inventory->getItemInHand()->getDamage() === 1){ //Milk! + $this->server->getPluginManager()->callEvent($ev = new PlayerItemConsumeEvent($this, $this->inventory->getItemInHand())); + if($ev->isCancelled()){ + $this->inventory->sendContents($this); + break; + } + + $pk = new EntityEventPacket(); + $pk->eid = $this->getId(); + $pk->event = EntityEventPacket::USE_ITEM; + $this->dataPacket($pk); + $this->server->broadcastPacket($this->getViewers(), $pk); + + if($this->isSurvival()){ + $slot = $this->inventory->getItemInHand(); + --$slot->count; + $this->inventory->setItemInHand($slot); + $this->inventory->addItem(Item::get(Item::BUCKET, 0, 1)); + } + + $this->removeAllEffects(); + }else{ + $this->inventory->sendContents($this); + } + break; + case PlayerActionPacket::ACTION_STOP_SLEEPING: + $this->stopSleep(); + break; + case PlayerActionPacket::ACTION_RESPAWN: + if($this->spawned === false or $this->isAlive() or !$this->isOnline()){ + break; + } + + if($this->server->isHardcore()){ + $this->setBanned(true); + break; + } + + $this->craftingType = 0; + + $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $this->getSpawn())); + + $this->teleport($ev->getRespawnPosition()); + + $this->setSprinting(false); + $this->setSneaking(false); + + $this->extinguish(); + $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 400, false); + $this->deadTicks = 0; + $this->noDamageTicks = 60; + + $this->removeAllEffects(); + $this->setHealth($this->getMaxHealth()); + + foreach($this->attributeMap->getAll() as $attr){ + $attr->resetToDefault(); + } + + $this->sendData($this); + + $this->sendSettings(); + $this->inventory->sendContents($this); + $this->inventory->sendArmorContents($this); + + $this->spawnToAll(); + $this->scheduleUpdate(); + break; + case PlayerActionPacket::ACTION_JUMP: + $this->jump(); + return true; + 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); + } + return true; + 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); + } + return true; + 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); + } + return true; + 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); + } + return true; + case PlayerActionPacket::ACTION_START_GLIDE: + case PlayerActionPacket::ACTION_STOP_GLIDE: + break; //TODO + default: + $this->server->getLogger()->debug("Unhandled/unknown player action type " . $packet->action . " from " . $this->getName()); + return false; + } + + $this->startAction = -1; + $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); + + return true; + } + + public function handlePlayerFall(PlayerFallPacket $packet) : bool{ + return false; + } + + public function handleHurtArmor(HurtArmorPacket $packet) : bool{ + return false; + } + + public function handleSetEntityData(SetEntityDataPacket $packet) : bool{ + return false; + } + + public function handleSetEntityMotion(SetEntityMotionPacket $packet) : bool{ + return false; + } + + public function handleSetEntityLink(SetEntityLinkPacket $packet) : bool{ + return false; + } + + public function handleSetHealth(SetHealthPacket $packet) : bool{ + return false; + } + + public function handleSetSpawnPosition(SetSpawnPositionPacket $packet) : bool{ + return false; + } + + public function handleAnimate(AnimatePacket $packet) : bool{ + if($this->spawned === false or !$this->isAlive()){ + return true; + } + + $this->server->getPluginManager()->callEvent($ev = new PlayerAnimationEvent($this, $packet->action)); + if($ev->isCancelled()){ + return true; + } + + $pk = new AnimatePacket(); + $pk->eid = $this->getId(); + $pk->action = $ev->getAnimationType(); + $this->server->broadcastPacket($this->getViewers(), $pk); + + return true; + } + + public function handleRespawn(RespawnPacket $packet) : bool{ + return false; + } + + public function handleDropItem(DropItemPacket $packet) : bool{ + if($this->spawned === false or !$this->isAlive()){ + return true; + } + + if($packet->item->getId() === Item::AIR){ + // Windows 10 Edition drops the contents of the crafting grid on container close - including air. + return true; + } + + $item = $this->inventory->getItemInHand(); + $ev = new PlayerDropItemEvent($this, $item); + $this->server->getPluginManager()->callEvent($ev); + if($ev->isCancelled()){ + $this->inventory->sendContents($this); + return true; + } + + $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); + + $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); + + return true; + } + + public function handleInventoryAction(InventoryActionPacket $packet) : bool{ + return false; + } + + public function handleContainerOpen(ContainerOpenPacket $packet) : bool{ + return false; + } + + public function handleContainerClose(ContainerClosePacket $packet) : bool{ + if($this->spawned === false or $packet->windowid === 0){ + return true; + } + + $this->craftingType = 0; + $this->currentTransaction = null; + if(isset($this->windowIndex[$packet->windowid])){ + $this->server->getPluginManager()->callEvent(new InventoryCloseEvent($this->windowIndex[$packet->windowid], $this)); + $this->removeWindow($this->windowIndex[$packet->windowid]); + }else{ + unset($this->windowIndex[$packet->windowid]); + } + + return true; + } + + public function handleContainerSetSlot(ContainerSetSlotPacket $packet) : bool{ + if($this->spawned === false or !$this->isAlive()){ + return true; + } + + if($packet->slot < 0){ + return false; + } + + switch($packet->windowid){ + case ContainerSetContentPacket::SPECIAL_INVENTORY: //Normal inventory change + if($packet->slot >= $this->inventory->getSize()){ + return false; + } + + $transaction = new BaseTransaction($this->inventory, $packet->slot, $this->inventory->getItem($packet->slot), $packet->item); + break; + case ContainerSetContentPacket::SPECIAL_ARMOR: //Armour change + if($packet->slot >= 4){ + return false; + } + + $transaction = new BaseTransaction($this->inventory, $packet->slot + $this->inventory->getSize(), $this->inventory->getArmorItem($packet->slot), $packet->item); + break; + case ContainerSetContentPacket::SPECIAL_HOTBAR: //Hotbar link update + //hotbarSlot 0-8, slot 9-44 + $this->inventory->setHotbarSlotIndex($packet->hotbarSlot, $packet->slot - 9); + return true; + default: + if(!isset($this->windowIndex[$packet->windowid])){ + return false; //unknown windowID and/or not matching any open windows + } + + $this->craftingType = 0; + $inv = $this->windowIndex[$packet->windowid]; + $transaction = new BaseTransaction($inv, $packet->slot, $inv->getItem($packet->slot), $packet->item); + break; + } + + if($transaction->getSourceItem()->equals($transaction->getTargetItem()) and $transaction->getTargetItem()->getCount() === $transaction->getSourceItem()->getCount()){ //No changes! + //No changes, just a local inventory update sent by the client + return true; + } + + if($this->currentTransaction === null or $this->currentTransaction->getCreationTime() < (microtime(true) - 8)){ + if($this->currentTransaction !== null){ + foreach($this->currentTransaction->getInventories() as $inventory){ + if($inventory instanceof PlayerInventory){ + $inventory->sendArmorContents($this); + } + $inventory->sendContents($this); + } + } + $this->currentTransaction = new SimpleTransactionGroup($this); + } + + $this->currentTransaction->addTransaction($transaction); + + if($this->currentTransaction->canExecute()){ + $achievements = []; + foreach($this->currentTransaction->getTransactions() as $ts){ + $inv = $ts->getInventory(); + if($inv instanceof FurnaceInventory){ + if($ts->getSlot() === 2){ + switch($inv->getResult()->getId()){ + case Item::IRON_INGOT: + $achievements[] = "acquireIron"; + break; + } + } + } + } + + if($this->currentTransaction->execute()){ + foreach($achievements as $a){ + $this->awardAchievement($a); + } + } + + $this->currentTransaction = null; + } + + return true; + } + + public function handleContainerSetData(ContainerSetDataPacket $packet) : bool{ + return false; + } + + public function handleContainerSetContent(ContainerSetContentPacket $packet) : bool{ + return false; + } + + public function handleCraftingData(CraftingDataPacket $packet) : bool{ + return false; + } + + public function handleCraftingEvent(CraftingEventPacket $packet) : bool{ + if($this->spawned === false or !$this->isAlive()){ + return true; + } + + $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); + return true; + } + + $canCraft = true; + + if($recipe instanceof ShapedRecipe){ + for($x = 0; $x < 3 and $canCraft; ++$x){ + for($y = 0; $y < 3; ++$y){ + /** @var Item $item */ + $item = $packet->input[$y * 3 + $x]; + $ingredient = $recipe->getIngredient($x, $y); + if($item->getCount() > 0){ + if($ingredient === null or !$ingredient->equals($item, !$ingredient->hasAnyDamageValue(), $ingredient->hasCompoundTag())){ + $canCraft = false; + break; + } + } + } + } + }elseif($recipe instanceof ShapelessRecipe){ + $needed = $recipe->getIngredientList(); + + for($x = 0; $x < 3 and $canCraft; ++$x){ + for($y = 0; $y < 3; ++$y){ + /** @var Item $item */ + $item = clone $packet->input[$y * 3 + $x]; + + foreach($needed as $k => $n){ + if($n->equals($item, !$n->hasAnyDamageValue(), $n->hasCompoundTag())){ + $remove = min($n->getCount(), $item->getCount()); + $n->setCount($n->getCount() - $remove); + $item->setCount($item->getCount() - $remove); + + if($n->getCount() === 0){ + unset($needed[$k]); + } + } + } + + if($item->getCount() > 0){ + $canCraft = false; + break; + } + } + } + + if(count($needed) > 0){ + $canCraft = false; + } + }else{ + $canCraft = false; + } + + /** @var Item[] $ingredients */ + $ingredients = $packet->input; + $result = $packet->output[0]; + + if(!$canCraft or !$recipe->getResult()->equals($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); + return true; + } + + $used = array_fill(0, $this->inventory->getSize(), 0); + + foreach($ingredients as $ingredient){ + $slot = -1; + foreach($this->inventory->getContents() as $index => $item){ + if($ingredient->getId() !== 0 and $ingredient->equals($item, !$ingredient->hasAnyDamageValue(), $ingredient->hasCompoundTag()) and ($item->getCount() - $used[$index]) >= 1){ + $slot = $index; + $used[$index]++; + break; + } + } + + if($ingredient->getId() !== 0 and $slot === -1){ + $canCraft = false; + break; + } + } + + 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); + return true; + } + + $this->server->getPluginManager()->callEvent($ev = new CraftItemEvent($this, $ingredients, $recipe)); + + if($ev->isCancelled()){ + $this->inventory->sendContents($this); + return true; + } + + 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; + } + + + return true; + } + + public function handleAdventureSettings(AdventureSettingsPacket $packet) : bool{ + if($packet->isFlying and !$this->allowFlight and !$this->server->getAllowFlight()){ + $this->kick($this->server->getLanguage()->translateString("kick.reason.cheat", ["%ability.flight"])); + return true; + }elseif($packet->isFlying !== $this->isFlying()){ + $this->server->getPluginManager()->callEvent($ev = new PlayerToggleFlightEvent($this, $packet->isFlying)); + if($ev->isCancelled()){ + $this->sendSettings(); + }else{ + $this->flying = $ev->isFlying(); + } + } + + if($packet->noClip and !$this->allowMovementCheats and !$this->isSpectator()){ + $this->kick($this->server->getLanguage()->translateString("kick.reason.cheat", ["%ability.noclip"])); + return true; + } + + //TODO: check other changes + + return true; + } + + public function handleBlockEntityData(BlockEntityDataPacket $packet) : bool{ + if($this->spawned === false or !$this->isAlive()){ + return true; + } + $this->craftingType = 0; + + $pos = new Vector3($packet->x, $packet->y, $packet->z); + if($pos->distanceSquared($this) > 10000){ + return true; + } + + $t = $this->level->getTile($pos); + if($t instanceof Spawnable){ + $nbt = new NBT(NBT::LITTLE_ENDIAN); + $nbt->read($packet->namedtag, false, true); + $nbt = $nbt->getData(); + if(!$t->updateCompoundTag($nbt, $this)){ + $t->spawnTo($this); + } + } + + return true; + } + + public function handlePlayerInput(PlayerInputPacket $packet) : bool{ + return false; //TODO + } + + public function handleFullChunkData(FullChunkDataPacket $packet) : bool{ + return false; + } + + public function handleSetCommandsEnabled(SetCommandsEnabledPacket $packet) : bool{ + return false; + } + + public function handleSetDifficulty(SetDifficultyPacket $packet) : bool{ + return false; + } + + public function handleChangeDimension(ChangeDimensionPacket $packet) : bool{ + return false; + } + + public function handleSetPlayerGameType(SetPlayerGameTypePacket $packet) : bool{ + if($packet->gamemode !== ($this->gamemode & 0x01)){ + //GUI gamemode change, set it back to original for now (only possible through client bug or hack with current allowed client permissions) + $pk = new SetPlayerGameTypePacket(); + $pk->gamemode = $this->gamemode & 0x01; + $this->dataPacket($pk); + $this->sendSettings(); + } + return true; + } + + public function handlePlayerList(PlayerListPacket $packet) : bool{ + return false; + } + + public function handleSpawnExperienceOrb(SpawnExperienceOrbPacket $packet) : bool{ + return false; //TODO + } + + public function handleClientboundMapItemData(ClientboundMapItemDataPacket $packet) : bool{ + return false; + } + + public function handleMapInfoRequest(MapInfoRequestPacket $packet) : bool{ + return false; //TODO + } + + public function handleRequestChunkRadius(RequestChunkRadiusPacket $packet) : bool{ + $this->setViewDistance($packet->radius); + + return true; + } + + public function handleChunkRadiusUpdated(ChunkRadiusUpdatedPacket $packet) : bool{ + return false; + } + + public function handleItemFrameDropItem(ItemFrameDropItemPacket $packet) : bool{ + if($this->spawned === false or !$this->isAlive()){ + return true; + } + + $tile = $this->level->getTile($this->temporalVector->setComponents($packet->x, $packet->y, $packet->z)); + if($tile instanceof ItemFrame){ + if($this->isSpectator()){ + $tile->spawnTo($this); + return true; + } + + if(lcg_value() <= $tile->getItemDropChance()){ + $this->level->dropItem($tile->getBlock(), $tile->getItem()); + } + $tile->setItem(null); + $tile->setItemRotation(0); + } + + return true; + } + + public function handleReplaceItemInSlot(ReplaceItemInSlotPacket $packet) : bool{ + return false; + } + + public function handleAddItem(AddItemPacket $packet) : bool{ + return false; + } + + public function handleShowCredits(ShowCreditsPacket $packet) : bool{ + return false; //TODO: handle resume + } + + public function handleAvailableCommands(AvailableCommandsPacket $packet) : bool{ + return false; + } + + public function handleCommandStep(CommandStepPacket $packet) : bool{ + if($this->spawned === false or !$this->isAlive()){ + return true; + } + $this->craftingType = 0; + $commandText = $packet->command; + if($packet->args !== null){ + foreach($packet->args as $arg){ //command ordering will be an issue + $commandText .= " " . $arg; + } + } + $this->server->getPluginManager()->callEvent($ev = new PlayerCommandPreprocessEvent($this, "/" . $commandText)); + if($ev->isCancelled()){ + return true; + } + + Timings::$playerCommandTimer->startTiming(); + $this->server->dispatchCommand($ev->getPlayer(), substr($ev->getMessage(), 1)); + Timings::$playerCommandTimer->stopTiming(); + + return true; + } + + public function handleCommandBlockUpdate(CommandBlockUpdatePacket $packet) : bool{ + return false; //TODO + } + + public function handleUpdateTrade(UpdateTradePacket $packet) : bool{ + return false; + } + + public function handleResourcePackDataInfo(ResourcePackDataInfoPacket $packet) : bool{ + return false; + } + + public function handleResourcePackChunkData(ResourcePackChunkDataPacket $packet) : bool{ + return false; + } + + public function handleResourcePackChunkRequest(ResourcePackChunkRequestPacket $packet) : bool{ + $manager = $this->server->getResourceManager(); + $pack = $manager->getPackById($packet->packId); + if(!($pack instanceof ResourcePack)){ + $this->close("", "disconnectionScreen.resourcePack", true); + $this->server->getLogger()->debug("Got a resource pack chunk request for unknown pack with UUID " . $packet->packId . ", available packs: " . implode(", ", $manager->getPackIdList())); + + return false; + } + + $pk = new ResourcePackChunkDataPacket(); + $pk->packId = $pack->getPackId(); + $pk->chunkIndex = $packet->chunkIndex; + $pk->data = $pack->getPackChunk(1048576 * $packet->chunkIndex, 1048576); + $pk->progress = (1048576 * $packet->chunkIndex); + $this->dataPacket($pk); + return true; + } + + public function handleTransfer(TransferPacket $packet) : bool{ + return false; + } + + public function handlePlaySound(PlaySoundPacket $packet) : bool{ + return false; + } + + public function handleStopSound(StopSoundPacket $packet) : bool{ + return false; + } + + public function handleSetTitle(SetTitlePacket $packet) : bool{ + return false; + } + + public function handleUnknown(UnknownPacket $packet) : bool{ + $this->server->getLogger()->debug("Received unknown packet from " . $this->getName() . ": 0x" . bin2hex($packet->payload)); + return true; + } + /** - * Handles a Minecraft packet - * TODO: Separate all of this in handlers - * - * WARNING: Do not use this, it's only for internal use. - * Changes to this function won't be recorded on the version. + * Called when a packet is received from the client. This method will call DataPacketReceiveEvent. * * @param DataPacket $packet */ @@ -1832,1103 +3361,15 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade return; } - if($packet::NETWORK_ID === ProtocolInfo::BATCH_PACKET){ - /** @var BatchPacket $packet */ - $this->server->getNetwork()->processBatch($packet, $this); - return; - } - $timings = Timings::getReceiveDataPacketTimings($packet); - $timings->startTiming(); + $packet->decode(); + assert($packet->feof(), "Still " . strlen(substr($packet->buffer, $packet->offset)) . " bytes unread in " . get_class($packet)); + $this->server->getPluginManager()->callEvent($ev = new DataPacketReceiveEvent($this, $packet)); - if($ev->isCancelled()){ - $timings->stopTiming(); - return; - } - - switch($packet::NETWORK_ID){ - case ProtocolInfo::LOGIN_PACKET: - if($this->loggedIn){ - break; - } - - $this->username = TextFormat::clean($packet->username); - $this->displayName = $this->username; - $this->iusername = strtolower($this->username); - $this->setDataProperty(self::DATA_NAMETAG, self::DATA_TYPE_STRING, $this->username, false); - - if(count($this->server->getOnlinePlayers()) >= $this->server->getMaxPlayers() and $this->kick("disconnectionScreen.serverFull", false)){ - break; - } - - if($packet->protocol !== ProtocolInfo::CURRENT_PROTOCOL){ - if($packet->protocol < ProtocolInfo::CURRENT_PROTOCOL){ - $message = "disconnectionScreen.outdatedClient"; - - $pk = new PlayStatusPacket(); - $pk->status = PlayStatusPacket::LOGIN_FAILED_CLIENT; - $this->directDataPacket($pk); - }else{ - $message = "disconnectionScreen.outdatedServer"; - - $pk = new PlayStatusPacket(); - $pk->status = PlayStatusPacket::LOGIN_FAILED_SERVER; - $this->directDataPacket($pk); - } - $this->close("", $message, false); - - break; - } - - $this->randomClientId = $packet->clientId; - - $this->uuid = UUID::fromString($packet->clientUUID); - $this->rawUUID = $this->uuid->toBinary(); - - $valid = true; - $len = strlen($packet->username); - if($len > 16 or $len < 3){ - $valid = false; - } - for($i = 0; $i < $len and $valid; ++$i){ - $c = ord($packet->username{$i}); - if(($c >= ord("a") and $c <= ord("z")) or - ($c >= ord("A") and $c <= ord("Z")) or - ($c >= ord("0") and $c <= ord("9")) or $c === ord("_") - ){ - continue; - } - - $valid = false; - break; - } - - if(!$valid or $this->iusername === "rcon" or $this->iusername === "console"){ - $this->close("", "disconnectionScreen.invalidName"); - - 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->skinId); - - $this->server->getPluginManager()->callEvent($ev = new PlayerPreLoginEvent($this, "Plugin reason")); - if($ev->isCancelled()){ - $this->close("", $ev->getKickMessage()); - - break; - } - - $this->onPlayerPreLogin(); - - break; - case ProtocolInfo::MOVE_PLAYER_PACKET: - $newPos = new Vector3($packet->x, $packet->y - $this->getEyeHeight(), $packet->z); - - if($newPos->distanceSquared($this) == 0 and ($packet->yaw % 360) === $this->yaw and ($packet->pitch % 360) === $this->pitch){ //player hasn't moved, just client spamming packets - break; - } - - $revert = false; - if(!$this->isAlive() or $this->spawned !== true){ - $revert = true; - $this->forceMovement = new Vector3($this->x, $this->y, $this->z); - } - - if($this->teleportPosition !== null or ($this->forceMovement instanceof Vector3 and ($newPos->distanceSquared($this->forceMovement) > 0.1 or $revert))){ - $this->sendPosition($this->forceMovement, $packet->yaw, $packet->pitch, MovePlayerPacket::MODE_RESET); - }else{ - $packet->yaw %= 360; - $packet->pitch %= 360; - - if($packet->yaw < 0){ - $packet->yaw += 360; - } - - $this->setRotation($packet->yaw, $packet->pitch); - $this->newPosition = $newPos; - $this->forceMovement = null; - } - - break; - case ProtocolInfo::ADVENTURE_SETTINGS_PACKET: - //TODO: player abilities, check for other changes - if($packet->isFlying and !$this->allowFlight and !$this->server->getAllowFlight()){ - $this->kick("Flying is not enabled on this server"); - break; - }else{ - $this->server->getPluginManager()->callEvent($ev = new PlayerToggleFlightEvent($this, $packet->isFlying)); - if($ev->isCancelled()){ - $this->sendSettings(); - }else{ - $this->flying = $ev->isFlying(); - } - break; - } - break; - case ProtocolInfo::MOB_EQUIPMENT_PACKET: - if($this->spawned === false or !$this->isAlive()){ - break; - } - - if($packet->inventorySlot === 255){ - $packet->inventorySlot = -1; //Cleared slot - }else{ - $packet->inventorySlot -= 9; //Get real inventory slot - $item = $this->inventory->getItem($packet->inventorySlot); - - if(!$item->equals($packet->item)){ - $this->inventory->sendContents($this); - break; - } - } - - $this->inventory->equipItem($packet->hotbarSlot, $packet->inventorySlot); - - $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); - break; - case ProtocolInfo::USE_ITEM_PACKET: - if($this->spawned === false or !$this->isAlive()){ - break; - } - - $blockVector = new Vector3($packet->x, $packet->y, $packet->z); - - $this->craftingType = 0; - - if($packet->face >= 0 and $packet->face <= 5){ //Use Block, place - $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); - - if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13) or $this->isSpectator()){ - }elseif($this->isCreative()){ - $item = $this->inventory->getItemInHand(); - if($this->level->useItemOn($blockVector, $item, $packet->face, $packet->fx, $packet->fy, $packet->fz, $this) === true){ - break; - } - }elseif(!$this->inventory->getItemInHand()->equals($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) or $item->getCount() !== $oldItem->getCount()){ - $this->inventory->setItemInHand($item); - $this->inventory->sendHeldItem($this->hasSpawned); - } - break; - } - } - - $this->inventory->sendHeldItem($this); - - if($blockVector->distanceSquared($this) > 10000){ - break; - } - $target = $this->level->getBlock($blockVector); - $block = $target->getSide($packet->face); - - $this->level->sendBlocks([$this], [$target, $block], UpdateBlockPacket::FLAG_ALL_PRIORITY); - break; - }elseif($packet->face === -1){ - $aimPos = new Vector3( - -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI), - -sin($this->pitch / 180 * M_PI), - cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI) - ); - - if($this->isCreative()){ - $item = $this->inventory->getItemInHand(); - }elseif(!$this->inventory->getItemInHand()->equals($packet->item)){ - $this->inventory->sendHeldItem($this); - break; - }else{ - $item = $this->inventory->getItemInHand(); - } - - $ev = new PlayerInteractEvent($this, $item, $aimPos, $packet->face, PlayerInteractEvent::RIGHT_CLICK_AIR); - - $this->server->getPluginManager()->callEvent($ev); - - if($ev->isCancelled()){ - $this->inventory->sendHeldItem($this); - break; - } - - if($item->getId() === Item::SNOWBALL){ - $nbt = new CompoundTag("", [ - "Pos" => new ListTag("Pos", [ - new DoubleTag("", $this->x), - new DoubleTag("", $this->y + $this->getEyeHeight()), - new DoubleTag("", $this->z) - ]), - "Motion" => new ListTag("Motion", [ - new DoubleTag("", $aimPos->x), - new DoubleTag("", $aimPos->y), - new DoubleTag("", $aimPos->z) - ]), - "Rotation" => new ListTag("Rotation", [ - new FloatTag("", $this->yaw), - new FloatTag("", $this->pitch) - ]), - ]); - - $f = 1.5; - $snowball = Entity::createEntity("Snowball", $this->getLevel(), $nbt, $this); - $snowball->setMotion($snowball->getMotion()->multiply($f)); - if($this->isSurvival()){ - $item->setCount($item->getCount() - 1); - $this->inventory->setItemInHand($item->getCount() > 0 ? $item : Item::get(Item::AIR)); - } - if($snowball instanceof Projectile){ - $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($snowball)); - if($projectileEv->isCancelled()){ - $snowball->kill(); - }else{ - $snowball->spawnToAll(); - $this->level->addSound(new LaunchSound($this), $this->getViewers()); - } - }else{ - $snowball->spawnToAll(); - } - } - - $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, true); - $this->startAction = $this->server->getTick(); - } - break; - case ProtocolInfo::PLAYER_ACTION_PACKET: - if($this->spawned === false or (!$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN and $packet->action !== PlayerActionPacket::ACTION_DIMENSION_CHANGE)){ - break; - } - - $pos = new Vector3($packet->x, $packet->y, $packet->z); - - switch($packet->action){ - case PlayerActionPacket::ACTION_START_BREAK: - if($this->lastBreak !== PHP_INT_MAX or $pos->distanceSquared($this) > 10000){ - break; - } - $target = $this->level->getBlock($pos); - $ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $target, $packet->face, $target->getId() === 0 ? PlayerInteractEvent::LEFT_CLICK_AIR : PlayerInteractEvent::LEFT_CLICK_BLOCK); - $this->getServer()->getPluginManager()->callEvent($ev); - if($ev->isCancelled()){ - $this->inventory->sendHeldItem($this); - break; - } - $block = $target->getSide($packet->face); - if($block->getId() === Block::FIRE){ - $this->level->setBlock($block, new Air()); - break; - } - $this->lastBreak = microtime(true); - break; - case PlayerActionPacket::ACTION_ABORT_BREAK: - $this->lastBreak = PHP_INT_MAX; - break; - case PlayerActionPacket::ACTION_STOP_BREAK: - 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(); - if($this->isSurvival() and !$this->inventory->contains(Item::get(Item::ARROW, 0, 1))){ - $this->inventory->sendContents($this); - break; - } - - $nbt = new CompoundTag("", [ - "Pos" => new ListTag("Pos", [ - new DoubleTag("", $this->x), - new DoubleTag("", $this->y + $this->getEyeHeight()), - new DoubleTag("", $this->z) - ]), - "Motion" => new ListTag("Motion", [ - new DoubleTag("", -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)), - new DoubleTag("", -sin($this->pitch / 180 * M_PI)), - new DoubleTag("", cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)) - ]), - "Rotation" => new ListTag("Rotation", [ - new FloatTag("", $this->yaw), - new FloatTag("", $this->pitch) - ]), - "Fire" => new ShortTag("Fire", $this->isOnFire() ? 45 * 60 : 0) - ]); - - $diff = ($this->server->getTick() - $this->startAction); - $p = $diff / 20; - $f = min((($p ** 2) + $p * 2) / 3, 1) * 2; - $ev = new EntityShootBowEvent($this, $bow, Entity::createEntity("Arrow", $this->getLevel(), $nbt, $this, $f == 2 ? true : false), $f); - - if($f < 0.1 or $diff < 5){ - $ev->setCancelled(); - } - - $this->server->getPluginManager()->callEvent($ev); - - if($ev->isCancelled()){ - $ev->getProjectile()->kill(); - $this->inventory->sendContents($this); - }else{ - $ev->getProjectile()->setMotion($ev->getProjectile()->getMotion()->multiply($ev->getForce())); - if($this->isSurvival()){ - $this->inventory->removeItem(Item::get(Item::ARROW, 0, 1)); - $bow->setDamage($bow->getDamage() + 1); - if($bow->getDamage() >= 385){ - $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 0)); - }else{ - $this->inventory->setItemInHand($bow); - } - } - if($ev->getProjectile() instanceof Projectile){ - $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($ev->getProjectile())); - if($projectileEv->isCancelled()){ - $ev->getProjectile()->kill(); - }else{ - $ev->getProjectile()->spawnToAll(); - $this->level->addSound(new LaunchSound($this), $this->getViewers()); - } - }else{ - $ev->getProjectile()->spawnToAll(); - } - } - } - }elseif($this->inventory->getItemInHand()->getId() === Item::BUCKET and $this->inventory->getItemInHand()->getDamage() === 1){ //Milk! - $this->server->getPluginManager()->callEvent($ev = new PlayerItemConsumeEvent($this, $this->inventory->getItemInHand())); - if($ev->isCancelled()){ - $this->inventory->sendContents($this); - break; - } - - $pk = new EntityEventPacket(); - $pk->eid = $this->getId(); - $pk->event = EntityEventPacket::USE_ITEM; - $this->dataPacket($pk); - $this->server->broadcastPacket($this->getViewers(), $pk); - - if($this->isSurvival()){ - $slot = $this->inventory->getItemInHand(); - --$slot->count; - $this->inventory->setItemInHand($slot); - $this->inventory->addItem(Item::get(Item::BUCKET, 0, 1)); - } - - $this->removeAllEffects(); - }else{ - $this->inventory->sendContents($this); - } - break; - case PlayerActionPacket::ACTION_STOP_SLEEPING: - $this->stopSleep(); - break; - case PlayerActionPacket::ACTION_RESPAWN: - if($this->spawned === false or $this->isAlive() or !$this->isOnline()){ - break; - } - - if($this->server->isHardcore()){ - $this->setBanned(true); - break; - } - - $this->craftingType = 0; - - $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $this->getSpawn())); - - $this->teleport($ev->getRespawnPosition()); - - $this->setSprinting(false); - $this->setSneaking(false); - - $this->extinguish(); - $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 400, false); - $this->deadTicks = 0; - $this->noDamageTicks = 60; - - $this->setHealth($this->getMaxHealth()); - - $this->removeAllEffects(); - $this->sendData($this); - - $this->sendSettings(); - $this->inventory->sendContents($this); - $this->inventory->sendArmorContents($this); - - $this->spawnToAll(); - $this->scheduleUpdate(); - break; - case PlayerActionPacket::ACTION_JUMP: - break 2; - 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 2; - 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 2; - default: - assert(false, "Unhandled player action " . $packet->action . " from " . $this->getName()); - } - - $this->startAction = -1; - $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); - break; - - case ProtocolInfo::REMOVE_BLOCK_PACKET: - if($this->spawned === false or !$this->isAlive()){ - break; - } - $this->craftingType = 0; - - $vector = new Vector3($packet->x, $packet->y, $packet->z); - - if($this->isCreative()){ - $item = $this->inventory->getItemInHand(); - }else{ - $item = $this->inventory->getItemInHand(); - } - - $oldItem = clone $item; - - if($this->canInteract($vector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 6) and $this->level->useBreakOn($vector, $item, $this, true)){ - if($this->isSurvival()){ - if(!$item->equals($oldItem) or $item->getCount() !== $oldItem->getCount()){ - $this->inventory->setItemInHand($item); - $this->inventory->sendHeldItem($this->hasSpawned); - } - - $this->exhaust(0.025, PlayerExhaustEvent::CAUSE_MINING); - } - break; - } - - $this->inventory->sendContents($this); - $target = $this->level->getBlock($vector); - $tile = $this->level->getTile($vector); - - $this->level->sendBlocks([$this], [$target], UpdateBlockPacket::FLAG_ALL_PRIORITY); - - $this->inventory->sendHeldItem($this); - - if($tile instanceof Spawnable){ - $tile->spawnTo($this); - } - break; - - case ProtocolInfo::MOB_ARMOR_EQUIPMENT_PACKET: - break; - - case ProtocolInfo::INTERACT_PACKET: - if($this->spawned === false or !$this->isAlive()){ - break; - } - - $this->craftingType = 0; - - $target = $this->level->getEntity($packet->target); - - $cancelled = false; - switch($packet->action){ - case InteractPacket::ACTION_LEFT_CLICK: //Attack - if($target instanceof Player and $this->server->getConfigBoolean("pvp", true) === false){ - $cancelled = true; - } - - if($target instanceof Entity and $this->getGamemode() !== Player::VIEW and $this->isAlive() and $target->isAlive()){ - if($target instanceof DroppedItem or $target instanceof Arrow){ - $this->kick("Attempting to attack an invalid entity"); - $this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()])); - break; - } - - $item = $this->inventory->getItemInHand(); - $damageTable = [ - Item::WOODEN_SWORD => 4, - Item::GOLD_SWORD => 4, - Item::STONE_SWORD => 5, - Item::IRON_SWORD => 6, - Item::DIAMOND_SWORD => 7, - - Item::WOODEN_AXE => 3, - Item::GOLD_AXE => 3, - Item::STONE_AXE => 3, - Item::IRON_AXE => 5, - Item::DIAMOND_AXE => 6, - - Item::WOODEN_PICKAXE => 2, - Item::GOLD_PICKAXE => 2, - Item::STONE_PICKAXE => 3, - Item::IRON_PICKAXE => 4, - Item::DIAMOND_PICKAXE => 5, - - Item::WOODEN_SHOVEL => 1, - Item::GOLD_SHOVEL => 1, - Item::STONE_SHOVEL => 2, - Item::IRON_SHOVEL => 3, - Item::DIAMOND_SHOVEL => 4, - ]; - - $damage = [ - EntityDamageEvent::MODIFIER_BASE => $damageTable[$item->getId()] ?? 1, - ]; - - if(!$this->canInteract($target, 8)){ - $cancelled = true; - }elseif($target instanceof Player){ - if(($target->getGamemode() & 0x01) > 0){ - break; - }elseif($this->server->getConfigBoolean("pvp") !== true or $this->server->getDifficulty() === 0){ - $cancelled = true; - } - - $armorValues = [ - Item::LEATHER_CAP => 1, - Item::LEATHER_TUNIC => 3, - Item::LEATHER_PANTS => 2, - Item::LEATHER_BOOTS => 1, - Item::CHAIN_HELMET => 1, - Item::CHAIN_CHESTPLATE => 5, - Item::CHAIN_LEGGINGS => 4, - Item::CHAIN_BOOTS => 1, - Item::GOLD_HELMET => 1, - Item::GOLD_CHESTPLATE => 5, - Item::GOLD_LEGGINGS => 3, - Item::GOLD_BOOTS => 1, - Item::IRON_HELMET => 2, - Item::IRON_CHESTPLATE => 6, - Item::IRON_LEGGINGS => 5, - Item::IRON_BOOTS => 2, - Item::DIAMOND_HELMET => 3, - Item::DIAMOND_CHESTPLATE => 8, - Item::DIAMOND_LEGGINGS => 6, - Item::DIAMOND_BOOTS => 3, - ]; - $points = 0; - foreach($target->getInventory()->getArmorContents() as $index => $i){ - if(isset($armorValues[$i->getId()])){ - $points += $armorValues[$i->getId()]; - } - } - - $damage[EntityDamageEvent::MODIFIER_ARMOR] = -floor($damage[EntityDamageEvent::MODIFIER_BASE] * $points * 0.04); - } - - $ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $damage); - if($cancelled){ - $ev->setCancelled(); - } - - $target->attack($ev->getFinalDamage(), $ev); - - if($ev->isCancelled()){ - if($item->isTool() and $this->isSurvival()){ - $this->inventory->sendContents($this); - } - break; - } - - if($this->isSurvival()){ - if($item->isTool()){ - if($item->useOn($target) and $item->getDamage() >= $item->getMaxDurability()){ - $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1)); - }else{ - $this->inventory->setItemInHand($item); - } - } - - $this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK); - } - } - break; - default: - break; //TODO: handle other actions - } - - break; - case ProtocolInfo::ANIMATE_PACKET: - if($this->spawned === false or !$this->isAlive()){ - break; - } - - $this->server->getPluginManager()->callEvent($ev = new PlayerAnimationEvent($this, $packet->action)); - if($ev->isCancelled()){ - break; - } - - $pk = new AnimatePacket(); - $pk->eid = $this->getId(); - $pk->action = $ev->getAnimationType(); - $this->server->broadcastPacket($this->getViewers(), $pk); - break; - case ProtocolInfo::SET_HEALTH_PACKET: //Not used - break; - case ProtocolInfo::ENTITY_EVENT_PACKET: - if($this->spawned === false or !$this->isAlive()){ - break; - } - $this->craftingType = 0; - - $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); //TODO: check if this should be true - - switch($packet->event){ - case EntityEventPacket::USE_ITEM: //Eating - $slot = $this->inventory->getItemInHand(); - - if($slot->canBeConsumed()){ - $ev = new PlayerItemConsumeEvent($this, $slot); - if(!$slot->canBeConsumedBy($this)){ - $ev->setCancelled(); - } - $this->server->getPluginManager()->callEvent($ev); - if(!$ev->isCancelled()){ - $slot->onConsume($this); - }else{ - $this->inventory->sendContents($this); - } - } - break; - } - break; - case ProtocolInfo::DROP_ITEM_PACKET: - if($this->spawned === false or !$this->isAlive()){ - break; - } - - if($packet->item->getId() === Item::AIR){ - // Windows 10 Edition drops the contents of the crafting grid on container close - including air. - break; - } - - $item = $this->inventory->getItemInHand(); - $ev = new PlayerDropItemEvent($this, $item); - $this->server->getPluginManager()->callEvent($ev); - if($ev->isCancelled()){ - $this->inventory->sendContents($this); - break; - } - - $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); - - $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); - break; - case ProtocolInfo::COMMAND_STEP_PACKET: - if($this->spawned === false or !$this->isAlive()){ - break; - } - $this->craftingType = 0; - $commandText = $packet->command; - if($packet->args !== null){ - foreach($packet->args as $arg){ //command ordering will be an issue - $commandText .= " " . $arg; - } - } - $this->server->getPluginManager()->callEvent($ev = new PlayerCommandPreprocessEvent($this, "/" . $commandText)); - if($ev->isCancelled()){ - break; - } - - Timings::$playerCommandTimer->startTiming(); - $this->server->dispatchCommand($ev->getPlayer(), substr($ev->getMessage(), 1)); - Timings::$playerCommandTimer->stopTiming(); - break; - case ProtocolInfo::TEXT_PACKET: - if($this->spawned === false or !$this->isAlive()){ - break; - } - $this->craftingType = 0; - if($packet->type === TextPacket::TYPE_CHAT){ - $packet->message = TextFormat::clean($packet->message, $this->removeFormat); - foreach(explode("\n", $packet->message) as $message){ - if(trim($message) != "" and strlen($message) <= 255 and $this->messageCounter-- > 0){ - if(substr($message, 0, 2) === "./"){ //Command (./ = fast hack for old plugins post 0.16) - $message = substr($message, 1); - } - - $ev = new PlayerCommandPreprocessEvent($this, $message); - - if(mb_strlen($ev->getMessage(), "UTF-8") > 320){ - $ev->setCancelled(); - } - $this->server->getPluginManager()->callEvent($ev); - - if($ev->isCancelled()){ - break; - } - - if(substr($ev->getMessage(), 0, 1) === "/"){ - Timings::$playerCommandTimer->startTiming(); - $this->server->dispatchCommand($ev->getPlayer(), substr($ev->getMessage(), 1)); - Timings::$playerCommandTimer->stopTiming(); - }else{ - $this->server->getPluginManager()->callEvent($ev = new PlayerChatEvent($this, $ev->getMessage())); - if(!$ev->isCancelled()){ - $this->server->broadcastMessage($this->getServer()->getLanguage()->translateString($ev->getFormat(), [$ev->getPlayer()->getDisplayName(), $ev->getMessage()]), $ev->getRecipients()); - } - } - } - } - } - break; - case ProtocolInfo::CONTAINER_CLOSE_PACKET: - if($this->spawned === false or $packet->windowid === 0){ - break; - } - $this->craftingType = 0; - $this->currentTransaction = null; - if(isset($this->windowIndex[$packet->windowid])){ - $this->server->getPluginManager()->callEvent(new InventoryCloseEvent($this->windowIndex[$packet->windowid], $this)); - $this->removeWindow($this->windowIndex[$packet->windowid]); - }else{ - unset($this->windowIndex[$packet->windowid]); - } - 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; - } - - $canCraft = true; - - if($recipe instanceof ShapedRecipe){ - for($x = 0; $x < 3 and $canCraft; ++$x){ - for($y = 0; $y < 3; ++$y){ - /** @var Item $item */ - $item = $packet->input[$y * 3 + $x]; - $ingredient = $recipe->getIngredient($x, $y); - if($item->getCount() > 0){ - if($ingredient === null or !$ingredient->equals($item, !$ingredient->hasAnyDamageValue(), $ingredient->hasCompoundTag())){ - $canCraft = false; - break; - } - } - } - } - }elseif($recipe instanceof ShapelessRecipe){ - $needed = $recipe->getIngredientList(); - - for($x = 0; $x < 3 and $canCraft; ++$x){ - for($y = 0; $y < 3; ++$y){ - /** @var Item $item */ - $item = clone $packet->input[$y * 3 + $x]; - - foreach($needed as $k => $n){ - if($n->equals($item, !$n->hasAnyDamageValue(), $n->hasCompoundTag())){ - $remove = min($n->getCount(), $item->getCount()); - $n->setCount($n->getCount() - $remove); - $item->setCount($item->getCount() - $remove); - - if($n->getCount() === 0){ - unset($needed[$k]); - } - } - } - - if($item->getCount() > 0){ - $canCraft = false; - break; - } - } - } - - if(count($needed) > 0){ - $canCraft = false; - } - }else{ - $canCraft = false; - } - - /** @var Item[] $ingredients */ - $ingredients = $packet->input; - $result = $packet->output[0]; - - if(!$canCraft or !$recipe->getResult()->equals($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 => $item){ - if($ingredient->getId() !== 0 and $ingredient->equals($item, !$ingredient->hasAnyDamageValue(), $ingredient->hasCompoundTag()) and ($item->getCount() - $used[$index]) >= 1){ - $slot = $index; - $used[$index]++; - break; - } - } - - if($ingredient->getId() !== 0 and $slot === -1){ - $canCraft = false; - break; - } - } - - 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; - } - - $this->server->getPluginManager()->callEvent($ev = new CraftItemEvent($this, $ingredients, $recipe)); - - if($ev->isCancelled()){ - $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: - if($this->spawned === false or !$this->isAlive()){ - break; - } - - if($packet->slot < 0){ - break; - } - - switch($packet->windowid){ - case ContainerSetContentPacket::SPECIAL_INVENTORY: //Normal inventory change - if($packet->slot >= $this->inventory->getSize()){ - break 2; - } - - $transaction = new BaseTransaction($this->inventory, $packet->slot, $this->inventory->getItem($packet->slot), $packet->item); - break; - case ContainerSetContentPacket::SPECIAL_ARMOR: //Armour change - if($packet->slot >= 4){ - break 2; - } - - $transaction = new BaseTransaction($this->inventory, $packet->slot + $this->inventory->getSize(), $this->inventory->getArmorItem($packet->slot), $packet->item); - break; - case ContainerSetContentPacket::SPECIAL_HOTBAR: //Hotbar link update - //hotbarSlot 0-8, slot 9-44 - $this->inventory->setHotbarSlotIndex($packet->hotbarSlot, $packet->slot - 9); - break 2; - default: - if(!isset($this->windowIndex[$packet->windowid])){ - break 2; //unknown windowID and/or not matching any open windows - } - - $this->craftingType = 0; - $inv = $this->windowIndex[$packet->windowid]; - $transaction = new BaseTransaction($inv, $packet->slot, $inv->getItem($packet->slot), $packet->item); - break; - } - - if($transaction->getSourceItem()->equals($transaction->getTargetItem()) and $transaction->getTargetItem()->getCount() === $transaction->getSourceItem()->getCount()){ //No changes! - //No changes, just a local inventory update sent by the client - break; - } - - if($this->currentTransaction === null or $this->currentTransaction->getCreationTime() < (microtime(true) - 8)){ - if($this->currentTransaction !== null){ - foreach($this->currentTransaction->getInventories() as $inventory){ - if($inventory instanceof PlayerInventory){ - $inventory->sendArmorContents($this); - } - $inventory->sendContents($this); - } - } - $this->currentTransaction = new SimpleTransactionGroup($this); - } - - $this->currentTransaction->addTransaction($transaction); - - if($this->currentTransaction->canExecute()){ - $achievements = []; - foreach($this->currentTransaction->getTransactions() as $ts){ - $inv = $ts->getInventory(); - if($inv instanceof FurnaceInventory){ - if($ts->getSlot() === 2){ - switch($inv->getResult()->getId()){ - case Item::IRON_INGOT: - $achievements[] = "acquireIron"; - break; - } - } - } - } - - if($this->currentTransaction->execute()){ - foreach($achievements as $a){ - $this->awardAchievement($a); - } - } - - $this->currentTransaction = null; - } - - break; - case ProtocolInfo::BLOCK_ENTITY_DATA_PACKET: - if($this->spawned === false or !$this->isAlive()){ - break; - } - $this->craftingType = 0; - - $pos = new Vector3($packet->x, $packet->y, $packet->z); - if($pos->distanceSquared($this) > 10000){ - break; - } - - $t = $this->level->getTile($pos); - if($t instanceof Spawnable){ - $nbt = new NBT(NBT::LITTLE_ENDIAN); - $nbt->read($packet->namedtag, false, true); - $nbt = $nbt->getData(); - if(!$t->updateCompoundTag($nbt, $this)){ - $t->spawnTo($this); - } - } - break; - case ProtocolInfo::REQUEST_CHUNK_RADIUS_PACKET: - $this->setViewDistance($packet->radius); - break; - case ProtocolInfo::SET_PLAYER_GAME_TYPE_PACKET: - if($packet->gamemode !== ($this->gamemode & 0x01)){ - //GUI gamemode change, set it back to original for now (only possible through client bug or hack with current allowed client permissions) - $pk = new SetPlayerGameTypePacket(); - $pk->gamemode = $this->gamemode & 0x01; - $this->dataPacket($pk); - $this->sendSettings(); - } - break; - case ProtocolInfo::ITEM_FRAME_DROP_ITEM_PACKET: - if($this->spawned === false or !$this->isAlive()){ - break; - } - - $tile = $this->level->getTile($this->temporalVector->setComponents($packet->x, $packet->y, $packet->z)); - if($tile instanceof ItemFrame){ - if($this->isSpectator()){ - $tile->spawnTo($this); - break; - } - - if(lcg_value() <= $tile->getItemDropChance()){ - $this->level->dropItem($tile->getBlock(), $tile->getItem()); - } - $tile->setItem(null); - $tile->setItemRotation(0); - } - - break; - default: - break; + if(!$ev->isCancelled() and !$packet->handle($this)){ + $this->server->getLogger()->debug("Unhandled " . get_class($packet) . " received from " . $this->getName() . ": 0x" . bin2hex($packet->buffer)); } $timings->stopTiming(); @@ -2950,7 +3391,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk = new TransferPacket(); $pk->address = $ev->getAddress(); $pk->port = $ev->getPort(); - $this->dataPacket($pk); + $this->directDataPacket($pk); $this->close("", $ev->getMessage(), false); return true; @@ -2991,8 +3432,71 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade return false; } - /** @var string[] */ - private $messageQueue = []; + /** + * Adds a title text to the user's screen, with an optional subtitle. + * + * @param string $title + * @param string $subtitle + * @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used. + * @param int $stay Duration in ticks to stay on screen for + * @param int $fadeOut Duration in ticks for fade-out. + */ + public function addTitle(string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1){ + $this->setTitleDuration($fadeIn, $stay, $fadeOut); + if($subtitle !== ""){ + $this->sendTitleText($subtitle, SetTitlePacket::TYPE_SET_SUBTITLE); + } + $this->sendTitleText($title, SetTitlePacket::TYPE_SET_TITLE); + } + + /** + * Adds small text to the user's screen. + * + * @param string $message + */ + public function addActionBarMessage(string $message){ + $this->sendTitleText($message, SetTitlePacket::TYPE_SET_ACTIONBAR_MESSAGE); + } + + /** + * Removes the title from the client's screen. + */ + public function removeTitles(){ + $pk = new SetTitlePacket(); + $pk->type = SetTitlePacket::TYPE_CLEAR_TITLE; + $this->dataPacket($pk); + } + + /** + * Sets the title duration. + * + * @param int $fadeIn Title fade-in time in ticks. + * @param int $stay Title stay time in ticks. + * @param int $fadeOut Title fade-out time in ticks. + */ + public function setTitleDuration(int $fadeIn, int $stay, int $fadeOut){ + if($fadeIn >= 0 and $stay >= 0 and $fadeOut >= 0){ + $pk = new SetTitlePacket(); + $pk->type = SetTitlePacket::TYPE_SET_ANIMATION_TIMES; + $pk->fadeInTime = $fadeIn; + $pk->stayTime = $stay; + $pk->fadeOutTime = $fadeOut; + $this->dataPacket($pk); + } + } + + /** + * Internal function used for sending titles. + * + * @param string $title + * @param int $type + */ + protected function sendTitleText(string $title, int $type){ + $pk = new SetTitlePacket(); + $pk->type = $type; + $pk->text = $title; + $this->dataPacket($pk); + } /** * Sends a direct chat message to a player @@ -3008,14 +3512,10 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $message = $message->getText(); } - //TODO: Remove this workaround (broken client MCPE 1.0.0) - $this->messageQueue[] = $this->server->getLanguage()->translateString($message); - /* $pk = new TextPacket(); $pk->type = TextPacket::TYPE_RAW; $pk->message = $this->server->getLanguage()->translateString($message); $this->dataPacket($pk); - */ } public function sendTranslation($message, array $parameters = []){ @@ -3070,21 +3570,41 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade * @param bool $notify */ final public function close($message = "", $reason = "generic reason", $notify = true){ - if($this->connected and !$this->closed){ if($notify and strlen((string) $reason) > 0){ - $pk = new DisconnectPacket; + $pk = new DisconnectPacket(); $pk->message = $reason; $this->directDataPacket($pk); } $this->connected = false; - if(strlen($this->getName()) > 0){ - $this->server->getPluginManager()->callEvent($ev = new PlayerQuitEvent($this, $message, true)); - if($this->loggedIn === true and $ev->getAutoSave()){ - $this->save(); + + $this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this); + $this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this); + + if($this->joined){ + //TODO: add events for player data saving + $this->save(); + + $this->server->getPluginManager()->callEvent($ev = new PlayerQuitEvent($this, $message)); + if($ev->getQuitMessage() != ""){ + $this->server->broadcastMessage($ev->getQuitMessage()); } } + $this->joined = false; + + if($this->isValid()){ + foreach($this->usedChunks as $index => $d){ + Level::getXZ($index, $chunkX, $chunkZ); + $this->level->unregisterChunkLoader($this, $chunkX, $chunkZ); + foreach($this->level->getChunkEntities($chunkX, $chunkZ) as $entity){ + $entity->despawnFrom($this); + } + unset($this->usedChunks[$index]); + } + } + $this->usedChunks = []; + $this->loadQueue = []; foreach($this->server->getOnlinePlayers() as $player){ if(!$player->canSee($this)){ @@ -3096,62 +3616,40 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade foreach($this->windowIndex as $window){ $this->removeWindow($window); } - - foreach($this->usedChunks as $index => $d){ - Level::getXZ($index, $chunkX, $chunkZ); - $this->level->unregisterChunkLoader($this, $chunkX, $chunkZ); - foreach($this->level->getChunkEntities($chunkX, $chunkZ) as $entity){ - $entity->despawnFrom($this, false); - } - unset($this->usedChunks[$index]); - } + $this->windows = null; + $this->windowIndex = []; parent::close(); + $this->spawned = false; $this->interface->close($this, $notify ? $reason : ""); if($this->loggedIn){ $this->server->removeOnlinePlayer($this); } - $this->loggedIn = false; - $this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this); - $this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this); - - if(isset($ev) and $this->username != "" and $this->spawned !== false and $ev->getQuitMessage() != ""){ - $this->server->broadcastMessage($ev->getQuitMessage()); - } - - $this->spawned = false; $this->server->getLogger()->info($this->getServer()->getLanguage()->translateString("pocketmine.player.logOut", [ TextFormat::AQUA . $this->getName() . TextFormat::WHITE, $this->ip, $this->port, $this->getServer()->getLanguage()->translateString($reason) ])); - $this->windows = new \SplObjectStorage(); - $this->windowIndex = []; - $this->usedChunks = []; - $this->loadQueue = []; - $this->hasSpawned = []; + $this->spawnPosition = null; - $this->forceMovement = null; + + if($this->perm !== null){ + $this->perm->clearPermissions(); + $this->perm = null; + } + + if($this->inventory !== null){ + $this->inventory = null; + $this->currentTransaction = null; + } + + $this->server->removePlayer($this); } - - if($this->perm !== null){ - $this->perm->clearPermissions(); - $this->perm = null; - } - - if($this->inventory !== null){ - $this->inventory = null; - $this->currentTransaction = null; - } - - $this->chunk = null; - - $this->server->removePlayer($this); } public function __debugInfo(){ @@ -3169,25 +3667,27 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } parent::saveNBT(); - if($this->level instanceof Level){ + + if($this->isValid()){ $this->namedtag->Level = new StringTag("Level", $this->level->getFolderName()); - if($this->hasValidSpawnPosition()){ - $this->namedtag["SpawnLevel"] = $this->spawnPosition->getLevel()->getFolderName(); - $this->namedtag["SpawnX"] = (int) $this->spawnPosition->x; - $this->namedtag["SpawnY"] = (int) $this->spawnPosition->y; - $this->namedtag["SpawnZ"] = (int) $this->spawnPosition->z; - } + } - foreach($this->achievements as $achievement => $status){ - $this->namedtag->Achievements[$achievement] = new ByteTag($achievement, $status === true ? 1 : 0); - } + if($this->hasValidSpawnPosition()){ + $this->namedtag["SpawnLevel"] = $this->spawnPosition->getLevel()->getFolderName(); + $this->namedtag["SpawnX"] = (int) $this->spawnPosition->x; + $this->namedtag["SpawnY"] = (int) $this->spawnPosition->y; + $this->namedtag["SpawnZ"] = (int) $this->spawnPosition->z; + } - $this->namedtag["playerGameType"] = $this->gamemode; - $this->namedtag["lastPlayed"] = new LongTag("lastPlayed", floor(microtime(true) * 1000)); + foreach($this->achievements as $achievement => $status){ + $this->namedtag->Achievements[$achievement] = new ByteTag($achievement, $status === true ? 1 : 0); + } - if($this->username != "" and $this->namedtag instanceof CompoundTag){ - $this->server->saveOfflinePlayerData($this->username, $this->namedtag, $async); - } + $this->namedtag["playerGameType"] = $this->gamemode; + $this->namedtag["lastPlayed"] = new LongTag("lastPlayed", floor(microtime(true) * 1000)); + + if($this->username != "" and $this->namedtag instanceof CompoundTag){ + $this->server->saveOfflinePlayerData($this->username, $this->namedtag, $async); } } @@ -3200,11 +3700,29 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade return $this->username; } + /** + * @return string + */ + public function getLowerCaseName() : string{ + return $this->iusername; + } + public function kill(){ if(!$this->spawned){ return; } + parent::kill(); + + $pk = new RespawnPacket(); + $pos = $this->getSpawn(); + $pk->x = $pos->x; + $pk->y = $pos->y; + $pk->z = $pos->z; + $this->dataPacket($pk); + } + + protected function callDeathEvent(){ $message = "death.attack.generic"; $params = [ @@ -3314,10 +3832,9 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade break; default: + break; } - Entity::kill(); - $this->server->getPluginManager()->callEvent($ev = new PlayerDeathEvent($this, $this->getDrops(), new TranslationContainer($message, $params))); if(!$ev->getKeepInventory()){ @@ -3335,13 +3852,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if($ev->getDeathMessage() != ""){ $this->server->broadcast($ev->getDeathMessage(), Server::BROADCAST_CHANNEL_USERS); } - - $pk = new RespawnPacket(); - $pos = $this->getSpawn(); - $pk->x = $pos->x; - $pk->y = $pos->y; - $pk->z = $pos->z; - $this->dataPacket($pk); } public function attack($damage, EntityDamageEvent $source){ @@ -3592,7 +4102,9 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } public function onChunkChanged(Chunk $chunk){ - unset($this->usedChunks[Level::chunkHash($chunk->getX(), $chunk->getZ())]); + if(isset($this->usedChunks[$hash = Level::chunkHash($chunk->getX(), $chunk->getZ())])){ + $this->usedChunks[$hash] = false; + } } public function onChunkLoaded(Chunk $chunk){ diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index 084118081..5699e1722 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -74,7 +74,7 @@ namespace pocketmine { use raklib\RakLib; const VERSION = "1.6.2dev"; - const API_VERSION = "3.0.0-ALPHA4"; + const API_VERSION = "3.0.0-ALPHA5"; const CODENAME = "Unleashed"; /* @@ -397,11 +397,6 @@ namespace pocketmine { ++$errors; } - if(!extension_loaded("sockets")){ - $logger->critical("Unable to find the Socket extension."); - ++$errors; - } - $pthreads_version = phpversion("pthreads"); if(substr_count($pthreads_version, ".") < 2){ $pthreads_version = "0.$pthreads_version"; @@ -430,19 +425,21 @@ namespace pocketmine { "); } - if(!extension_loaded("curl")){ - $logger->critical("Unable to find the cURL extension."); - ++$errors; - } + $extensions = [ + "curl" => "cURL", + "json" => "JSON", + "mbstring" => "Multibyte String", + "yaml" => "YAML", + "sockets" => "Sockets", + "zip" => "Zip", + "zlib" => "Zlib" + ]; - if(!extension_loaded("yaml")){ - $logger->critical("Unable to find the YAML extension."); - ++$errors; - } - - if(!extension_loaded("zlib")){ - $logger->critical("Unable to find the Zlib extension."); - ++$errors; + foreach($extensions as $ext => $name){ + if(!extension_loaded($ext)){ + $logger->critical("Unable to find the $name ($ext) extension."); + ++$errors; + } } if($errors > 0){ @@ -452,6 +449,10 @@ namespace pocketmine { exit(1); //Exit with error } + if(PHP_INT_SIZE < 8){ + $logger->warning("Running PocketMine-MP with 32-bit systems/PHP is deprecated. Support for 32-bit may be dropped in the future."); + } + $gitHash = str_repeat("00", 20); if(file_exists(\pocketmine\PATH . ".git/HEAD")){ //Found Git information! $ref = trim(file_get_contents(\pocketmine\PATH . ".git/HEAD")); diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 3290ab0c9..f9c6477d5 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -48,8 +48,8 @@ use pocketmine\inventory\Recipe; use pocketmine\item\enchantment\Enchantment; use pocketmine\item\Item; use pocketmine\lang\BaseLang; -use pocketmine\level\format\io\LevelProviderManager; use pocketmine\level\format\io\leveldb\LevelDB; +use pocketmine\level\format\io\LevelProviderManager; use pocketmine\level\format\io\region\Anvil; use pocketmine\level\format\io\region\McRegion; use pocketmine\level\format\io\region\PMAnvil; @@ -74,13 +74,13 @@ use pocketmine\nbt\tag\LongTag; use pocketmine\nbt\tag\ShortTag; use pocketmine\nbt\tag\StringTag; use pocketmine\network\CompressBatchedTask; +use pocketmine\network\mcpe\protocol\BatchPacket; +use pocketmine\network\mcpe\protocol\DataPacket; +use pocketmine\network\mcpe\protocol\ProtocolInfo; +use pocketmine\network\mcpe\protocol\PlayerListPacket; +use pocketmine\network\mcpe\RakLibInterface; use pocketmine\network\Network; -use pocketmine\network\protocol\BatchPacket; -use pocketmine\network\protocol\DataPacket; -use pocketmine\network\protocol\Info as ProtocolInfo; -use pocketmine\network\protocol\PlayerListPacket; use pocketmine\network\query\QueryHandler; -use pocketmine\network\RakLibInterface; use pocketmine\network\rcon\RCON; use pocketmine\network\upnp\UPnP; use pocketmine\permission\BanList; @@ -90,6 +90,7 @@ use pocketmine\plugin\Plugin; use pocketmine\plugin\PluginLoadOrder; use pocketmine\plugin\PluginManager; use pocketmine\plugin\ScriptPluginLoader; +use pocketmine\resourcepacks\ResourcePackManager; use pocketmine\scheduler\FileWriteTask; use pocketmine\scheduler\SendUsageTask; use pocketmine\scheduler\ServerScheduler; @@ -179,6 +180,9 @@ class Server{ /** @var CraftingManager */ private $craftingManager; + /** @var ResourcePackManager */ + private $resourceManager; + /** @var ConsoleCommandSender */ private $consoleSender; @@ -597,6 +601,13 @@ class Server{ return $this->craftingManager; } + /** + * @return ResourcePackManager + */ + public function getResourceManager() : ResourcePackManager{ + return $this->resourceManager; + } + /** * @return ServerScheduler */ @@ -690,7 +701,7 @@ class Server{ * * @return CompoundTag */ - public function getOfflinePlayerData($name){ + public function getOfflinePlayerData($name) : CompoundTag{ $name = strtolower($name); $path = $this->getDataPath() . "players/"; if($this->shouldSavePlayerData()){ @@ -809,7 +820,7 @@ class Server{ public function getPlayerExact($name){ $name = strtolower($name); foreach($this->getOnlinePlayers() as $player){ - if(strtolower($player->getName()) === $name){ + if($player->getLowerCaseName() === $name){ return $player; } } @@ -826,7 +837,7 @@ class Server{ $partialName = strtolower($partialName); $matchedPlayers = []; foreach($this->getOnlinePlayers() as $player){ - if(strtolower($player->getName()) === $partialName){ + if($player->getLowerCaseName() === $partialName){ $matchedPlayers = [$player]; break; }elseif(stripos($player->getName(), $partialName) !== false){ @@ -1540,6 +1551,8 @@ class Server{ Attribute::init(); $this->craftingManager = new CraftingManager(); + $this->resourceManager = new ResourcePackManager($this, $this->getDataPath() . "resource_packs" . DIRECTORY_SEPARATOR); + $this->pluginManager = new PluginManager($this, $this->commandMap); $this->pluginManager->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this->consoleSender); $this->pluginManager->setUseTimings($this->getProperty("settings.enable-profiling", false)); @@ -2196,7 +2209,7 @@ class Server{ foreach($this->players as $p){ if(!$p->loggedIn and ($tickTime - $p->creationTime) >= 10){ $p->close("", "Login timeout"); - }elseif($this->alwaysTickPlayers){ + }elseif($this->alwaysTickPlayers and $p->joined){ $p->onUpdate($currentTick); } } @@ -2241,7 +2254,7 @@ class Server{ if($this->getAutoSave()){ Timings::$worldSaveTimer->startTiming(); foreach($this->players as $index => $player){ - if($player->isOnline()){ + if($player->joined){ $player->save(true); }elseif(!$player->isConnected()){ $this->removePlayer($player); diff --git a/src/pocketmine/block/Furnace.php b/src/pocketmine/block/Furnace.php index a013b3ab4..44281446f 100644 --- a/src/pocketmine/block/Furnace.php +++ b/src/pocketmine/block/Furnace.php @@ -29,4 +29,8 @@ class Furnace extends BurningFurnace{ public function getName(){ return "Furnace"; } + + public function getLightLevel(){ + return 0; + } } \ No newline at end of file diff --git a/src/pocketmine/command/defaults/DifficultyCommand.php b/src/pocketmine/command/defaults/DifficultyCommand.php index e49784fde..64650a47f 100644 --- a/src/pocketmine/command/defaults/DifficultyCommand.php +++ b/src/pocketmine/command/defaults/DifficultyCommand.php @@ -24,7 +24,7 @@ namespace pocketmine\command\defaults; use pocketmine\command\Command; use pocketmine\command\CommandSender; use pocketmine\event\TranslationContainer; -use pocketmine\network\protocol\SetDifficultyPacket; +use pocketmine\network\mcpe\protocol\SetDifficultyPacket; use pocketmine\Server; class DifficultyCommand extends VanillaCommand{ diff --git a/src/pocketmine/command/defaults/EffectCommand.php b/src/pocketmine/command/defaults/EffectCommand.php index ba30dc112..76e761b38 100644 --- a/src/pocketmine/command/defaults/EffectCommand.php +++ b/src/pocketmine/command/defaults/EffectCommand.php @@ -23,7 +23,6 @@ namespace pocketmine\command\defaults; use pocketmine\command\CommandSender; use pocketmine\entity\Effect; -use pocketmine\entity\InstantEffect; use pocketmine\event\TranslationContainer; use pocketmine\utils\TextFormat; @@ -80,12 +79,9 @@ class EffectCommand extends VanillaCommand{ $amplification = 0; if(count($args) >= 3){ - $duration = (int) $args[2]; - if(!($effect instanceof InstantEffect)){ - $duration *= 20; - } - }elseif($effect instanceof InstantEffect){ - $duration = 1; + $duration = ((int) $args[2]) * 20; //ticks + }else{ + $duration = $effect->getDefaultDuration(); } if(count($args) >= 4){ diff --git a/src/pocketmine/command/defaults/VersionCommand.php b/src/pocketmine/command/defaults/VersionCommand.php index 54d091fdf..e18651b56 100644 --- a/src/pocketmine/command/defaults/VersionCommand.php +++ b/src/pocketmine/command/defaults/VersionCommand.php @@ -23,7 +23,7 @@ namespace pocketmine\command\defaults; use pocketmine\command\CommandSender; use pocketmine\event\TranslationContainer; -use pocketmine\network\protocol\Info; +use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\plugin\Plugin; use pocketmine\utils\TextFormat; @@ -51,7 +51,7 @@ class VersionCommand extends VanillaCommand{ $sender->getServer()->getCodename(), $sender->getServer()->getApiVersion(), $sender->getServer()->getVersion(), - Info::CURRENT_PROTOCOL + ProtocolInfo::CURRENT_PROTOCOL ])); }else{ $pluginName = implode(" ", $args); diff --git a/src/pocketmine/entity/Arrow.php b/src/pocketmine/entity/Arrow.php index 969021323..a31a66b9e 100644 --- a/src/pocketmine/entity/Arrow.php +++ b/src/pocketmine/entity/Arrow.php @@ -24,7 +24,7 @@ namespace pocketmine\entity; use pocketmine\level\Level; use pocketmine\level\particle\CriticalParticle; use pocketmine\nbt\tag\CompoundTag; -use pocketmine\network\protocol\AddEntityPacket; +use pocketmine\network\mcpe\protocol\AddEntityPacket; use pocketmine\Player; class Arrow extends Projectile{ diff --git a/src/pocketmine/entity/Attribute.php b/src/pocketmine/entity/Attribute.php index be5a6cfae..c48534c63 100644 --- a/src/pocketmine/entity/Attribute.php +++ b/src/pocketmine/entity/Attribute.php @@ -165,6 +165,10 @@ class Attribute{ return $this; } + public function resetToDefault(){ + $this->setValue($this->getDefaultValue()); + } + public function getValue(){ return $this->currentValue; } diff --git a/src/pocketmine/entity/AttributeMap.php b/src/pocketmine/entity/AttributeMap.php index cdf821c52..b74492f08 100644 --- a/src/pocketmine/entity/AttributeMap.php +++ b/src/pocketmine/entity/AttributeMap.php @@ -38,6 +38,9 @@ class AttributeMap implements \ArrayAccess{ return $this->attributes[$id] ?? null; } + /** + * @return Attribute[] + */ public function getAll(): array{ return $this->attributes; } diff --git a/src/pocketmine/entity/Effect.php b/src/pocketmine/entity/Effect.php index 9327c2042..a5327dbde 100644 --- a/src/pocketmine/entity/Effect.php +++ b/src/pocketmine/entity/Effect.php @@ -26,22 +26,20 @@ use pocketmine\event\entity\EntityEffectAddEvent; use pocketmine\event\entity\EntityEffectRemoveEvent; use pocketmine\event\entity\EntityRegainHealthEvent; use pocketmine\event\player\PlayerExhaustEvent; -use pocketmine\network\protocol\MobEffectPacket; +use pocketmine\network\mcpe\protocol\MobEffectPacket; use pocketmine\Player; +use pocketmine\utils\Config; class Effect{ const SPEED = 1; const SLOWNESS = 2; const HASTE = 3; - const SWIFTNESS = 3; - const FATIGUE = 4; - const MINING_FATIGUE = 4; + const FATIGUE = 4, MINING_FATIGUE = 4; const STRENGTH = 5; -// TODO: const HEALING = 6; -// TODO: const HARMING = 7; + const INSTANT_HEALTH = 6, HEALING = 6; + const INSTANT_DAMAGE = 7, HARMING = 7; const JUMP = 8; - const NAUSEA = 9; - const CONFUSION = 9; + const NAUSEA = 9, CONFUSION = 9; const REGENERATION = 10; const DAMAGE_RESISTANCE = 11; const FIRE_RESISTANCE = 12; @@ -54,44 +52,43 @@ class Effect{ const POISON = 19; const WITHER = 20; const HEALTH_BOOST = 21; - const ABSORPTION = 22; // TODO implement + const ABSORPTION = 22; const SATURATION = 23; + const LEVITATION = 24; //TODO /** @var Effect[] */ - protected static $effects; + protected static $effects = []; public static function init(){ - self::$effects = new \SplFixedArray(256); + $config = new Config(\pocketmine\PATH . "src/pocketmine/resources/effects.json", Config::JSON, []); - self::$effects[Effect::SPEED] = new Effect(Effect::SPEED, "%potion.moveSpeed", 124, 175, 198); - self::$effects[Effect::SLOWNESS] = new Effect(Effect::SLOWNESS, "%potion.moveSlowdown", 90, 108, 129, true); - self::$effects[Effect::SWIFTNESS] = new Effect(Effect::SWIFTNESS, "%potion.digSpeed", 217, 192, 67); - self::$effects[Effect::FATIGUE] = new Effect(Effect::FATIGUE, "%potion.digSlowDown", 74, 66, 23, true); - self::$effects[Effect::STRENGTH] = new Effect(Effect::STRENGTH, "%potion.damageBoost", 147, 36, 35); - //self::$effects[Effect::HEALING] = new InstantEffect(Effect::HEALING, "%potion.heal", 248, 36, 35); - //self::$effects[Effect::HARMING] = new InstantEffect(Effect::HARMING, "%potion.harm", 67, 10, 9, true); - self::$effects[Effect::JUMP] = new Effect(Effect::JUMP, "%potion.jump", 34, 255, 76); - self::$effects[Effect::NAUSEA] = new Effect(Effect::NAUSEA, "%potion.confusion", 85, 29, 74, true); - self::$effects[Effect::REGENERATION] = new Effect(Effect::REGENERATION, "%potion.regeneration", 205, 92, 171); - self::$effects[Effect::DAMAGE_RESISTANCE] = new Effect(Effect::DAMAGE_RESISTANCE, "%potion.resistance", 153, 69, 58); - self::$effects[Effect::FIRE_RESISTANCE] = new Effect(Effect::FIRE_RESISTANCE, "%potion.fireResistance", 228, 154, 58); - self::$effects[Effect::WATER_BREATHING] = new Effect(Effect::WATER_BREATHING, "%potion.waterBreathing", 46, 82, 153); - self::$effects[Effect::INVISIBILITY] = new Effect(Effect::INVISIBILITY, "%potion.invisibility", 127, 131, 146); - self::$effects[Effect::BLINDNESS] = new Effect(Effect::BLINDNESS, "%potion.blindness", 191, 192, 192); - self::$effects[Effect::NIGHT_VISION] = new Effect(Effect::NIGHT_VISION, "%potion.nightVision", 0, 0, 139); - self::$effects[Effect::HUNGER] = new Effect(Effect::HUNGER, "%potion.hunger", 46, 139, 87); - self::$effects[Effect::WEAKNESS] = new Effect(Effect::WEAKNESS, "%potion.weakness", 72, 77, 72, true); - self::$effects[Effect::POISON] = new Effect(Effect::POISON, "%potion.poison", 78, 147, 49, true); - self::$effects[Effect::WITHER] = new Effect(Effect::WITHER, "%potion.wither", 53, 42, 39, true); - self::$effects[Effect::HEALTH_BOOST] = new Effect(Effect::HEALTH_BOOST, "%potion.healthBoost", 248, 125, 35); - self::$effects[Effect::ABSORPTION] = new Effect(Effect::ABSORPTION, "%potion.absorption", 36, 107, 251); - self::$effects[Effect::SATURATION] = new Effect(Effect::SATURATION, "%potion.saturation", 255, 0, 255); + foreach($config->getAll() as $name => $data){ + $color = hexdec($data["color"]); + $r = ($color >> 16) & 0xff; + $g = ($color >> 8) & 0xff; + $b = $color & 0xff; + self::registerEffect($name, new Effect( + $data["id"], + "%" . $data["name"], + $r, + $g, + $b, + $data["isBad"] ?? false, + $data["default_duration"] ?? 300 * 20, + $data["has_bubbles"] ?? true + )); + } + } + + public static function registerEffect(string $internalName, Effect $effect){ + self::$effects[$effect->getId()] = $effect; + self::$effects[$internalName] = $effect; } /** * @param int $id * - * @return $this + * @return Effect|null */ public static function getEffect($id){ if(isset(self::$effects[$id])){ @@ -100,9 +97,14 @@ class Effect{ return null; } + /** + * @param string $name + * + * @return Effect|null + */ public static function getEffectByName($name){ - if(defined(Effect::class . "::" . strtoupper($name))){ - return self::getEffect(constant(Effect::class . "::" . strtoupper($name))); + if(isset(self::$effects[$name])){ + return clone self::$effects[$name]; } return null; } @@ -124,40 +126,106 @@ class Effect{ protected $bad; - public function __construct($id, $name, $r, $g, $b, $isBad = false){ + protected $defaultDuration = 300 * 20; + + protected $hasBubbles = true; + + /** + * @param int $id Effect ID as per Minecraft PE + * @param string $name Translation key used for effect name + * @param int $r 0-255, red balance of potion particle colour + * @param int $g 0-255, green balance of potion particle colour + * @param int $b 0-255, blue balance of potion particle colour + * @param bool $isBad Whether the effect is harmful + * @param int $defaultDuration Duration in ticks the effect will last for by default if applied without a duration. + * @param bool $hasBubbles Whether the effect has potion bubbles. Some do not (e.g. Instant Damage has its own particles instead of bubbles) + */ + public function __construct($id, $name, $r, $g, $b, $isBad = false, int $defaultDuration = 300 * 20, bool $hasBubbles = true){ $this->id = $id; $this->name = $name; $this->bad = (bool) $isBad; $this->setColor($r, $g, $b); + $this->defaultDuration = $defaultDuration; + $this->duration = $defaultDuration; + $this->hasBubbles = $hasBubbles; } + /** + * Returns the translation key used to translate this effect's name. + * @return string + */ public function getName(){ return $this->name; } + /** + * Returns the effect ID as per Minecraft PE + * @return int + */ public function getId(){ return $this->id; } + /** + * Sets the duration in ticks of the effect. + * @param $ticks + * + * @return $this + */ public function setDuration($ticks){ $this->duration = $ticks; return $this; } + /** + * Returns the duration remaining of the effect in ticks. + * @return int + */ public function getDuration(){ return $this->duration; } + /** + * Returns the default duration this effect will apply for if a duration is not specified. + * @return int + */ + public function getDefaultDuration() : int{ + return $this->defaultDuration; + } + + /** + * Returns whether this effect will give the subject potion bubbles. + * @return bool + */ + public function hasBubbles() : bool{ + return $this->hasBubbles; + } + + /** + * Returns whether this effect will produce some visible effect, such as bubbles or particles. + * NOTE: Do not confuse this with {@link Effect#hasBubbles}. For example, Instant Damage does not have bubbles, but still produces visible effects (particles). + * + * @return bool + */ public function isVisible(){ return $this->show; } + /** + * Changes the visibility of the effect. + * @param bool $bool + * + * @return $this + */ public function setVisible($bool){ $this->show = (bool) $bool; return $this; } /** + * Returns the amplifier of this effect. + * TODO: fix mess of amplifier used instead of level for effect calculation. + * * @return int */ public function getAmplifier(){ @@ -174,19 +242,40 @@ class Effect{ return $this; } + /** + * Returns whether the effect is ambient. + * @return bool + */ public function isAmbient(){ return $this->ambient; } + /** + * Sets the ambiency of this effect. + * @param bool $ambient + * + * @return $this + */ public function setAmbient($ambient = true){ $this->ambient = (bool) $ambient; return $this; } + /** + * Returns whether this effect is harmful. + * TODO: implement inverse effect results for undead mobs + * + * @return bool + */ public function isBad(){ return $this->bad; } + /** + * Returns whether the effect will do something on the current tick. + * + * @return bool + */ public function canTick(){ switch($this->id){ case Effect::POISON: @@ -212,10 +301,19 @@ class Effect{ return ($this->duration % $interval) === 0; } return true; + case Effect::INSTANT_DAMAGE: + case Effect::INSTANT_HEALTH: + //If forced to last longer than 1 tick, these apply every tick. + return true; } return false; } + /** + * Applies effect results to an entity. + * + * @param Entity $entity + */ public function applyEffect(Entity $entity){ switch($this->id){ case Effect::POISON: @@ -241,17 +339,48 @@ class Effect{ if($entity instanceof Human){ $entity->exhaust(0.5 * $this->amplifier, PlayerExhaustEvent::CAUSE_POTION); } + break; + case Effect::INSTANT_HEALTH: + //TODO: add particles (witch spell) + if($entity->getHealth() < $entity->getMaxHealth()){ + $amount = 2 * (2 ** (($this->amplifier + 1) % 32)); + $entity->heal($amount, new EntityRegainHealthEvent($entity, $amount, EntityRegainHealthEvent::CAUSE_MAGIC)); + } + break; + case Effect::INSTANT_DAMAGE: + //TODO: add particles (witch spell) + $amount = 2 * (2 ** (($this->amplifier + 1) % 32)); + $entity->attack($amount, new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_MAGIC, $amount)); + break; } } + /** + * Returns an RGB color array of this effect's color. + * @return array + */ public function getColor(){ return [$this->color >> 16, ($this->color >> 8) & 0xff, $this->color & 0xff]; } + /** + * Sets the color of this effect. + * + * @param int $r + * @param int $g + * @param int $b + */ public function setColor($r, $g, $b){ $this->color = (($r & 0xff) << 16) + (($g & 0xff) << 8) + ($b & 0xff); } + /** + * Adds this effect to the Entity, performing effect overriding as specified. + * + * @param Entity $entity + * @param bool $modify + * @param Effect|null $oldEffect + */ public function add(Entity $entity, $modify = false, Effect $oldEffect = null){ $entity->getLevel()->getServer()->getPluginManager()->callEvent($ev = new EntityEffectAddEvent($entity, $this, $modify, $oldEffect)); if($ev->isCancelled()){ @@ -273,30 +402,58 @@ class Effect{ $entity->dataPacket($pk); } - if($this->id === Effect::INVISIBILITY){ - $entity->setDataFlag(Entity::DATA_FLAGS, Entity::DATA_FLAG_INVISIBLE, true); - $entity->setNameTagVisible(false); - }elseif($this->id === Effect::SPEED){ - $attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED); - if($ev->willModify() and $oldEffect !== null){ - $speed = $attr->getValue() / (1 + 0.2 * $oldEffect->getAmplifier()); - }else{ - $speed = $attr->getValue(); - } - $speed *= (1 + 0.2 * $this->amplifier); - $attr->setValue($speed); - }elseif($this->id === Effect::SLOWNESS){ - $attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED); - if($ev->willModify() and $oldEffect !== null){ - $speed = $attr->getValue() / (1 - 0.15 * $oldEffect->getAmplifier()); - }else{ - $speed = $attr->getValue(); - } - $speed *= (1 - 0.15 * $this->amplifier); - $attr->setValue($speed, true); + switch($this->id){ + case Effect::INVISIBILITY: + $entity->setDataFlag(Entity::DATA_FLAGS, Entity::DATA_FLAG_INVISIBLE, true); + $entity->setNameTagVisible(false); + break; + case Effect::SPEED: + $attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED); + if($ev->willModify() and $oldEffect !== null){ + $speed = $attr->getValue() / (1 + 0.2 * $oldEffect->getAmplifier()); + }else{ + $speed = $attr->getValue(); + } + $speed *= (1 + 0.2 * $this->amplifier); + $attr->setValue($speed); + break; + case Effect::SLOWNESS: + $attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED); + if($ev->willModify() and $oldEffect !== null){ + $speed = $attr->getValue() / (1 - 0.15 * $oldEffect->getAmplifier()); + }else{ + $speed = $attr->getValue(); + } + $speed *= (1 - 0.15 * $this->amplifier); + $attr->setValue($speed, true); + break; + + case Effect::HEALTH_BOOST: + $attr = $entity->getAttributeMap()->getAttribute(Attribute::HEALTH); + if($ev->willModify() and $oldEffect !== null){ + $max = $attr->getMaxValue() - (4 * ($oldEffect->getAmplifier() + 1)); + }else{ + $max = $attr->getMaxValue(); + } + + $max += (4 * ($this->amplifier + 1)); + $attr->setMaxValue($max); + break; + case Effect::ABSORPTION: + $new = (4 * ($this->amplifier + 1)); + if($new > $entity->getAbsorption()){ + $entity->setAbsorption($new); + } + break; + } } + /** + * Removes the effect from the entity, resetting any changed values back to their original defaults. + * + * @param Entity $entity + */ public function remove(Entity $entity){ $entity->getLevel()->getServer()->getPluginManager()->callEvent($ev = new EntityEffectRemoveEvent($entity, $this)); if($ev->isCancelled()){ @@ -311,15 +468,26 @@ class Effect{ $entity->dataPacket($pk); } - if($this->id === Effect::INVISIBILITY){ - $entity->setDataFlag(Entity::DATA_FLAGS, Entity::DATA_FLAG_INVISIBLE, false); - $entity->setNameTagVisible(true); - }elseif($this->id === Effect::SPEED){ - $attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED); - $attr->setValue($attr->getValue() / (1 + 0.2 * $this->amplifier)); - }elseif($this->id === Effect::SLOWNESS){ - $attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED); - $attr->setValue($attr->getValue() / (1 - 0.15 * $this->amplifier)); + switch($this->id){ + case Effect::INVISIBILITY: + $entity->setDataFlag(Entity::DATA_FLAGS, Entity::DATA_FLAG_INVISIBLE, false); + $entity->setNameTagVisible(true); + break; + case Effect::SPEED: + $attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED); + $attr->setValue($attr->getValue() / (1 + 0.2 * $this->amplifier)); + break; + case Effect::SLOWNESS: + $attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED); + $attr->setValue($attr->getValue() / (1 - 0.15 * $this->amplifier)); + break; + case Effect::HEALTH_BOOST: + $attr = $entity->getAttributeMap()->getAttribute(Attribute::HEALTH); + $attr->setMaxValue($attr->getMaxValue() - (4 * ($this->amplifier + 1))); + break; + case Effect::ABSORPTION: + $entity->setAbsorption(0); + break; } } } diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index 4537c7d2b..094e80ea5 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -52,9 +52,9 @@ use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ShortTag; use pocketmine\nbt\tag\StringTag; -use pocketmine\network\protocol\MobEffectPacket; -use pocketmine\network\protocol\RemoveEntityPacket; -use pocketmine\network\protocol\SetEntityDataPacket; +use pocketmine\network\mcpe\protocol\MobEffectPacket; +use pocketmine\network\mcpe\protocol\RemoveEntityPacket; +use pocketmine\network\mcpe\protocol\SetEntityDataPacket; use pocketmine\Player; use pocketmine\plugin\Plugin; use pocketmine\Server; @@ -339,6 +339,7 @@ abstract class Entity extends Location implements Metadatable{ $this->invulnerable = $this->namedtag["Invulnerable"] > 0 ? true : false; $this->attributeMap = new AttributeMap(); + $this->addAttributes(); $this->chunk->addEntity($this); $this->level->addEntity($this); @@ -501,10 +502,6 @@ abstract class Entity extends Location implements Metadatable{ $this->effects[$effect->getId()] = $effect; $this->recalculateEffectColor(); - - if($effect->getId() === Effect::HEALTH_BOOST){ - $this->setHealth($this->getHealth() + 4 * ($effect->getAmplifier() + 1)); - } } protected function recalculateEffectColor(){ @@ -513,7 +510,7 @@ abstract class Entity extends Location implements Metadatable{ $count = 0; $ambient = true; foreach($this->effects as $effect){ - if($effect->isVisible()){ + if($effect->isVisible() and $effect->hasBubbles()){ $c = $effect->getColor(); $color[0] += $c[0] * ($effect->getAmplifier() + 1); $color[1] += $c[1] * ($effect->getAmplifier() + 1); @@ -530,7 +527,7 @@ abstract class Entity extends Location implements Metadatable{ $g = ($color[1] / $count) & 0xff; $b = ($color[2] / $count) & 0xff; - $this->setDataProperty(Entity::DATA_POTION_COLOR, Entity::DATA_TYPE_INT, ($r << 16) + ($g << 8) + $b); + $this->setDataProperty(Entity::DATA_POTION_COLOR, Entity::DATA_TYPE_INT, 0xff000000 | ($r << 16) | ($g << 8) | $b); $this->setDataProperty(Entity::DATA_POTION_AMBIENT, Entity::DATA_TYPE_BYTE, $ambient ? 1 : 0); }else{ $this->setDataProperty(Entity::DATA_POTION_COLOR, Entity::DATA_TYPE_INT, 0); @@ -646,8 +643,6 @@ abstract class Entity extends Location implements Metadatable{ $this->scheduleUpdate(); - $this->addAttributes(); - if(isset($this->namedtag->ActiveEffects)){ foreach($this->namedtag->ActiveEffects->getValue() as $e){ $amplifier = $e["Amplifier"] & 0xff; //0-255 only @@ -724,6 +719,7 @@ abstract class Entity extends Location implements Metadatable{ /** * @param Player $player + * @param bool $send */ public function despawnFrom(Player $player, bool $send = true){ if(isset($this->hasSpawned[$player->getLoaderId()])){ @@ -758,7 +754,21 @@ abstract class Entity extends Location implements Metadatable{ $this->setLastDamageCause($source); - $this->setHealth($this->getHealth() - $source->getFinalDamage()); + $damage = $source->getFinalDamage(); + + $absorption = $this->getAbsorption(); + if($absorption > 0){ + if($absorption > $damage){ + //Use absorption health before normal health. + $this->setAbsorption($absorption - $damage); + $damage = 0; + }else{ + $this->setAbsorption(0); + $damage -= $absorption; + } + } + + $this->setHealth($this->getHealth() - $damage); } /** @@ -808,6 +818,14 @@ abstract class Entity extends Location implements Metadatable{ } } + public function getAbsorption() : int{ + return 0; + } + + public function setAbsorption(int $absorption){ + + } + /** * @param EntityDamageEvent $type */ @@ -830,7 +848,7 @@ abstract class Entity extends Location implements Metadatable{ * @return int */ public function getMaxHealth(){ - return $this->maxHealth + ($this->hasEffect(Effect::HEALTH_BOOST) ? 4 * ($this->getEffect(Effect::HEALTH_BOOST)->getAmplifier() + 1) : 0); + return $this->maxHealth; } /** @@ -1659,7 +1677,10 @@ abstract class Entity extends Location implements Metadatable{ if(!$this->closed){ $this->server->getPluginManager()->callEvent(new EntityDespawnEvent($this)); $this->closed = true; + $this->despawnFromAll(); + $this->hasSpawned = []; + if($this->chunk !== null){ $this->chunk->removeEntity($this); $this->chunk = null; diff --git a/src/pocketmine/entity/FallingSand.php b/src/pocketmine/entity/FallingSand.php index 3a7c9f8e7..945892eb4 100644 --- a/src/pocketmine/entity/FallingSand.php +++ b/src/pocketmine/entity/FallingSand.php @@ -29,7 +29,7 @@ use pocketmine\item\Item as ItemItem; use pocketmine\math\Vector3; use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\IntTag; -use pocketmine\network\protocol\AddEntityPacket; +use pocketmine\network\mcpe\protocol\AddEntityPacket; use pocketmine\Player; class FallingSand extends Entity{ diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index cd0cffcc1..9797ead87 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -27,6 +27,7 @@ use pocketmine\event\player\PlayerExhaustEvent; use pocketmine\inventory\InventoryHolder; use pocketmine\inventory\PlayerInventory; use pocketmine\item\Item as ItemItem; +use pocketmine\level\Level; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; @@ -35,8 +36,7 @@ use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ShortTag; use pocketmine\nbt\tag\StringTag; -use pocketmine\network\protocol\AddPlayerPacket; -use pocketmine\network\protocol\RemoveEntityPacket; +use pocketmine\network\mcpe\protocol\AddPlayerPacket; use pocketmine\Player; use pocketmine\utils\UUID; @@ -62,13 +62,21 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ public $eyeHeight = 1.62; protected $skinId; - protected $skin; + protected $skin = null; protected $foodTickTimer = 0; protected $totalXp = 0; protected $xpSeed; + public function __construct(Level $level, CompoundTag $nbt){ + if($this->skin === null and (!isset($nbt->Skin) or !isset($nbt->Skin->Data) or !Player::isValidSkin($nbt->Skin->Data->getValue()))){ + throw new \InvalidStateException((new \ReflectionClass($this))->getShortName() . " must have a valid skin set"); + } + + parent::__construct($level, $nbt); + } + public function getSkinData(){ return $this->skin; } @@ -96,10 +104,23 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ * @param string $skinId */ public function setSkin($str, $skinId){ + if(!Player::isValidSkin($str)){ + throw new \InvalidStateException("Specified skin is not valid, must be 8KiB or 16KiB"); + } + $this->skin = $str; $this->skinId = $skinId; } + public function jump(){ + parent::jump(); + if($this->isSprinting()){ + $this->exhaust(0.8, PlayerExhaustEvent::CAUSE_SPRINT_JUMPING); + }else{ + $this->exhaust(0.2, PlayerExhaustEvent::CAUSE_JUMPING); + } + } + public function getFood() : float{ return $this->attributeMap->getAttribute(Attribute::HUNGER)->getValue(); } @@ -353,42 +374,50 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ public function entityBaseTick($tickDiff = 1){ $hasUpdate = parent::entityBaseTick($tickDiff); + $this->doFoodTick($tickDiff); + + return $hasUpdate; + } + + public function doFoodTick(int $tickDiff = 1){ if($this->isAlive()){ $food = $this->getFood(); $health = $this->getHealth(); - if($food >= 18){ - $this->foodTickTimer++; - if($this->foodTickTimer >= 80 and $health < $this->getMaxHealth()){ - $this->heal(1, new EntityRegainHealthEvent($this, 1, EntityRegainHealthEvent::CAUSE_SATURATION)); - $this->exhaust(3.0, PlayerExhaustEvent::CAUSE_HEALTH_REGEN); - $this->foodTickTimer = 0; + $difficulty = $this->server->getDifficulty(); + $this->foodTickTimer += $tickDiff; + if($this->foodTickTimer >= 80){ + $this->foodTickTimer = 0; + } + + if($difficulty === 0 and $this->foodTickTimer % 10 === 0){ //Peaceful + if($food < 20){ + $this->addFood(1.0); } - }elseif($food === 0){ - $this->foodTickTimer++; - if($this->foodTickTimer >= 80){ - $diff = $this->server->getDifficulty(); - $can = false; - if($diff === 1){ - $can = $health > 10; - }elseif($diff === 2){ - $can = $health > 1; - }elseif($diff === 3){ - $can = true; + if($this->foodTickTimer % 20 === 0 and $health < $this->getMaxHealth()){ + $this->heal(1, new EntityRegainHealthEvent($this, 1, EntityRegainHealthEvent::CAUSE_SATURATION)); + } + } + + if($this->foodTickTimer === 0){ + if($food >= 18){ + if($health < $this->getMaxHealth()){ + $this->heal(1, new EntityRegainHealthEvent($this, 1, EntityRegainHealthEvent::CAUSE_SATURATION)); + $this->exhaust(3.0, PlayerExhaustEvent::CAUSE_HEALTH_REGEN); } - if($can){ + }elseif($food <= 0){ + if(($difficulty === 1 and $health > 10) or ($difficulty === 2 and $health > 1) or $difficulty === 3){ $this->attack(1, new EntityDamageEvent($this, EntityDamageEvent::CAUSE_STARVATION, 1)); } } } + if($food <= 6){ if($this->isSprinting()){ $this->setSprinting(false); } } } - - return $hasUpdate; } public function getName(){ @@ -471,7 +500,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ if($player !== $this and !isset($this->hasSpawned[$player->getLoaderId()])){ $this->hasSpawned[$player->getLoaderId()] = $player; - if(strlen($this->skin) < 64 * 32 * 4){ + if(!Player::isValidSkin($this->skin)){ throw new \InvalidStateException((new \ReflectionClass($this))->getShortName() . " must have a valid skin set"); } @@ -505,7 +534,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ public function close(){ if(!$this->closed){ - if(!($this instanceof Player) or $this->loggedIn){ + if($this->inventory !== null){ foreach($this->inventory->getViewers() as $viewer){ $viewer->removeWindow($this->inventory); } diff --git a/src/pocketmine/entity/InstantEffect.php b/src/pocketmine/entity/InstantEffect.php deleted file mode 100644 index 708f39bee..000000000 --- a/src/pocketmine/entity/InstantEffect.php +++ /dev/null @@ -1,26 +0,0 @@ -isAlive(); parent::setHealth($amount); - $this->attributeMap->getAttribute(Attribute::HEALTH)->setValue($this->getHealth()); + $this->attributeMap->getAttribute(Attribute::HEALTH)->setValue($this->getHealth(), true); if($this->isAlive() and !$wasAlive){ $pk = new EntityEventPacket(); $pk->eid = $this->getId(); @@ -77,10 +79,22 @@ abstract class Living extends Entity implements Damageable{ } } + public function getMaxHealth(){ + return $this->attributeMap->getAttribute(Attribute::HEALTH)->getMaxValue(); + } + public function setMaxHealth($amount){ $this->attributeMap->getAttribute(Attribute::HEALTH)->setMaxValue($amount); } + public function getAbsorption() : int{ + return (int) $this->attributeMap->getAttribute(Attribute::ABSORPTION)->getValue(); + } + + public function setAbsorption(int $absorption){ + $this->attributeMap->getAttribute(Attribute::ABSORPTION)->setValue($absorption); + } + public function saveNBT(){ parent::saveNBT(); $this->namedtag->Health = new ShortTag("Health", $this->getHealth()); @@ -103,6 +117,23 @@ abstract class Living extends Entity implements Damageable{ $this->attackTime = 0; } + /** + * Returns the initial upwards velocity of a jumping entity in blocks/tick, including additional velocity due to effects. + * @return float + */ + public function getJumpVelocity() : float{ + return $this->jumpVelocity + ($this->hasEffect(Effect::JUMP) ? (($this->getEffect(Effect::JUMP)->getAmplifier() + 1) / 10) : 0); + } + + /** + * Called when the entity jumps from the ground. This method adds upwards velocity to the entity. + */ + public function jump(){ + if($this->onGround){ + $this->motionY = $this->getJumpVelocity(); //Y motion should already be 0 if we're jumping from the ground. + } + } + public function attack($damage, EntityDamageEvent $source){ if($this->attackTime > 0 or $this->noDamageTicks > 0){ $lastCause = $this->getLastDamageCause(); @@ -171,6 +202,10 @@ abstract class Living extends Entity implements Damageable{ return; } parent::kill(); + $this->callDeathEvent(); + } + + protected function callDeathEvent(){ $this->server->getPluginManager()->callEvent($ev = new EntityDeathEvent($this, $this->getDrops())); foreach($ev->getDrops() as $item){ $this->getLevel()->dropItem($this, $item); diff --git a/src/pocketmine/entity/PrimedTNT.php b/src/pocketmine/entity/PrimedTNT.php index a098909c4..c03ef1772 100644 --- a/src/pocketmine/entity/PrimedTNT.php +++ b/src/pocketmine/entity/PrimedTNT.php @@ -25,7 +25,7 @@ use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\ExplosionPrimeEvent; use pocketmine\level\Explosion; use pocketmine\nbt\tag\ByteTag; -use pocketmine\network\protocol\AddEntityPacket; +use pocketmine\network\mcpe\protocol\AddEntityPacket; use pocketmine\Player; class PrimedTNT extends Entity implements Explosive{ diff --git a/src/pocketmine/entity/Snowball.php b/src/pocketmine/entity/Snowball.php index cc9b1aabf..e7d4cc4a6 100644 --- a/src/pocketmine/entity/Snowball.php +++ b/src/pocketmine/entity/Snowball.php @@ -23,7 +23,7 @@ namespace pocketmine\entity; use pocketmine\level\Level; use pocketmine\nbt\tag\CompoundTag; -use pocketmine\network\protocol\AddEntityPacket; +use pocketmine\network\mcpe\protocol\AddEntityPacket; use pocketmine\Player; class Snowball extends Projectile{ diff --git a/src/pocketmine/entity/Squid.php b/src/pocketmine/entity/Squid.php index 433a660dc..50a398360 100644 --- a/src/pocketmine/entity/Squid.php +++ b/src/pocketmine/entity/Squid.php @@ -25,8 +25,8 @@ use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\item\Item as ItemItem; use pocketmine\math\Vector3; -use pocketmine\network\protocol\AddEntityPacket; -use pocketmine\network\protocol\EntityEventPacket; +use pocketmine\network\mcpe\protocol\AddEntityPacket; +use pocketmine\network\mcpe\protocol\EntityEventPacket; use pocketmine\Player; class Squid extends WaterAnimal implements Ageable{ @@ -43,8 +43,8 @@ class Squid extends WaterAnimal implements Ageable{ private $switchDirectionTicker = 0; public function initEntity(){ + $this->setMaxHealth(10); parent::initEntity(); - $this->setMaxHealth(5); } public function getName(){ diff --git a/src/pocketmine/entity/Villager.php b/src/pocketmine/entity/Villager.php index 50c0df314..dff059ef4 100644 --- a/src/pocketmine/entity/Villager.php +++ b/src/pocketmine/entity/Villager.php @@ -22,7 +22,7 @@ namespace pocketmine\entity; use pocketmine\nbt\tag\IntTag; -use pocketmine\network\protocol\AddEntityPacket; +use pocketmine\network\mcpe\protocol\AddEntityPacket; use pocketmine\Player; class Villager extends Creature implements NPC, Ageable{ diff --git a/src/pocketmine/entity/Zombie.php b/src/pocketmine/entity/Zombie.php index ea7ec1f5a..633631cdc 100644 --- a/src/pocketmine/entity/Zombie.php +++ b/src/pocketmine/entity/Zombie.php @@ -23,7 +23,7 @@ namespace pocketmine\entity; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\item\Item as ItemItem; -use pocketmine\network\protocol\AddEntityPacket; +use pocketmine\network\mcpe\protocol\AddEntityPacket; use pocketmine\Player; class Zombie extends Monster{ diff --git a/src/pocketmine/event/Timings.php b/src/pocketmine/event/Timings.php index 5052c848e..636ac1cba 100644 --- a/src/pocketmine/event/Timings.php +++ b/src/pocketmine/event/Timings.php @@ -22,7 +22,7 @@ namespace pocketmine\event; use pocketmine\entity\Entity; -use pocketmine\network\protocol\DataPacket; +use pocketmine\network\mcpe\protocol\DataPacket; use pocketmine\Player; use pocketmine\plugin\PluginManager; use pocketmine\scheduler\PluginTask; diff --git a/src/pocketmine/event/player/PlayerExhaustEvent.php b/src/pocketmine/event/player/PlayerExhaustEvent.php index 623b5fdf4..747988fb5 100644 --- a/src/pocketmine/event/player/PlayerExhaustEvent.php +++ b/src/pocketmine/event/player/PlayerExhaustEvent.php @@ -34,19 +34,21 @@ class PlayerExhaustEvent extends PlayerEvent implements Cancellable{ const CAUSE_HEALTH_REGEN = 4; const CAUSE_POTION = 5; const CAUSE_WALKING = 6; - const CAUSE_SNEAKING = 7; + const CAUSE_SPRINTING = 7; const CAUSE_SWIMMING = 8; - const CAUSE_JUMPING = 10; + const CAUSE_JUMPING = 9; + const CAUSE_SPRINT_JUMPING = 10; const CAUSE_CUSTOM = 11; - const CAUSE_FLAG_SPRINT = 0x10000; - /** @var float */ private $amount; + /** @var int */ + private $cause; public function __construct(Human $human, float $amount, int $cause){ $this->player = $human; $this->amount = $amount; + $this->cause = $cause; } /** @@ -63,4 +65,12 @@ class PlayerExhaustEvent extends PlayerEvent implements Cancellable{ public function setAmount(float $amount){ $this->amount = $amount; } + + /** + * Returns an int cause of the exhaustion - one of the constants at the top of this class. + * @return int + */ + public function getCause() : int{ + return $this->cause; + } } diff --git a/src/pocketmine/event/server/DataPacketReceiveEvent.php b/src/pocketmine/event/server/DataPacketReceiveEvent.php index 84a58e274..cc5b89360 100644 --- a/src/pocketmine/event/server/DataPacketReceiveEvent.php +++ b/src/pocketmine/event/server/DataPacketReceiveEvent.php @@ -22,7 +22,7 @@ namespace pocketmine\event\server; use pocketmine\event\Cancellable; -use pocketmine\network\protocol\DataPacket; +use pocketmine\network\mcpe\protocol\DataPacket; use pocketmine\Player; class DataPacketReceiveEvent extends ServerEvent implements Cancellable{ diff --git a/src/pocketmine/event/server/DataPacketSendEvent.php b/src/pocketmine/event/server/DataPacketSendEvent.php index b0b70b850..16848e499 100644 --- a/src/pocketmine/event/server/DataPacketSendEvent.php +++ b/src/pocketmine/event/server/DataPacketSendEvent.php @@ -22,7 +22,7 @@ namespace pocketmine\event\server; use pocketmine\event\Cancellable; -use pocketmine\network\protocol\DataPacket; +use pocketmine\network\mcpe\protocol\DataPacket; use pocketmine\Player; class DataPacketSendEvent extends ServerEvent implements Cancellable{ diff --git a/src/pocketmine/inventory/BaseInventory.php b/src/pocketmine/inventory/BaseInventory.php index 98781b5a5..4bd1d9431 100644 --- a/src/pocketmine/inventory/BaseInventory.php +++ b/src/pocketmine/inventory/BaseInventory.php @@ -25,8 +25,8 @@ use pocketmine\entity\Entity; use pocketmine\event\entity\EntityInventoryChangeEvent; use pocketmine\event\inventory\InventoryOpenEvent; use pocketmine\item\Item; -use pocketmine\network\protocol\ContainerSetContentPacket; -use pocketmine\network\protocol\ContainerSetSlotPacket; +use pocketmine\network\mcpe\protocol\ContainerSetContentPacket; +use pocketmine\network\mcpe\protocol\ContainerSetSlotPacket; use pocketmine\Player; use pocketmine\Server; diff --git a/src/pocketmine/inventory/ChestInventory.php b/src/pocketmine/inventory/ChestInventory.php index cc2839896..74cc9910d 100644 --- a/src/pocketmine/inventory/ChestInventory.php +++ b/src/pocketmine/inventory/ChestInventory.php @@ -22,7 +22,7 @@ namespace pocketmine\inventory; use pocketmine\level\Level; -use pocketmine\network\protocol\BlockEventPacket; +use pocketmine\network\mcpe\protocol\BlockEventPacket; use pocketmine\Player; use pocketmine\tile\Chest; diff --git a/src/pocketmine/inventory/ContainerInventory.php b/src/pocketmine/inventory/ContainerInventory.php index a11f6011f..e17c96964 100644 --- a/src/pocketmine/inventory/ContainerInventory.php +++ b/src/pocketmine/inventory/ContainerInventory.php @@ -22,8 +22,8 @@ namespace pocketmine\inventory; use pocketmine\math\Vector3; -use pocketmine\network\protocol\ContainerClosePacket; -use pocketmine\network\protocol\ContainerOpenPacket; +use pocketmine\network\mcpe\protocol\ContainerClosePacket; +use pocketmine\network\mcpe\protocol\ContainerOpenPacket; use pocketmine\Player; abstract class ContainerInventory extends BaseInventory{ diff --git a/src/pocketmine/inventory/CraftingManager.php b/src/pocketmine/inventory/CraftingManager.php index 65476f7a5..5ecbde9df 100644 --- a/src/pocketmine/inventory/CraftingManager.php +++ b/src/pocketmine/inventory/CraftingManager.php @@ -23,7 +23,7 @@ namespace pocketmine\inventory; use pocketmine\event\Timings; use pocketmine\item\Item; -use pocketmine\network\protocol\CraftingDataPacket; +use pocketmine\network\mcpe\protocol\CraftingDataPacket; use pocketmine\Server; use pocketmine\utils\Config; use pocketmine\utils\MainLogger; diff --git a/src/pocketmine/inventory/DoubleChestInventory.php b/src/pocketmine/inventory/DoubleChestInventory.php index a0a471916..deee9b58f 100644 --- a/src/pocketmine/inventory/DoubleChestInventory.php +++ b/src/pocketmine/inventory/DoubleChestInventory.php @@ -23,7 +23,7 @@ namespace pocketmine\inventory; use pocketmine\item\Item; use pocketmine\level\Level; -use pocketmine\network\protocol\BlockEventPacket; +use pocketmine\network\mcpe\protocol\BlockEventPacket; use pocketmine\Player; use pocketmine\tile\Chest; diff --git a/src/pocketmine/inventory/InventoryType.php b/src/pocketmine/inventory/InventoryType.php index 6177714fd..4b97db2e9 100644 --- a/src/pocketmine/inventory/InventoryType.php +++ b/src/pocketmine/inventory/InventoryType.php @@ -21,7 +21,7 @@ namespace pocketmine\inventory; -use pocketmine\network\protocol\types\InventoryNetworkIds; +use pocketmine\network\mcpe\protocol\types\WindowTypes; /** * Saves all the information regarding default inventory sizes and types @@ -63,15 +63,15 @@ class InventoryType{ //TODO: move network stuff out of here //TODO: move inventory data to json static::$default = [ - static::CHEST => new InventoryType(27, "Chest", InventoryNetworkIds::CONTAINER), - static::DOUBLE_CHEST => new InventoryType(27 + 27, "Double Chest", InventoryNetworkIds::CONTAINER), - static::PLAYER => new InventoryType(36 + 4, "Player", InventoryNetworkIds::INVENTORY), //36 CONTAINER, 4 ARMOR - static::CRAFTING => new InventoryType(5, "Crafting", InventoryNetworkIds::INVENTORY), //yes, the use of INVENTORY is intended! 4 CRAFTING slots, 1 RESULT - static::WORKBENCH => new InventoryType(10, "Crafting", InventoryNetworkIds::WORKBENCH), //9 CRAFTING slots, 1 RESULT - static::FURNACE => new InventoryType(3, "Furnace", InventoryNetworkIds::FURNACE), //2 INPUT, 1 OUTPUT - static::ENCHANT_TABLE => new InventoryType(2, "Enchant", InventoryNetworkIds::ENCHANTMENT), //1 INPUT/OUTPUT, 1 LAPIS - static::BREWING_STAND => new InventoryType(4, "Brewing", InventoryNetworkIds::BREWING_STAND), //1 INPUT, 3 POTION - static::ANVIL => new InventoryType(3, "Anvil", InventoryNetworkIds::ANVIL) //2 INPUT, 1 OUTP + static::CHEST => new InventoryType(27, "Chest", WindowTypes::CONTAINER), + static::DOUBLE_CHEST => new InventoryType(27 + 27, "Double Chest", WindowTypes::CONTAINER), + static::PLAYER => new InventoryType(36 + 4, "Player", WindowTypes::INVENTORY), //36 CONTAINER, 4 ARMOR + static::CRAFTING => new InventoryType(5, "Crafting", WindowTypes::INVENTORY), //yes, the use of INVENTORY is intended! 4 CRAFTING slots, 1 RESULT + static::WORKBENCH => new InventoryType(10, "Crafting", WindowTypes::WORKBENCH), //9 CRAFTING slots, 1 RESULT + static::FURNACE => new InventoryType(3, "Furnace", WindowTypes::FURNACE), //2 INPUT, 1 OUTPUT + static::ENCHANT_TABLE => new InventoryType(2, "Enchant", WindowTypes::ENCHANTMENT), //1 INPUT/OUTPUT, 1 LAPIS + static::BREWING_STAND => new InventoryType(4, "Brewing", WindowTypes::BREWING_STAND), //1 INPUT, 3 POTION + static::ANVIL => new InventoryType(3, "Anvil", WindowTypes::ANVIL) //2 INPUT, 1 OUTP ]; } diff --git a/src/pocketmine/inventory/PlayerInventory.php b/src/pocketmine/inventory/PlayerInventory.php index 2e1723304..ebc3f5862 100644 --- a/src/pocketmine/inventory/PlayerInventory.php +++ b/src/pocketmine/inventory/PlayerInventory.php @@ -26,10 +26,10 @@ use pocketmine\event\entity\EntityArmorChangeEvent; use pocketmine\event\entity\EntityInventoryChangeEvent; use pocketmine\event\player\PlayerItemHeldEvent; use pocketmine\item\Item; -use pocketmine\network\protocol\ContainerSetContentPacket; -use pocketmine\network\protocol\ContainerSetSlotPacket; -use pocketmine\network\protocol\MobArmorEquipmentPacket; -use pocketmine\network\protocol\MobEquipmentPacket; +use pocketmine\network\mcpe\protocol\ContainerSetContentPacket; +use pocketmine\network\mcpe\protocol\ContainerSetSlotPacket; +use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket; +use pocketmine\network\mcpe\protocol\MobEquipmentPacket; use pocketmine\Player; use pocketmine\Server; diff --git a/src/pocketmine/item/Food.php b/src/pocketmine/item/Food.php index 8fc692017..c9c97aa7c 100644 --- a/src/pocketmine/item/Food.php +++ b/src/pocketmine/item/Food.php @@ -24,7 +24,7 @@ namespace pocketmine\item; use pocketmine\entity\Entity; use pocketmine\entity\Human; use pocketmine\event\entity\EntityEatItemEvent; -use pocketmine\network\protocol\EntityEventPacket; +use pocketmine\network\mcpe\protocol\EntityEventPacket; use pocketmine\Player; abstract class Food extends Item implements FoodSource{ diff --git a/src/pocketmine/item/Item.php b/src/pocketmine/item/Item.php index 7b49f0f6e..2305a836c 100644 --- a/src/pocketmine/item/Item.php +++ b/src/pocketmine/item/Item.php @@ -670,6 +670,35 @@ class Item implements ItemIds, \JsonSerializable{ return $this; } + public function getLore() : array{ + $tag = $this->getNamedTagEntry("display"); + if($tag instanceof CompoundTag and isset($tag->Lore) and $tag->Lore instanceof ListTag){ + $lines = []; + foreach($tag->Lore->getValue() as $line){ + $lines[] = $line->getValue(); + } + + return $lines; + } + + return []; + } + + public function setLore(array $lines){ + $tag = $this->getNamedTag() ?? new CompoundTag("", []); + if(!isset($tag->display)){ + $tag->display = new CompoundTag("display", []); + } + $tag->display->Lore = new ListTag("Lore"); + $tag->display->Lore->setTagType(NBT::TAG_String); + $count = 0; + foreach($lines as $line){ + $tag->display->Lore[$count++] = new StringTag("", $line); + } + + $this->setNamedTag($tag); + } + /** * @param $name * @return Tag|null diff --git a/src/pocketmine/level/Explosion.php b/src/pocketmine/level/Explosion.php index 693a2e2e0..0fe5b3e6b 100644 --- a/src/pocketmine/level/Explosion.php +++ b/src/pocketmine/level/Explosion.php @@ -38,7 +38,7 @@ use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\DoubleTag; use pocketmine\nbt\tag\FloatTag; use pocketmine\nbt\tag\ListTag; -use pocketmine\network\protocol\ExplodePacket; +use pocketmine\network\mcpe\protocol\ExplodePacket; use pocketmine\utils\Random; class Explosion{ diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index a8677ea12..37b272d49 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -90,14 +90,14 @@ use pocketmine\nbt\tag\FloatTag; use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ShortTag; use pocketmine\nbt\tag\StringTag; -use pocketmine\network\protocol\BatchPacket; -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; -use pocketmine\network\protocol\SetTimePacket; -use pocketmine\network\protocol\UpdateBlockPacket; +use pocketmine\network\mcpe\protocol\BatchPacket; +use pocketmine\network\mcpe\protocol\DataPacket; +use pocketmine\network\mcpe\protocol\FullChunkDataPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\MoveEntityPacket; +use pocketmine\network\mcpe\protocol\SetEntityMotionPacket; +use pocketmine\network\mcpe\protocol\SetTimePacket; +use pocketmine\network\mcpe\protocol\UpdateBlockPacket; use pocketmine\Player; use pocketmine\plugin\Plugin; use pocketmine\Server; @@ -134,9 +134,6 @@ class Level implements ChunkManager, Metadatable{ /** @var Tile[] */ private $tiles = []; - private $motionToSend = []; - private $moveToSend = []; - /** @var Player[] */ private $players = []; @@ -425,7 +422,9 @@ class Level implements ChunkManager, Metadatable{ } public function close(){ - assert(!$this->closed, "Tried to close a level which is already closed"); + if($this->closed){ + throw new \InvalidStateException("Tried to close a level which is already closed"); + } if($this->getAutoSave()){ $this->save(); @@ -652,6 +651,9 @@ class Level implements ChunkManager, Metadatable{ * */ public function doTick(int $currentTick){ + if($this->closed){ + throw new \InvalidStateException("Attempted to tick a Level which has been closed"); + } $this->timings->doTick->startTiming(); @@ -742,35 +744,6 @@ class Level implements ChunkManager, Metadatable{ $this->checkSleep(); } - foreach($this->moveToSend as $index => $entry){ - Level::getXZ($index, $chunkX, $chunkZ); - foreach($entry as $e){ - $pk = new MoveEntityPacket(); - $pk->eid = $e[0]; - $pk->x = $e[1]; - $pk->y = $e[2]; - $pk->z = $e[3]; - $pk->yaw = $e[4]; - $pk->headYaw = $e[5]; - $pk->pitch = $e[6]; - $this->addChunkPacket($chunkX, $chunkZ, $pk); - } - } - $this->moveToSend = []; - - foreach($this->motionToSend as $index => $entry){ - Level::getXZ($index, $chunkX, $chunkZ); - foreach($entry as $entity){ - $pk = new SetEntityMotionPacket(); - $pk->eid = $entity[0]; - $pk->motionX = $entity[1]; - $pk->motionY = $entity[2]; - $pk->motionZ = $entity[3]; - $this->addChunkPacket($chunkX, $chunkZ, $pk); - } - } - $this->motionToSend = []; - foreach($this->chunkPackets as $index => $entries){ Level::getXZ($index, $chunkX, $chunkZ); $chunkPlayers = $this->getChunkPlayers($chunkX, $chunkZ); @@ -1597,6 +1570,25 @@ class Level implements ChunkManager, Metadatable{ $ev->setCancelled(); } } + + if($player->isAdventure(true) and !$ev->isCancelled()){ + $tag = $item->getNamedTagEntry("CanDestroy"); + $canBreak = false; + if($tag instanceof ListTag){ + foreach($tag as $v){ + if($v instanceof StringTag){ + $entry = Item::fromString($v->getValue()); + if($entry->getId() > 0 and $entry->getBlock() !== null and $entry->getBlock()->getId() === $target->getId()){ + $canBreak = true; + break; + } + } + } + } + + $ev->setCancelled(!$canBreak); + } + $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled()){ return false; @@ -1608,8 +1600,8 @@ class Level implements ChunkManager, Metadatable{ $breakTime = 3; } - if($player->hasEffect(Effect::SWIFTNESS)){ - $breakTime *= 1 - (0.2 * ($player->getEffect(Effect::SWIFTNESS)->getAmplifier() + 1)); + if($player->hasEffect(Effect::HASTE)){ + $breakTime *= 1 - (0.2 * ($player->getEffect(Effect::HASTE)->getAmplifier() + 1)); } if($player->hasEffect(Effect::MINING_FATIGUE)){ @@ -1642,24 +1634,6 @@ class Level implements ChunkManager, Metadatable{ } } - $tag = $item->getNamedTagEntry("CanDestroy"); - if($tag instanceof ListTag){ - $canBreak = false; - foreach($tag as $v){ - if($v instanceof StringTag){ - $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){ $this->addParticle(new DestroyBlockParticle($target->add(0.5, 0.5, 0.5), $target)); } @@ -1734,6 +1708,25 @@ class Level implements ChunkManager, Metadatable{ $ev->setCancelled(); } } + + if($player->isAdventure(true) and !$ev->isCancelled()){ + $canPlace = false; + $tag = $item->getNamedTagEntry("CanPlaceOn"); + if($tag instanceof ListTag){ + foreach($tag as $v){ + if($v instanceof StringTag){ + $entry = Item::fromString($v->getValue()); + if($entry->getId() > 0 and $entry->getBlock() !== null and $entry->getBlock()->getId() === $target->getId()){ + $canPlace = true; + break; + } + } + } + } + + $ev->setCancelled(!$canPlace); + } + $this->server->getPluginManager()->callEvent($ev); if(!$ev->isCancelled()){ $target->onUpdate(self::BLOCK_UPDATE_TOUCH); @@ -1796,24 +1789,6 @@ class Level implements ChunkManager, Metadatable{ } } - $tag = $item->getNamedTagEntry("CanPlaceOn"); - if($tag instanceof ListTag){ - $canPlace = false; - foreach($tag as $v){ - if($v instanceof StringTag){ - $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); @@ -2255,18 +2230,20 @@ class Level implements ChunkManager, Metadatable{ $oldEntities = $oldChunk !== null ? $oldChunk->getEntities() : []; $oldTiles = $oldChunk !== null ? $oldChunk->getTiles() : []; - $this->provider->setChunk($chunkX, $chunkZ, $chunk); - $this->chunks[$index] = $chunk; - foreach($oldEntities as $entity){ $chunk->addEntity($entity); + $oldChunk->removeEntity($entity); $entity->chunk = $chunk; } foreach($oldTiles as $tile){ $chunk->addTile($tile); + $oldChunk->removeTile($tile); $tile->chunk = $chunk; } + + $this->provider->setChunk($chunkX, $chunkZ, $chunk); + $this->chunks[$index] = $chunk; } unset($this->chunkCache[$index]); @@ -2909,16 +2886,23 @@ class Level implements ChunkManager, Metadatable{ } public function addEntityMotion(int $chunkX, int $chunkZ, int $entityId, float $x, float $y, float $z){ - if(!isset($this->motionToSend[$index = Level::chunkHash($chunkX, $chunkZ)])){ - $this->motionToSend[$index] = []; - } - $this->motionToSend[$index][$entityId] = [$entityId, $x, $y, $z]; + $pk = new SetEntityMotionPacket(); + $pk->eid = $entityId; + $pk->motionX = $x; + $pk->motionY = $y; + $pk->motionZ = $z; + $this->addChunkPacket($chunkX, $chunkZ, $pk); } public function addEntityMovement(int $chunkX, int $chunkZ, int $entityId, float $x, float $y, float $z, float $yaw, float $pitch, $headYaw = null){ - if(!isset($this->moveToSend[$index = Level::chunkHash($chunkX, $chunkZ)])){ - $this->moveToSend[$index] = []; - } - $this->moveToSend[$index][$entityId] = [$entityId, $x, $y, $z, $yaw, $headYaw === null ? $yaw : $headYaw, $pitch]; + $pk = new MoveEntityPacket(); + $pk->eid = $entityId; + $pk->x = $x; + $pk->y = $y; + $pk->z = $z; + $pk->yaw = $yaw; + $pk->pitch = $pitch; + $pk->headYaw = $headYaw ?? $yaw; + $this->addChunkPacket($chunkX, $chunkZ, $pk); } } diff --git a/src/pocketmine/level/format/Chunk.php b/src/pocketmine/level/format/Chunk.php index 9f47296cf..636ad41a2 100644 --- a/src/pocketmine/level/format/Chunk.php +++ b/src/pocketmine/level/format/Chunk.php @@ -568,6 +568,9 @@ class Chunk{ * @param Entity $entity */ public function addEntity(Entity $entity){ + if($entity->closed){ + throw new \InvalidArgumentException("Attempted to add a garbage closed Entity to a chunk"); + } $this->entities[$entity->getId()] = $entity; if(!($entity instanceof Player) and $this->isInit){ $this->hasChanged = true; @@ -588,6 +591,9 @@ class Chunk{ * @param Tile $tile */ public function addTile(Tile $tile){ + if($tile->closed){ + throw new \InvalidArgumentException("Attempted to add a garbage closed Tile to a chunk"); + } $this->tiles[$tile->getId()] = $tile; if(isset($this->tileList[$index = (($tile->x & 0x0f) << 12) | (($tile->z & 0x0f) << 8) | ($tile->y & 0xff)]) and $this->tileList[$index] !== $tile){ $this->tileList[$index]->close(); @@ -691,9 +697,16 @@ class Chunk{ continue; //Fixes entities allocated in wrong chunks. } - if(($entity = Entity::createEntity($nbt["id"], $level, $nbt)) instanceof Entity){ - $entity->spawnToAll(); - }else{ + try{ + $entity = Entity::createEntity($nbt["id"], $level, $nbt); + if($entity instanceof Entity){ + $entity->spawnToAll(); + }else{ + $changed = true; + continue; + } + }catch(\Throwable $t){ + $level->getServer()->getLogger()->logException($t); $changed = true; continue; } diff --git a/src/pocketmine/level/format/io/leveldb/LevelDB.php b/src/pocketmine/level/format/io/leveldb/LevelDB.php index 1107d8b02..67a1eebbe 100644 --- a/src/pocketmine/level/format/io/leveldb/LevelDB.php +++ b/src/pocketmine/level/format/io/leveldb/LevelDB.php @@ -22,18 +22,18 @@ namespace pocketmine\level\format\io\leveldb; use pocketmine\level\format\Chunk; -use pocketmine\level\format\SubChunk; use pocketmine\level\format\io\BaseLevelProvider; use pocketmine\level\format\io\ChunkUtils; -use pocketmine\level\generator\Generator; +use pocketmine\level\format\SubChunk; use pocketmine\level\generator\Flat; +use pocketmine\level\generator\Generator; use pocketmine\level\Level; use pocketmine\level\LevelException; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\{ ByteTag, CompoundTag, FloatTag, IntTag, LongTag, StringTag }; -use pocketmine\network\protocol\Info as ProtocolInfo; +use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\utils\Binary; use pocketmine\utils\MainLogger; diff --git a/src/pocketmine/level/particle/DestroyBlockParticle.php b/src/pocketmine/level/particle/DestroyBlockParticle.php index 783159c27..5ced4462f 100644 --- a/src/pocketmine/level/particle/DestroyBlockParticle.php +++ b/src/pocketmine/level/particle/DestroyBlockParticle.php @@ -23,7 +23,7 @@ namespace pocketmine\level\particle; use pocketmine\block\Block; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class DestroyBlockParticle extends Particle{ diff --git a/src/pocketmine/level/particle/FloatingTextParticle.php b/src/pocketmine/level/particle/FloatingTextParticle.php index d42e2735f..9ef93bc50 100644 --- a/src/pocketmine/level/particle/FloatingTextParticle.php +++ b/src/pocketmine/level/particle/FloatingTextParticle.php @@ -24,8 +24,8 @@ namespace pocketmine\level\particle; use pocketmine\entity\Entity; use pocketmine\entity\Item as ItemEntity; use pocketmine\math\Vector3; -use pocketmine\network\protocol\AddEntityPacket; -use pocketmine\network\protocol\RemoveEntityPacket; +use pocketmine\network\mcpe\protocol\AddEntityPacket; +use pocketmine\network\mcpe\protocol\RemoveEntityPacket; class FloatingTextParticle extends Particle{ //TODO: HACK! diff --git a/src/pocketmine/level/particle/GenericParticle.php b/src/pocketmine/level/particle/GenericParticle.php index 657e03475..698d42bb1 100644 --- a/src/pocketmine/level/particle/GenericParticle.php +++ b/src/pocketmine/level/particle/GenericParticle.php @@ -22,7 +22,7 @@ namespace pocketmine\level\particle; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class GenericParticle extends Particle{ diff --git a/src/pocketmine/level/particle/MobSpawnParticle.php b/src/pocketmine/level/particle/MobSpawnParticle.php index 0b5001158..98527129a 100644 --- a/src/pocketmine/level/particle/MobSpawnParticle.php +++ b/src/pocketmine/level/particle/MobSpawnParticle.php @@ -22,7 +22,7 @@ namespace pocketmine\level\particle; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class MobSpawnParticle extends Particle{ diff --git a/src/pocketmine/level/particle/Particle.php b/src/pocketmine/level/particle/Particle.php index a0a8e8016..b95817990 100644 --- a/src/pocketmine/level/particle/Particle.php +++ b/src/pocketmine/level/particle/Particle.php @@ -22,7 +22,7 @@ namespace pocketmine\level\particle; use pocketmine\math\Vector3; -use pocketmine\network\protocol\DataPacket; +use pocketmine\network\mcpe\protocol\DataPacket; abstract class Particle extends Vector3{ diff --git a/src/pocketmine/level/sound/AnvilBreakSound.php b/src/pocketmine/level/sound/AnvilBreakSound.php index 7418976cb..246c44c52 100644 --- a/src/pocketmine/level/sound/AnvilBreakSound.php +++ b/src/pocketmine/level/sound/AnvilBreakSound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class AnvilBreakSound extends GenericSound{ public function __construct(Vector3 $pos, $pitch = 0){ diff --git a/src/pocketmine/level/sound/AnvilFallSound.php b/src/pocketmine/level/sound/AnvilFallSound.php index c5b901495..f84b50e8d 100644 --- a/src/pocketmine/level/sound/AnvilFallSound.php +++ b/src/pocketmine/level/sound/AnvilFallSound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class AnvilFallSound extends GenericSound{ public function __construct(Vector3 $pos, $pitch = 0){ diff --git a/src/pocketmine/level/sound/AnvilUseSound.php b/src/pocketmine/level/sound/AnvilUseSound.php index 8be6d62ef..024ad017b 100644 --- a/src/pocketmine/level/sound/AnvilUseSound.php +++ b/src/pocketmine/level/sound/AnvilUseSound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class AnvilUseSound extends GenericSound{ public function __construct(Vector3 $pos, $pitch = 0){ diff --git a/src/pocketmine/level/sound/BlazeShootSound.php b/src/pocketmine/level/sound/BlazeShootSound.php index e020230fd..68d15dd93 100644 --- a/src/pocketmine/level/sound/BlazeShootSound.php +++ b/src/pocketmine/level/sound/BlazeShootSound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class BlazeShootSound extends GenericSound{ public function __construct(Vector3 $pos, $pitch = 0){ diff --git a/src/pocketmine/level/sound/ClickSound.php b/src/pocketmine/level/sound/ClickSound.php index 993d9eefd..3261fe9b0 100644 --- a/src/pocketmine/level/sound/ClickSound.php +++ b/src/pocketmine/level/sound/ClickSound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class ClickSound extends GenericSound{ public function __construct(Vector3 $pos, $pitch = 0){ diff --git a/src/pocketmine/level/sound/DoorBumpSound.php b/src/pocketmine/level/sound/DoorBumpSound.php index 8b0146791..0278ad4d5 100644 --- a/src/pocketmine/level/sound/DoorBumpSound.php +++ b/src/pocketmine/level/sound/DoorBumpSound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class DoorBumpSound extends GenericSound{ public function __construct(Vector3 $pos, $pitch = 0){ diff --git a/src/pocketmine/level/sound/DoorCrashSound.php b/src/pocketmine/level/sound/DoorCrashSound.php index 9468d9e28..2fc713f46 100644 --- a/src/pocketmine/level/sound/DoorCrashSound.php +++ b/src/pocketmine/level/sound/DoorCrashSound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class DoorCrashSound extends GenericSound{ public function __construct(Vector3 $pos, $pitch = 0){ diff --git a/src/pocketmine/level/sound/DoorSound.php b/src/pocketmine/level/sound/DoorSound.php index e6361f3ee..486d371e2 100644 --- a/src/pocketmine/level/sound/DoorSound.php +++ b/src/pocketmine/level/sound/DoorSound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class DoorSound extends GenericSound{ public function __construct(Vector3 $pos, $pitch = 0){ diff --git a/src/pocketmine/level/sound/EndermanTeleportSound.php b/src/pocketmine/level/sound/EndermanTeleportSound.php index c4e43130e..98f1b2e94 100644 --- a/src/pocketmine/level/sound/EndermanTeleportSound.php +++ b/src/pocketmine/level/sound/EndermanTeleportSound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class EndermanTeleportSound extends GenericSound{ public function __construct(Vector3 $pos){ diff --git a/src/pocketmine/level/sound/FizzSound.php b/src/pocketmine/level/sound/FizzSound.php index 241f0c7a0..32fd9a004 100644 --- a/src/pocketmine/level/sound/FizzSound.php +++ b/src/pocketmine/level/sound/FizzSound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class FizzSound extends GenericSound{ public function __construct(Vector3 $pos, $pitch = 0){ diff --git a/src/pocketmine/level/sound/GenericSound.php b/src/pocketmine/level/sound/GenericSound.php index e9f5fdcf2..f3e399cf1 100644 --- a/src/pocketmine/level/sound/GenericSound.php +++ b/src/pocketmine/level/sound/GenericSound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class GenericSound extends Sound{ diff --git a/src/pocketmine/level/sound/GhastShootSound.php b/src/pocketmine/level/sound/GhastShootSound.php index 215a0c178..d7dd0581f 100644 --- a/src/pocketmine/level/sound/GhastShootSound.php +++ b/src/pocketmine/level/sound/GhastShootSound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class GhastShootSound extends GenericSound{ public function __construct(Vector3 $pos, $pitch = 0){ diff --git a/src/pocketmine/level/sound/GhastSound.php b/src/pocketmine/level/sound/GhastSound.php index 46ad47783..0178c5302 100644 --- a/src/pocketmine/level/sound/GhastSound.php +++ b/src/pocketmine/level/sound/GhastSound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class GhastSound extends GenericSound{ public function __construct(Vector3 $pos, $pitch = 0){ diff --git a/src/pocketmine/level/sound/LaunchSound.php b/src/pocketmine/level/sound/LaunchSound.php index 4240f5c4c..3245c2da4 100644 --- a/src/pocketmine/level/sound/LaunchSound.php +++ b/src/pocketmine/level/sound/LaunchSound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class LaunchSound extends GenericSound{ public function __construct(Vector3 $pos, $pitch = 0){ diff --git a/src/pocketmine/level/sound/PopSound.php b/src/pocketmine/level/sound/PopSound.php index 6adf740ea..b291e56fd 100644 --- a/src/pocketmine/level/sound/PopSound.php +++ b/src/pocketmine/level/sound/PopSound.php @@ -22,10 +22,10 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; class PopSound extends GenericSound{ public function __construct(Vector3 $pos, $pitch = 0){ - parent::__construct($pos, LevelEventPacket::EVENT_SOUND_CLICK_FAIL, $pitch); + parent::__construct($pos, LevelEventPacket::EVENT_SOUND_POP, $pitch); } } diff --git a/src/pocketmine/level/sound/Sound.php b/src/pocketmine/level/sound/Sound.php index 9ec5ec4e5..2e84ad113 100644 --- a/src/pocketmine/level/sound/Sound.php +++ b/src/pocketmine/level/sound/Sound.php @@ -22,7 +22,7 @@ namespace pocketmine\level\sound; use pocketmine\math\Vector3; -use pocketmine\network\protocol\DataPacket; +use pocketmine\network\mcpe\protocol\DataPacket; abstract class Sound extends Vector3{ diff --git a/src/pocketmine/network/Network.php b/src/pocketmine/network/Network.php index 88960e56c..1c3336269 100644 --- a/src/pocketmine/network/Network.php +++ b/src/pocketmine/network/Network.php @@ -24,79 +24,91 @@ */ namespace pocketmine\network; -use pocketmine\network\protocol\AddEntityPacket; -use pocketmine\network\protocol\AddHangingEntityPacket; -use pocketmine\network\protocol\AddItemEntityPacket; -use pocketmine\network\protocol\AddItemPacket; -use pocketmine\network\protocol\AddPaintingPacket; -use pocketmine\network\protocol\AddPlayerPacket; -use pocketmine\network\protocol\AdventureSettingsPacket; -use pocketmine\network\protocol\AnimatePacket; -use pocketmine\network\protocol\AvailableCommandsPacket; -use pocketmine\network\protocol\BatchPacket; -use pocketmine\network\protocol\BlockEntityDataPacket; -use pocketmine\network\protocol\BlockEventPacket; -use pocketmine\network\protocol\ChangeDimensionPacket; -use pocketmine\network\protocol\ChunkRadiusUpdatedPacket; -use pocketmine\network\protocol\CommandStepPacket; -use pocketmine\network\protocol\ContainerClosePacket; -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\DisconnectPacket; -use pocketmine\network\protocol\DropItemPacket; -use pocketmine\network\protocol\EntityEventPacket; -use pocketmine\network\protocol\ExplodePacket; -use pocketmine\network\protocol\FullChunkDataPacket; -use pocketmine\network\protocol\HurtArmorPacket; -use pocketmine\network\protocol\Info; -use pocketmine\network\protocol\Info as ProtocolInfo; -use pocketmine\network\protocol\InteractPacket; -use pocketmine\network\protocol\InventoryActionPacket; -use pocketmine\network\protocol\ItemFrameDropItemPacket; -use pocketmine\network\protocol\LevelEventPacket; -use pocketmine\network\protocol\LevelSoundEventPacket; -use pocketmine\network\protocol\LoginPacket; -use pocketmine\network\protocol\MobArmorEquipmentPacket; -use pocketmine\network\protocol\MobEquipmentPacket; -use pocketmine\network\protocol\MoveEntityPacket; -use pocketmine\network\protocol\MovePlayerPacket; -use pocketmine\network\protocol\PlayerActionPacket; -use pocketmine\network\protocol\PlayerFallPacket; -use pocketmine\network\protocol\PlayerInputPacket; -use pocketmine\network\protocol\PlayerListPacket; -use pocketmine\network\protocol\PlayStatusPacket; -use pocketmine\network\protocol\RemoveBlockPacket; -use pocketmine\network\protocol\RemoveEntityPacket; -use pocketmine\network\protocol\ReplaceItemInSlotPacket; -use pocketmine\network\protocol\RequestChunkRadiusPacket; -use pocketmine\network\protocol\ResourcePackClientResponsePacket; -use pocketmine\network\protocol\ResourcePacksInfoPacket; -use pocketmine\network\protocol\RespawnPacket; -use pocketmine\network\protocol\SetCommandsEnabledPacket; -use pocketmine\network\protocol\SetDifficultyPacket; -use pocketmine\network\protocol\SetEntityDataPacket; -use pocketmine\network\protocol\SetEntityLinkPacket; -use pocketmine\network\protocol\SetEntityMotionPacket; -use pocketmine\network\protocol\SetHealthPacket; -use pocketmine\network\protocol\SetPlayerGameTypePacket; -use pocketmine\network\protocol\SetSpawnPositionPacket; -use pocketmine\network\protocol\SetTimePacket; -use pocketmine\network\protocol\SpawnExperienceOrbPacket; -use pocketmine\network\protocol\StartGamePacket; -use pocketmine\network\protocol\TakeItemEntityPacket; -use pocketmine\network\protocol\TextPacket; -use pocketmine\network\protocol\TransferPacket; -use pocketmine\network\protocol\UpdateBlockPacket; -use pocketmine\network\protocol\UpdateTradePacket; -use pocketmine\network\protocol\UseItemPacket; -use pocketmine\Player; +use pocketmine\network\mcpe\protocol\AddEntityPacket; +use pocketmine\network\mcpe\protocol\AddHangingEntityPacket; +use pocketmine\network\mcpe\protocol\AddItemEntityPacket; +use pocketmine\network\mcpe\protocol\AddItemPacket; +use pocketmine\network\mcpe\protocol\AddPaintingPacket; +use pocketmine\network\mcpe\protocol\AddPlayerPacket; +use pocketmine\network\mcpe\protocol\AdventureSettingsPacket; +use pocketmine\network\mcpe\protocol\AnimatePacket; +use pocketmine\network\mcpe\protocol\AvailableCommandsPacket; +use pocketmine\network\mcpe\protocol\BatchPacket; +use pocketmine\network\mcpe\protocol\BlockEntityDataPacket; +use pocketmine\network\mcpe\protocol\BlockEventPacket; +use pocketmine\network\mcpe\protocol\BlockPickRequestPacket; +use pocketmine\network\mcpe\protocol\ChangeDimensionPacket; +use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket; +use pocketmine\network\mcpe\protocol\ClientboundMapItemDataPacket; +use pocketmine\network\mcpe\protocol\ClientToServerHandshakePacket; +use pocketmine\network\mcpe\protocol\CommandBlockUpdatePacket; +use pocketmine\network\mcpe\protocol\CommandStepPacket; +use pocketmine\network\mcpe\protocol\ContainerClosePacket; +use pocketmine\network\mcpe\protocol\ContainerOpenPacket; +use pocketmine\network\mcpe\protocol\ContainerSetContentPacket; +use pocketmine\network\mcpe\protocol\ContainerSetDataPacket; +use pocketmine\network\mcpe\protocol\ContainerSetSlotPacket; +use pocketmine\network\mcpe\protocol\CraftingDataPacket; +use pocketmine\network\mcpe\protocol\CraftingEventPacket; +use pocketmine\network\mcpe\protocol\DataPacket; +use pocketmine\network\mcpe\protocol\DisconnectPacket; +use pocketmine\network\mcpe\protocol\DropItemPacket; +use pocketmine\network\mcpe\protocol\EntityEventPacket; +use pocketmine\network\mcpe\protocol\ExplodePacket; +use pocketmine\network\mcpe\protocol\FullChunkDataPacket; +use pocketmine\network\mcpe\protocol\HurtArmorPacket; +use pocketmine\network\mcpe\protocol\InteractPacket; +use pocketmine\network\mcpe\protocol\InventoryActionPacket; +use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket; +use pocketmine\network\mcpe\protocol\LevelEventPacket; +use pocketmine\network\mcpe\protocol\LevelSoundEventPacket; +use pocketmine\network\mcpe\protocol\LoginPacket; +use pocketmine\network\mcpe\protocol\MapInfoRequestPacket; +use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket; +use pocketmine\network\mcpe\protocol\MobEquipmentPacket; +use pocketmine\network\mcpe\protocol\MoveEntityPacket; +use pocketmine\network\mcpe\protocol\MovePlayerPacket; +use pocketmine\network\mcpe\protocol\PlayerActionPacket; +use pocketmine\network\mcpe\protocol\PlayerFallPacket; +use pocketmine\network\mcpe\protocol\PlayerInputPacket; +use pocketmine\network\mcpe\protocol\PlayerListPacket; +use pocketmine\network\mcpe\protocol\PlaySoundPacket; +use pocketmine\network\mcpe\protocol\PlayStatusPacket; +use pocketmine\network\mcpe\protocol\ProtocolInfo; +use pocketmine\network\mcpe\protocol\RemoveBlockPacket; +use pocketmine\network\mcpe\protocol\RemoveEntityPacket; +use pocketmine\network\mcpe\protocol\ReplaceItemInSlotPacket; +use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket; +use pocketmine\network\mcpe\protocol\ResourcePackChunkDataPacket; +use pocketmine\network\mcpe\protocol\ResourcePackChunkRequestPacket; +use pocketmine\network\mcpe\protocol\ResourcePackClientResponsePacket; +use pocketmine\network\mcpe\protocol\ResourcePackDataInfoPacket; +use pocketmine\network\mcpe\protocol\ResourcePacksInfoPacket; +use pocketmine\network\mcpe\protocol\RespawnPacket; +use pocketmine\network\mcpe\protocol\RiderJumpPacket; +use pocketmine\network\mcpe\protocol\ServerToClientHandshakePacket; +use pocketmine\network\mcpe\protocol\SetCommandsEnabledPacket; +use pocketmine\network\mcpe\protocol\SetDifficultyPacket; +use pocketmine\network\mcpe\protocol\SetEntityDataPacket; +use pocketmine\network\mcpe\protocol\SetEntityLinkPacket; +use pocketmine\network\mcpe\protocol\SetEntityMotionPacket; +use pocketmine\network\mcpe\protocol\SetHealthPacket; +use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket; +use pocketmine\network\mcpe\protocol\SetSpawnPositionPacket; +use pocketmine\network\mcpe\protocol\SetTimePacket; +use pocketmine\network\mcpe\protocol\SetTitlePacket; +use pocketmine\network\mcpe\protocol\ShowCreditsPacket; +use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket; +use pocketmine\network\mcpe\protocol\StartGamePacket; +use pocketmine\network\mcpe\protocol\StopSoundPacket; +use pocketmine\network\mcpe\protocol\TakeItemEntityPacket; +use pocketmine\network\mcpe\protocol\TextPacket; +use pocketmine\network\mcpe\protocol\TransferPacket; +use pocketmine\network\mcpe\protocol\UnknownPacket; +use pocketmine\network\mcpe\protocol\UpdateBlockPacket; +use pocketmine\network\mcpe\protocol\UpdateTradePacket; +use pocketmine\network\mcpe\protocol\UseItemPacket; use pocketmine\Server; -use pocketmine\utils\BinaryStream; class Network{ @@ -223,46 +235,6 @@ class Network{ return $this->server; } - public function processBatch(BatchPacket $packet, Player $p){ - try{ - if(strlen($packet->payload) === 0){ - //prevent zlib_decode errors for incorrectly-decoded packets - throw new \InvalidArgumentException("BatchPacket payload is empty or packet decode error"); - } - - $str = zlib_decode($packet->payload, 1024 * 1024 * 64); //Max 64MB - $len = strlen($str); - - if($len === 0){ - throw new \InvalidStateException("Decoded BatchPacket payload is empty"); - } - - $stream = new BinaryStream($str); - - while($stream->offset < $len){ - $buf = $stream->getString(); - - if(($pk = $this->getPacket(ord($buf{0}))) !== null){ - if($pk::NETWORK_ID === Info::BATCH_PACKET){ - throw new \InvalidStateException("Invalid BatchPacket inside BatchPacket"); - } - - $pk->setBuffer($buf, 1); - - $pk->decode(); - assert($pk->feof(), "Still " . strlen(substr($pk->buffer, $pk->offset)) . " bytes unread in " . get_class($pk)); - $p->handleDataPacket($pk); - } - } - }catch(\Throwable $e){ - if(\pocketmine\DEBUG > 1){ - $logger = $this->server->getLogger(); - $logger->debug("BatchPacket " . " 0x" . bin2hex($packet->payload)); - $logger->logException($e); - } - } - } - /** * @param $id * @@ -274,7 +246,7 @@ class Network{ if($class !== null){ return clone $class; } - return null; + return new UnknownPacket(); } @@ -316,8 +288,12 @@ class Network{ $this->registerPacket(ProtocolInfo::BATCH_PACKET, BatchPacket::class); $this->registerPacket(ProtocolInfo::BLOCK_ENTITY_DATA_PACKET, BlockEntityDataPacket::class); $this->registerPacket(ProtocolInfo::BLOCK_EVENT_PACKET, BlockEventPacket::class); + $this->registerPacket(ProtocolInfo::BLOCK_PICK_REQUEST_PACKET, BlockPickRequestPacket::class); $this->registerPacket(ProtocolInfo::CHANGE_DIMENSION_PACKET, ChangeDimensionPacket::class); $this->registerPacket(ProtocolInfo::CHUNK_RADIUS_UPDATED_PACKET, ChunkRadiusUpdatedPacket::class); + $this->registerPacket(ProtocolInfo::CLIENTBOUND_MAP_ITEM_DATA_PACKET, ClientboundMapItemDataPacket::class); + $this->registerPacket(ProtocolInfo::CLIENT_TO_SERVER_HANDSHAKE_PACKET, ClientToServerHandshakePacket::class); + $this->registerPacket(ProtocolInfo::COMMAND_BLOCK_UPDATE_PACKET, CommandBlockUpdatePacket::class); $this->registerPacket(ProtocolInfo::COMMAND_STEP_PACKET, CommandStepPacket::class); $this->registerPacket(ProtocolInfo::CONTAINER_CLOSE_PACKET, ContainerClosePacket::class); $this->registerPacket(ProtocolInfo::CONTAINER_OPEN_PACKET, ContainerOpenPacket::class); @@ -338,6 +314,7 @@ class Network{ $this->registerPacket(ProtocolInfo::LEVEL_EVENT_PACKET, LevelEventPacket::class); $this->registerPacket(ProtocolInfo::LEVEL_SOUND_EVENT_PACKET, LevelSoundEventPacket::class); $this->registerPacket(ProtocolInfo::LOGIN_PACKET, LoginPacket::class); + $this->registerPacket(ProtocolInfo::MAP_INFO_REQUEST_PACKET, MapInfoRequestPacket::class); $this->registerPacket(ProtocolInfo::MOB_ARMOR_EQUIPMENT_PACKET, MobArmorEquipmentPacket::class); $this->registerPacket(ProtocolInfo::MOB_EQUIPMENT_PACKET, MobEquipmentPacket::class); $this->registerPacket(ProtocolInfo::MOVE_ENTITY_PACKET, MoveEntityPacket::class); @@ -346,14 +323,20 @@ class Network{ $this->registerPacket(ProtocolInfo::PLAYER_FALL_PACKET, PlayerFallPacket::class); $this->registerPacket(ProtocolInfo::PLAYER_INPUT_PACKET, PlayerInputPacket::class); $this->registerPacket(ProtocolInfo::PLAYER_LIST_PACKET, PlayerListPacket::class); + $this->registerPacket(ProtocolInfo::PLAY_SOUND_PACKET, PlaySoundPacket::class); $this->registerPacket(ProtocolInfo::PLAY_STATUS_PACKET, PlayStatusPacket::class); $this->registerPacket(ProtocolInfo::REMOVE_BLOCK_PACKET, RemoveBlockPacket::class); $this->registerPacket(ProtocolInfo::REMOVE_ENTITY_PACKET, RemoveEntityPacket::class); $this->registerPacket(ProtocolInfo::REPLACE_ITEM_IN_SLOT_PACKET, ReplaceItemInSlotPacket::class); $this->registerPacket(ProtocolInfo::REQUEST_CHUNK_RADIUS_PACKET, RequestChunkRadiusPacket::class); + $this->registerPacket(ProtocolInfo::RESOURCE_PACK_CHUNK_DATA_PACKET, ResourcePackChunkDataPacket::class); + $this->registerPacket(ProtocolInfo::RESOURCE_PACK_CHUNK_REQUEST_PACKET, ResourcePackChunkRequestPacket::class); $this->registerPacket(ProtocolInfo::RESOURCE_PACK_CLIENT_RESPONSE_PACKET, ResourcePackClientResponsePacket::class); + $this->registerPacket(ProtocolInfo::RESOURCE_PACK_DATA_INFO_PACKET, ResourcePackDataInfoPacket::class); $this->registerPacket(ProtocolInfo::RESOURCE_PACKS_INFO_PACKET, ResourcePacksInfoPacket::class); $this->registerPacket(ProtocolInfo::RESPAWN_PACKET, RespawnPacket::class); + $this->registerPacket(ProtocolInfo::RIDER_JUMP_PACKET, RiderJumpPacket::class); + $this->registerPacket(ProtocolInfo::SERVER_TO_CLIENT_HANDSHAKE_PACKET, ServerToClientHandshakePacket::class); $this->registerPacket(ProtocolInfo::SET_COMMANDS_ENABLED_PACKET, SetCommandsEnabledPacket::class); $this->registerPacket(ProtocolInfo::SET_DIFFICULTY_PACKET, SetDifficultyPacket::class); $this->registerPacket(ProtocolInfo::SET_ENTITY_DATA_PACKET, SetEntityDataPacket::class); @@ -363,8 +346,11 @@ class Network{ $this->registerPacket(ProtocolInfo::SET_PLAYER_GAME_TYPE_PACKET, SetPlayerGameTypePacket::class); $this->registerPacket(ProtocolInfo::SET_SPAWN_POSITION_PACKET, SetSpawnPositionPacket::class); $this->registerPacket(ProtocolInfo::SET_TIME_PACKET, SetTimePacket::class); + $this->registerPacket(ProtocolInfo::SET_TITLE_PACKET, SetTitlePacket::class); + $this->registerPacket(ProtocolInfo::SHOW_CREDITS_PACKET, ShowCreditsPacket::class); $this->registerPacket(ProtocolInfo::SPAWN_EXPERIENCE_ORB_PACKET, SpawnExperienceOrbPacket::class); $this->registerPacket(ProtocolInfo::START_GAME_PACKET, StartGamePacket::class); + $this->registerPacket(ProtocolInfo::STOP_SOUND_PACKET, StopSoundPacket::class); $this->registerPacket(ProtocolInfo::TAKE_ITEM_ENTITY_PACKET, TakeItemEntityPacket::class); $this->registerPacket(ProtocolInfo::TEXT_PACKET, TextPacket::class); $this->registerPacket(ProtocolInfo::TRANSFER_PACKET, TransferPacket::class); diff --git a/src/pocketmine/network/SourceInterface.php b/src/pocketmine/network/SourceInterface.php index f662992a0..492c5b60c 100644 --- a/src/pocketmine/network/SourceInterface.php +++ b/src/pocketmine/network/SourceInterface.php @@ -24,7 +24,7 @@ */ namespace pocketmine\network; -use pocketmine\network\protocol\DataPacket; +use pocketmine\network\mcpe\protocol\DataPacket; use pocketmine\Player; /** diff --git a/src/pocketmine/network/CachedEncapsulatedPacket.php b/src/pocketmine/network/mcpe/CachedEncapsulatedPacket.php similarity index 96% rename from src/pocketmine/network/CachedEncapsulatedPacket.php rename to src/pocketmine/network/mcpe/CachedEncapsulatedPacket.php index 0ddcff4b7..57545d57c 100644 --- a/src/pocketmine/network/CachedEncapsulatedPacket.php +++ b/src/pocketmine/network/mcpe/CachedEncapsulatedPacket.php @@ -19,7 +19,7 @@ * */ -namespace pocketmine\network; +namespace pocketmine\network\mcpe; use raklib\protocol\EncapsulatedPacket; diff --git a/src/pocketmine/network/mcpe/NetworkSession.php b/src/pocketmine/network/mcpe/NetworkSession.php new file mode 100644 index 000000000..1d1892e0c --- /dev/null +++ b/src/pocketmine/network/mcpe/NetworkSession.php @@ -0,0 +1,297 @@ +buffer !== ""){ $pk = $this->getPacket($packet->buffer); - if($pk !== null){ - $pk->decode(); - assert($pk->feof(), "Still " . strlen(substr($pk->buffer, $pk->offset)) . " bytes unread!"); - $this->players[$identifier]->handleDataPacket($pk); - } + $this->players[$identifier]->handleDataPacket($pk); } }catch(\Throwable $e){ if(\pocketmine\DEBUG > 1 and isset($pk)){ @@ -172,8 +169,8 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{ $this->interface->sendOption("name", "MCPE;" . rtrim(addcslashes($name, ";"), '\\') . ";" . - Info::CURRENT_PROTOCOL . ";" . - Info::MINECRAFT_VERSION_NETWORK . ";" . + ProtocolInfo::CURRENT_PROTOCOL . ";" . + ProtocolInfo::MINECRAFT_VERSION_NETWORK . ";" . $info->getPlayerCount() . ";" . $info->getMaxPlayerCount() ); @@ -208,7 +205,7 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{ $pk = $packet->__encapsulatedPacket; } - if(!$immediate and !$needACK and $packet::NETWORK_ID !== ProtocolInfo::BATCH_PACKET + if(!$immediate and !$needACK and $packet->canBeBatched() and Network::$BATCH_THRESHOLD >= 0 and strlen($packet->buffer) >= Network::$BATCH_THRESHOLD){ $this->server->batchPackets([$player], [$packet], true); @@ -218,8 +215,8 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{ if($pk === null){ $pk = new EncapsulatedPacket(); $pk->buffer = chr(0xfe) . $packet->buffer; // #blameshoghi - $packet->reliability = PacketReliability::RELIABLE_ORDERED; - $packet->orderChannel = 0; + $pk->reliability = PacketReliability::RELIABLE_ORDERED; + $pk->orderChannel = 0; if($needACK === true){ $pk->identifierACK = $this->identifiersACK[$identifier]++; diff --git a/src/pocketmine/network/protocol/AddEntityPacket.php b/src/pocketmine/network/mcpe/protocol/AddEntityPacket.php similarity index 81% rename from src/pocketmine/network/protocol/AddEntityPacket.php rename to src/pocketmine/network/mcpe/protocol/AddEntityPacket.php index b987fd4c6..84f69f379 100644 --- a/src/pocketmine/network/protocol/AddEntityPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AddEntityPacket.php @@ -19,14 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include use pocketmine\entity\Attribute; +use pocketmine\network\mcpe\NetworkSession; class AddEntityPacket extends DataPacket{ - const NETWORK_ID = Info::ADD_ENTITY_PACKET; + const NETWORK_ID = ProtocolInfo::ADD_ENTITY_PACKET; public $eid; public $type; @@ -49,8 +50,8 @@ class AddEntityPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->eid); //EntityUniqueID - TODO: verify this - $this->putEntityId($this->eid); + $this->putEntityUniqueId($this->eid); + $this->putEntityRuntimeId($this->eid); $this->putUnsignedVarInt($this->type); $this->putVector3f($this->x, $this->y, $this->z); $this->putVector3f($this->speedX, $this->speedY, $this->speedZ); @@ -66,10 +67,14 @@ class AddEntityPacket extends DataPacket{ $this->putEntityMetadata($this->metadata); $this->putUnsignedVarInt(count($this->links)); foreach($this->links as $link){ - $this->putEntityId($link[0]); - $this->putEntityId($link[1]); + $this->putEntityUniqueId($link[0]); + $this->putEntityUniqueId($link[1]); $this->putByte($link[2]); } } + public function handle(NetworkSession $session) : bool{ + return $session->handleAddEntity($this); + } + } diff --git a/src/pocketmine/network/protocol/AddHangingEntityPacket.php b/src/pocketmine/network/mcpe/protocol/AddHangingEntityPacket.php similarity index 70% rename from src/pocketmine/network/protocol/AddHangingEntityPacket.php rename to src/pocketmine/network/mcpe/protocol/AddHangingEntityPacket.php index 1cba0a649..5d2d4310e 100644 --- a/src/pocketmine/network/protocol/AddHangingEntityPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AddHangingEntityPacket.php @@ -19,12 +19,14 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class AddHangingEntityPacket extends DataPacket{ - const NETWORK_ID = Info::ADD_HANGING_ENTITY_PACKET; + const NETWORK_ID = ProtocolInfo::ADD_HANGING_ENTITY_PACKET; public $entityUniqueId; public $entityRuntimeId; @@ -39,10 +41,14 @@ class AddHangingEntityPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->entityUniqueId); - $this->putEntityId($this->entityRuntimeId); - $this->putBlockCoords($this->x, $this->y, $this->z); + $this->putEntityUniqueId($this->entityUniqueId); + $this->putEntityRuntimeId($this->entityRuntimeId); + $this->putBlockPosition($this->x, $this->y, $this->z); $this->putVarInt($this->unknown); } + public function handle(NetworkSession $session) : bool{ + return $session->handleAddHangingEntity($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/AddItemEntityPacket.php b/src/pocketmine/network/mcpe/protocol/AddItemEntityPacket.php similarity index 77% rename from src/pocketmine/network/protocol/AddItemEntityPacket.php rename to src/pocketmine/network/mcpe/protocol/AddItemEntityPacket.php index ac6486463..48d7f0130 100644 --- a/src/pocketmine/network/protocol/AddItemEntityPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AddItemEntityPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class AddItemEntityPacket extends DataPacket{ - const NETWORK_ID = Info::ADD_ITEM_ENTITY_PACKET; + const NETWORK_ID = ProtocolInfo::ADD_ITEM_ENTITY_PACKET; public $eid; public $item; @@ -42,11 +44,15 @@ class AddItemEntityPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->eid); //EntityUniqueID - $this->putEntityId($this->eid); //EntityRuntimeID + $this->putEntityUniqueId($this->eid); + $this->putEntityRuntimeId($this->eid); $this->putSlot($this->item); $this->putVector3f($this->x, $this->y, $this->z); $this->putVector3f($this->speedX, $this->speedY, $this->speedZ); } + public function handle(NetworkSession $session) : bool{ + return $session->handleAddItemEntity($this); + } + } diff --git a/src/pocketmine/network/protocol/AddItemPacket.php b/src/pocketmine/network/mcpe/protocol/AddItemPacket.php similarity index 79% rename from src/pocketmine/network/protocol/AddItemPacket.php rename to src/pocketmine/network/mcpe/protocol/AddItemPacket.php index af50d545c..397d87f9d 100644 --- a/src/pocketmine/network/protocol/AddItemPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AddItemPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class AddItemPacket extends DataPacket{ - const NETWORK_ID = Info::ADD_ITEM_PACKET; + const NETWORK_ID = ProtocolInfo::ADD_ITEM_PACKET; public $item; @@ -38,4 +40,8 @@ class AddItemPacket extends DataPacket{ $this->putSlot($this->item); } + public function handle(NetworkSession $session) : bool{ + return $session->handleAddItem($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/AddPaintingPacket.php b/src/pocketmine/network/mcpe/protocol/AddPaintingPacket.php similarity index 72% rename from src/pocketmine/network/protocol/AddPaintingPacket.php rename to src/pocketmine/network/mcpe/protocol/AddPaintingPacket.php index 3cc3e550b..9ae5b2988 100644 --- a/src/pocketmine/network/protocol/AddPaintingPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AddPaintingPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class AddPaintingPacket extends DataPacket{ - const NETWORK_ID = Info::ADD_PAINTING_PACKET; + const NETWORK_ID = ProtocolInfo::ADD_PAINTING_PACKET; public $eid; public $x; @@ -40,11 +42,15 @@ class AddPaintingPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->eid); //EntityUniqueID - $this->putEntityId($this->eid); //EntityRuntimeID - $this->putBlockCoords($this->x, $this->y, $this->z); + $this->putEntityUniqueId($this->eid); + $this->putEntityRuntimeId($this->eid); + $this->putBlockPosition($this->x, $this->y, $this->z); $this->putVarInt($this->direction); $this->putString($this->title); } + public function handle(NetworkSession $session) : bool{ + return $session->handleAddPainting($this); + } + } diff --git a/src/pocketmine/network/protocol/AddPlayerPacket.php b/src/pocketmine/network/mcpe/protocol/AddPlayerPacket.php similarity index 81% rename from src/pocketmine/network/protocol/AddPlayerPacket.php rename to src/pocketmine/network/mcpe/protocol/AddPlayerPacket.php index 892cb391c..b64c3b428 100644 --- a/src/pocketmine/network/protocol/AddPlayerPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AddPlayerPacket.php @@ -19,12 +19,14 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class AddPlayerPacket extends DataPacket{ - const NETWORK_ID = Info::ADD_PLAYER_PACKET; + const NETWORK_ID = ProtocolInfo::ADD_PLAYER_PACKET; public $uuid; public $username; @@ -49,8 +51,8 @@ class AddPlayerPacket extends DataPacket{ $this->reset(); $this->putUUID($this->uuid); $this->putString($this->username); - $this->putEntityId($this->eid); //EntityUniqueID - $this->putEntityId($this->eid); //EntityRuntimeID + $this->putEntityUniqueId($this->eid); + $this->putEntityRuntimeId($this->eid); $this->putVector3f($this->x, $this->y, $this->z); $this->putVector3f($this->speedX, $this->speedY, $this->speedZ); $this->putLFloat($this->pitch); @@ -60,4 +62,8 @@ class AddPlayerPacket extends DataPacket{ $this->putEntityMetadata($this->metadata); } + public function handle(NetworkSession $session) : bool{ + return $session->handleAddPlayer($this); + } + } diff --git a/src/pocketmine/network/protocol/AdventureSettingsPacket.php b/src/pocketmine/network/mcpe/protocol/AdventureSettingsPacket.php similarity index 91% rename from src/pocketmine/network/protocol/AdventureSettingsPacket.php rename to src/pocketmine/network/mcpe/protocol/AdventureSettingsPacket.php index 0a5745679..8cc7989ac 100644 --- a/src/pocketmine/network/protocol/AdventureSettingsPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AdventureSettingsPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class AdventureSettingsPacket extends DataPacket{ - const NETWORK_ID = Info::ADVENTURE_SETTINGS_PACKET; + const NETWORK_ID = ProtocolInfo::ADVENTURE_SETTINGS_PACKET; const PERMISSION_NORMAL = 0; const PERMISSION_OPERATOR = 1; @@ -95,4 +97,8 @@ class AdventureSettingsPacket extends DataPacket{ $this->putUnsignedVarInt($this->userPermission); } + public function handle(NetworkSession $session) : bool{ + return $session->handleAdventureSettings($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/AnimatePacket.php b/src/pocketmine/network/mcpe/protocol/AnimatePacket.php similarity index 74% rename from src/pocketmine/network/protocol/AnimatePacket.php rename to src/pocketmine/network/mcpe/protocol/AnimatePacket.php index c0d27012d..fba81c904 100644 --- a/src/pocketmine/network/protocol/AnimatePacket.php +++ b/src/pocketmine/network/mcpe/protocol/AnimatePacket.php @@ -19,26 +19,32 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class AnimatePacket extends DataPacket{ - const NETWORK_ID = Info::ANIMATE_PACKET; + const NETWORK_ID = ProtocolInfo::ANIMATE_PACKET; public $action; public $eid; public function decode(){ $this->action = $this->getVarInt(); - $this->eid = $this->getEntityId(); + $this->eid = $this->getEntityRuntimeId(); } public function encode(){ $this->reset(); $this->putVarInt($this->action); - $this->putEntityId($this->eid); + $this->putEntityRuntimeId($this->eid); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleAnimate($this); } } diff --git a/src/pocketmine/network/protocol/AvailableCommandsPacket.php b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php similarity index 79% rename from src/pocketmine/network/protocol/AvailableCommandsPacket.php rename to src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php index 3f86e126b..fc458e074 100644 --- a/src/pocketmine/network/protocol/AvailableCommandsPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php @@ -19,12 +19,14 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class AvailableCommandsPacket extends DataPacket{ - const NETWORK_ID = Info::AVAILABLE_COMMANDS_PACKET; + const NETWORK_ID = ProtocolInfo::AVAILABLE_COMMANDS_PACKET; public $commands; //JSON-encoded command data public $unknown; @@ -39,4 +41,8 @@ class AvailableCommandsPacket extends DataPacket{ $this->putString($this->unknown); } + public function handle(NetworkSession $session) : bool{ + return $session->handleAvailableCommands($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/BatchPacket.php b/src/pocketmine/network/mcpe/protocol/BatchPacket.php new file mode 100644 index 000000000..a288b24c4 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/BatchPacket.php @@ -0,0 +1,80 @@ + + + +use pocketmine\network\mcpe\NetworkSession; + +class BatchPacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::BATCH_PACKET; + + public $payload; + + public function canBeBatched() : bool{ + return false; + } + + public function canBeSentBeforeLogin() : bool{ + return true; + } + + public function decode(){ + $this->payload = $this->getString(); + } + + public function encode(){ + $this->reset(); + $this->putString($this->payload); + } + + public function handle(NetworkSession $session) : bool{ + if(strlen($this->payload) < 2){ + throw new \InvalidStateException("Not enough bytes in payload, expected zlib header"); + } + + $str = zlib_decode($this->payload, 1024 * 1024 * 64); //Max 64MB + $len = strlen($str); + + if($len === 0){ + throw new \InvalidStateException("Decoded BatchPacket payload is empty"); + } + + $this->setBuffer($str, 0); + + $network = $session->getServer()->getNetwork(); + while(!$this->feof()){ + $buf = $this->getString(); + $pk = $network->getPacket(ord($buf{0})); + if(!$pk->canBeBatched()){ + throw new \InvalidArgumentException("Received invalid " . get_class($pk) . " inside BatchPacket"); + } + + $pk->setBuffer($buf, 1); + $session->handleDataPacket($pk); + } + + return true; + } + +} \ No newline at end of file diff --git a/src/pocketmine/network/protocol/BlockEntityDataPacket.php b/src/pocketmine/network/mcpe/protocol/BlockEntityDataPacket.php similarity index 72% rename from src/pocketmine/network/protocol/BlockEntityDataPacket.php rename to src/pocketmine/network/mcpe/protocol/BlockEntityDataPacket.php index df01e6142..cc1fbc52d 100644 --- a/src/pocketmine/network/protocol/BlockEntityDataPacket.php +++ b/src/pocketmine/network/mcpe/protocol/BlockEntityDataPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class BlockEntityDataPacket extends DataPacket{ - const NETWORK_ID = Info::BLOCK_ENTITY_DATA_PACKET; + const NETWORK_ID = ProtocolInfo::BLOCK_ENTITY_DATA_PACKET; public $x; public $y; @@ -33,14 +35,18 @@ class BlockEntityDataPacket extends DataPacket{ public $namedtag; public function decode(){ - $this->getBlockCoords($this->x, $this->y, $this->z); + $this->getBlockPosition($this->x, $this->y, $this->z); $this->namedtag = $this->get(true); } public function encode(){ $this->reset(); - $this->putBlockCoords($this->x, $this->y, $this->z); + $this->putBlockPosition($this->x, $this->y, $this->z); $this->put($this->namedtag); } + public function handle(NetworkSession $session) : bool{ + return $session->handleBlockEntityData($this); + } + } diff --git a/src/pocketmine/network/protocol/BlockEventPacket.php b/src/pocketmine/network/mcpe/protocol/BlockEventPacket.php similarity index 76% rename from src/pocketmine/network/protocol/BlockEventPacket.php rename to src/pocketmine/network/mcpe/protocol/BlockEventPacket.php index 14b4ca274..ba0667a4a 100644 --- a/src/pocketmine/network/protocol/BlockEventPacket.php +++ b/src/pocketmine/network/mcpe/protocol/BlockEventPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class BlockEventPacket extends DataPacket{ - const NETWORK_ID = Info::BLOCK_EVENT_PACKET; + const NETWORK_ID = ProtocolInfo::BLOCK_EVENT_PACKET; public $x; public $y; @@ -39,9 +41,13 @@ class BlockEventPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putBlockCoords($this->x, $this->y, $this->z); + $this->putBlockPosition($this->x, $this->y, $this->z); $this->putVarInt($this->case1); $this->putVarInt($this->case2); } + public function handle(NetworkSession $session) : bool{ + return $session->handleBlockEvent($this); + } + } diff --git a/src/pocketmine/network/mcpe/protocol/BlockPickRequestPacket.php b/src/pocketmine/network/mcpe/protocol/BlockPickRequestPacket.php new file mode 100644 index 000000000..89ff5180f --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/BlockPickRequestPacket.php @@ -0,0 +1,52 @@ + + + +use pocketmine\network\mcpe\NetworkSession; + +class BlockPickRequestPacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::BLOCK_PICK_REQUEST_PACKET; + + public $tileX; + public $tileY; + public $tileZ; + public $hotbarSlot; + + public function decode(){ + $this->getSignedBlockPosition($this->tileX, $this->tileY, $this->tileZ); + $this->hotbarSlot = $this->getByte(); + } + + public function encode(){ + $this->reset(); + $this->putSignedBlockPosition($this->tileX, $this->tileY, $this->tileZ); + $this->putByte($this->hotbarSlot); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleBlockPickRequest($this); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/protocol/ChangeDimensionPacket.php b/src/pocketmine/network/mcpe/protocol/ChangeDimensionPacket.php similarity index 81% rename from src/pocketmine/network/protocol/ChangeDimensionPacket.php rename to src/pocketmine/network/mcpe/protocol/ChangeDimensionPacket.php index 54a9b443a..93f85494d 100644 --- a/src/pocketmine/network/protocol/ChangeDimensionPacket.php +++ b/src/pocketmine/network/mcpe/protocol/ChangeDimensionPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class ChangeDimensionPacket extends DataPacket{ - const NETWORK_ID = Info::CHANGE_DIMENSION_PACKET; + const NETWORK_ID = ProtocolInfo::CHANGE_DIMENSION_PACKET; const DIMENSION_OVERWORLD = 0; const DIMENSION_NETHER = 1; @@ -47,4 +49,8 @@ class ChangeDimensionPacket extends DataPacket{ $this->putBool($this->unknown); } + public function handle(NetworkSession $session) : bool{ + return $session->handleChangeDimension($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/ChunkRadiusUpdatedPacket.php b/src/pocketmine/network/mcpe/protocol/ChunkRadiusUpdatedPacket.php similarity index 77% rename from src/pocketmine/network/protocol/ChunkRadiusUpdatedPacket.php rename to src/pocketmine/network/mcpe/protocol/ChunkRadiusUpdatedPacket.php index a97c8b32a..9c6b6dc7c 100644 --- a/src/pocketmine/network/protocol/ChunkRadiusUpdatedPacket.php +++ b/src/pocketmine/network/mcpe/protocol/ChunkRadiusUpdatedPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class ChunkRadiusUpdatedPacket extends DataPacket{ - const NETWORK_ID = Info::CHUNK_RADIUS_UPDATED_PACKET; + const NETWORK_ID = ProtocolInfo::CHUNK_RADIUS_UPDATED_PACKET; public $radius; @@ -37,4 +39,8 @@ class ChunkRadiusUpdatedPacket extends DataPacket{ $this->putVarInt($this->radius); } + public function handle(NetworkSession $session) : bool{ + return $session->handleChunkRadiusUpdated($this); + } + } diff --git a/src/pocketmine/network/mcpe/protocol/ClientToServerHandshakePacket.php b/src/pocketmine/network/mcpe/protocol/ClientToServerHandshakePacket.php new file mode 100644 index 000000000..ef1aa7c33 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/ClientToServerHandshakePacket.php @@ -0,0 +1,48 @@ + + + +use pocketmine\network\mcpe\NetworkSession; + +class ClientToServerHandshakePacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::CLIENT_TO_SERVER_HANDSHAKE_PACKET; + + public function canBeSentBeforeLogin() : bool{ + return true; + } + + public function decode(){ + //No payload + } + + public function encode(){ + $this->reset(); + //No payload + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleClientToServerHandshake($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/ClientboundMapItemDataPacket.php b/src/pocketmine/network/mcpe/protocol/ClientboundMapItemDataPacket.php new file mode 100644 index 000000000..d1a94d9a8 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/ClientboundMapItemDataPacket.php @@ -0,0 +1,149 @@ + + + +use pocketmine\network\mcpe\NetworkSession; +use pocketmine\utils\Color; + +class ClientboundMapItemDataPacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::CLIENTBOUND_MAP_ITEM_DATA_PACKET; + + const BITFLAG_TEXTURE_UPDATE = 0x02; + const BITFLAG_DECORATION_UPDATE = 0x04; + + public $mapId; + public $type; + + public $eids = []; + public $scale; + public $decorations = []; + + public $width; + public $height; + public $xOffset = 0; + public $yOffset = 0; + /** @var Color[][] */ + public $colors = []; + + public function decode(){ + $this->mapId = $this->getVarInt(); + $this->type = $this->getUnsignedVarInt(); + + if(($this->type & 0x08) !== 0){ + $count = $this->getUnsignedVarInt(); + for($i = 0; $i < $count; ++$i){ + $this->eids[] = $this->getVarInt(); //entity unique ID, signed var-int + } + } + + if(($this->type & (self::BITFLAG_DECORATION_UPDATE | self::BITFLAG_TEXTURE_UPDATE)) !== 0){ //Decoration bitflag or colour bitflag + $this->scale = $this->getByte(); + } + + if(($this->type & self::BITFLAG_DECORATION_UPDATE) !== 0){ + $count = $this->getUnsignedVarInt(); + for($i = 0; $i < $count; ++$i){ + $weird = $this->getVarInt(); + $this->decorations[$i]["rot"] = $weird & 0x0f; + $this->decorations[$i]["img"] = $weird >> 4; + + $this->decorations[$i]["xOffset"] = $this->getByte(); + $this->decorations[$i]["yOffset"] = $this->getByte(); + $this->decorations[$i]["label"] = $this->getString(); + + $this->decorations[$i]["color"] = Color::fromARGB($this->getLInt()); //already BE, don't need to reverse it again + } + } + + if(($this->type & self::BITFLAG_TEXTURE_UPDATE) !== 0){ + $this->width = $this->getVarInt(); + $this->height = $this->getVarInt(); + $this->xOffset = $this->getVarInt(); + $this->yOffset = $this->getVarInt(); + for($y = 0; $y < $this->height; ++$y){ + for($x = 0; $x < $this->width; ++$x){ + $this->colors[$y][$x] = Color::fromABGR($this->getUnsignedVarInt()); + } + } + } + } + + public function encode(){ + $this->reset(); + $this->putVarInt($this->mapId); //entity unique ID, signed var-int + + $type = 0; + if(($eidsCount = count($this->eids)) > 0){ + $type |= 0x08; + } + if(($decorationCount = count($this->decorations)) > 0){ + $type |= self::BITFLAG_DECORATION_UPDATE; + } + if(count($this->colors) > 0){ + $type |= self::BITFLAG_TEXTURE_UPDATE; + } + + $this->putUnsignedVarInt($type); + + if(($type & 0x08) !== 0){ //TODO: find out what these are for + $this->putUnsignedVarInt($eidsCount); + foreach($this->eids as $eid){ + $this->putVarInt($eid); + } + } + + if(($type & (self::BITFLAG_TEXTURE_UPDATE | self::BITFLAG_DECORATION_UPDATE)) !== 0){ + $this->putByte($this->scale); + } + + if(($type & self::BITFLAG_DECORATION_UPDATE) !== 0){ + $this->putUnsignedVarInt($decorationCount); + foreach($this->decorations as $decoration){ + $this->putVarInt(($decoration["rot"] & 0x0f) | ($decoration["img"] << 4)); + $this->putByte($decoration["xOffset"]); + $this->putByte($decoration["yOffset"]); + $this->putString($decoration["label"]); + $this->putLInt($decoration["color"]->toARGB()); + } + } + + if(($type & self::BITFLAG_TEXTURE_UPDATE) !== 0){ + $this->putVarInt($this->width); + $this->putVarInt($this->height); + $this->putVarInt($this->xOffset); + $this->putVarInt($this->yOffset); + for($y = 0; $y < $this->height; ++$y){ + for($x = 0; $x < $this->width; ++$x){ + $this->putUnsignedVarInt($this->colors[$y][$x]->toABGR()); + } + } + } + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleClientboundMapItemData($this); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/CommandBlockUpdatePacket.php b/src/pocketmine/network/mcpe/protocol/CommandBlockUpdatePacket.php new file mode 100644 index 000000000..cba3f9ad0 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/CommandBlockUpdatePacket.php @@ -0,0 +1,93 @@ + + + +use pocketmine\network\mcpe\NetworkSession; + +class CommandBlockUpdatePacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::COMMAND_BLOCK_UPDATE_PACKET; + + public $isBlock; + + public $x; + public $y; + public $z; + public $commandBlockMode; + public $isRedstoneMode; + public $isConditional; + + public $minecartEid; + + public $command; + public $lastOutput; + public $name; + + public $shouldTrackOutput; + + public function decode(){ + $this->isBlock = $this->getBool(); + + if($this->isBlock){ + $this->getBlockPosition($this->x, $this->y, $this->z); + $this->commandBlockMode = $this->getUnsignedVarInt(); + $this->isRedstoneMode = $this->getBool(); + $this->isConditional = $this->getBool(); + }else{ + //Minecart with command block + $this->minecartEid = $this->getEntityRuntimeId(); + } + + $this->command = $this->getString(); + $this->lastOutput = $this->getString(); + $this->name = $this->getString(); + + $this->shouldTrackOutput = $this->getBool(); + } + + public function encode(){ + $this->reset(); + $this->putBool($this->isBlock); + + if($this->isBlock){ + $this->putBlockPosition($this->x, $this->y, $this->z); + $this->putUnsignedVarInt($this->commandBlockMode); + $this->putBool($this->isRedstoneMode); + $this->putBool($this->isConditional); + }else{ + $this->putEntityRuntimeId($this->minecartEid); + } + + $this->putString($this->command); + $this->putString($this->lastOutput); + $this->putString($this->name); + + $this->putBool($this->shouldTrackOutput); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleCommandBlockUpdate($this); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/protocol/CommandStepPacket.php b/src/pocketmine/network/mcpe/protocol/CommandStepPacket.php similarity index 67% rename from src/pocketmine/network/protocol/CommandStepPacket.php rename to src/pocketmine/network/mcpe/protocol/CommandStepPacket.php index 0aef29977..26ccf3233 100644 --- a/src/pocketmine/network/protocol/CommandStepPacket.php +++ b/src/pocketmine/network/mcpe/protocol/CommandStepPacket.php @@ -19,24 +19,14 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include -class CommandStepPacket extends DataPacket{ - const NETWORK_ID = Info::COMMAND_STEP_PACKET; +use pocketmine\network\mcpe\NetworkSession; - /** - * unknown (string) - * unknown (string) - * unknown (uvarint) - * unknown (uvarint) - * unknown (bool) - * unknown (uvarint64) - * unknown (string) - * unknown (string) - * https://gist.github.com/dktapps/8285b93af4ca38e0104bfeb9a6c87afd - */ +class CommandStepPacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::COMMAND_STEP_PACKET; public $command; public $overload; @@ -44,7 +34,7 @@ class CommandStepPacket extends DataPacket{ public $uvarint2; public $bool; public $uvarint64; - public $args; //JSON formatted command arguments + public $args; public $string4; public function decode(){ @@ -53,16 +43,19 @@ class CommandStepPacket extends DataPacket{ $this->uvarint1 = $this->getUnsignedVarInt(); $this->uvarint2 = $this->getUnsignedVarInt(); $this->bool = (bool) $this->getByte(); - $this->uvarint64 = $this->getUnsignedVarInt(); //TODO: varint64 + $this->uvarint64 = $this->getUnsignedVarLong(); $this->args = json_decode($this->getString()); $this->string4 = $this->getString(); - while(!$this->feof()){ - $this->getByte(); //prevent assertion errors. TODO: find out why there are always 3 extra bytes at the end of this packet. - } + + $this->get(true); //TODO: read command origin data } public function encode(){ } + public function handle(NetworkSession $session) : bool{ + return $session->handleCommandStep($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/ContainerClosePacket.php b/src/pocketmine/network/mcpe/protocol/ContainerClosePacket.php similarity index 79% rename from src/pocketmine/network/protocol/ContainerClosePacket.php rename to src/pocketmine/network/mcpe/protocol/ContainerClosePacket.php index f60788a0a..c566152fd 100644 --- a/src/pocketmine/network/protocol/ContainerClosePacket.php +++ b/src/pocketmine/network/mcpe/protocol/ContainerClosePacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class ContainerClosePacket extends DataPacket{ - const NETWORK_ID = Info::CONTAINER_CLOSE_PACKET; + const NETWORK_ID = ProtocolInfo::CONTAINER_CLOSE_PACKET; public $windowid; @@ -38,4 +40,7 @@ class ContainerClosePacket extends DataPacket{ $this->putByte($this->windowid); } + public function handle(NetworkSession $session) : bool{ + return $session->handleContainerClose($this); + } } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/ContainerOpenPacket.php b/src/pocketmine/network/mcpe/protocol/ContainerOpenPacket.php similarity index 75% rename from src/pocketmine/network/protocol/ContainerOpenPacket.php rename to src/pocketmine/network/mcpe/protocol/ContainerOpenPacket.php index 5968d7977..698c9d058 100644 --- a/src/pocketmine/network/protocol/ContainerOpenPacket.php +++ b/src/pocketmine/network/mcpe/protocol/ContainerOpenPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class ContainerOpenPacket extends DataPacket{ - const NETWORK_ID = Info::CONTAINER_OPEN_PACKET; + const NETWORK_ID = ProtocolInfo::CONTAINER_OPEN_PACKET; public $windowid; public $type; @@ -44,8 +46,12 @@ class ContainerOpenPacket extends DataPacket{ $this->putByte($this->windowid); $this->putByte($this->type); $this->putVarInt($this->slots); - $this->putBlockCoords($this->x, $this->y, $this->z); - $this->putEntityId($this->entityId); + $this->putBlockPosition($this->x, $this->y, $this->z); + $this->putEntityUniqueId($this->entityId); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleContainerOpen($this); } } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/ContainerSetContentPacket.php b/src/pocketmine/network/mcpe/protocol/ContainerSetContentPacket.php similarity index 87% rename from src/pocketmine/network/protocol/ContainerSetContentPacket.php rename to src/pocketmine/network/mcpe/protocol/ContainerSetContentPacket.php index 00e078be3..ade1394d4 100644 --- a/src/pocketmine/network/protocol/ContainerSetContentPacket.php +++ b/src/pocketmine/network/mcpe/protocol/ContainerSetContentPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class ContainerSetContentPacket extends DataPacket{ - const NETWORK_ID = Info::CONTAINER_SET_CONTENT_PACKET; + const NETWORK_ID = ProtocolInfo::CONTAINER_SET_CONTENT_PACKET; const SPECIAL_INVENTORY = 0; const SPECIAL_ARMOR = 0x78; @@ -73,4 +75,8 @@ class ContainerSetContentPacket extends DataPacket{ } } + public function handle(NetworkSession $session) : bool{ + return $session->handleContainerSetContent($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/ContainerSetDataPacket.php b/src/pocketmine/network/mcpe/protocol/ContainerSetDataPacket.php similarity index 79% rename from src/pocketmine/network/protocol/ContainerSetDataPacket.php rename to src/pocketmine/network/mcpe/protocol/ContainerSetDataPacket.php index d2756a928..a8faaeab4 100644 --- a/src/pocketmine/network/protocol/ContainerSetDataPacket.php +++ b/src/pocketmine/network/mcpe/protocol/ContainerSetDataPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class ContainerSetDataPacket extends DataPacket{ - const NETWORK_ID = Info::CONTAINER_SET_DATA_PACKET; + const NETWORK_ID = ProtocolInfo::CONTAINER_SET_DATA_PACKET; public $windowid; public $property; @@ -42,4 +44,8 @@ class ContainerSetDataPacket extends DataPacket{ $this->putVarInt($this->value); } + public function handle(NetworkSession $session) : bool{ + return $session->handleContainerSetData($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/ContainerSetSlotPacket.php b/src/pocketmine/network/mcpe/protocol/ContainerSetSlotPacket.php similarity index 84% rename from src/pocketmine/network/protocol/ContainerSetSlotPacket.php rename to src/pocketmine/network/mcpe/protocol/ContainerSetSlotPacket.php index 97915b5b8..f5358ee12 100644 --- a/src/pocketmine/network/protocol/ContainerSetSlotPacket.php +++ b/src/pocketmine/network/mcpe/protocol/ContainerSetSlotPacket.php @@ -19,14 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include use pocketmine\item\Item; +use pocketmine\network\mcpe\NetworkSession; class ContainerSetSlotPacket extends DataPacket{ - const NETWORK_ID = Info::CONTAINER_SET_SLOT_PACKET; + const NETWORK_ID = ProtocolInfo::CONTAINER_SET_SLOT_PACKET; public $windowid; public $slot; @@ -52,4 +53,8 @@ class ContainerSetSlotPacket extends DataPacket{ $this->putByte($this->selectSlot); } + public function handle(NetworkSession $session) : bool{ + return $session->handleContainerSetSlot($this); + } + } diff --git a/src/pocketmine/network/protocol/CraftingDataPacket.php b/src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php similarity index 95% rename from src/pocketmine/network/protocol/CraftingDataPacket.php rename to src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php index 4ecccc738..f183510c6 100644 --- a/src/pocketmine/network/protocol/CraftingDataPacket.php +++ b/src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php @@ -19,20 +19,20 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include use pocketmine\inventory\FurnaceRecipe; -use pocketmine\inventory\MultiRecipe; use pocketmine\inventory\ShapedRecipe; use pocketmine\inventory\ShapelessRecipe; use pocketmine\item\Item; +use pocketmine\network\mcpe\NetworkSession; use pocketmine\utils\BinaryStream; class CraftingDataPacket extends DataPacket{ - const NETWORK_ID = Info::CRAFTING_DATA_PACKET; + const NETWORK_ID = ProtocolInfo::CRAFTING_DATA_PACKET; const ENTRY_SHAPELESS = 0; const ENTRY_SHAPED = 1; @@ -198,4 +198,8 @@ class CraftingDataPacket extends DataPacket{ $this->putBool($this->cleanRecipes); } + public function handle(NetworkSession $session) : bool{ + return $session->handleCraftingData($this); + } + } diff --git a/src/pocketmine/network/protocol/CraftingEventPacket.php b/src/pocketmine/network/mcpe/protocol/CraftingEventPacket.php similarity index 85% rename from src/pocketmine/network/protocol/CraftingEventPacket.php rename to src/pocketmine/network/mcpe/protocol/CraftingEventPacket.php index ffc294b19..e45d42410 100644 --- a/src/pocketmine/network/protocol/CraftingEventPacket.php +++ b/src/pocketmine/network/mcpe/protocol/CraftingEventPacket.php @@ -19,14 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include use pocketmine\item\Item; +use pocketmine\network\mcpe\NetworkSession; class CraftingEventPacket extends DataPacket{ - const NETWORK_ID = Info::CRAFTING_EVENT_PACKET; + const NETWORK_ID = ProtocolInfo::CRAFTING_EVENT_PACKET; public $windowId; public $type; @@ -62,4 +63,8 @@ class CraftingEventPacket extends DataPacket{ } + public function handle(NetworkSession $session) : bool{ + return $session->handleCraftingEvent($this); + } + } diff --git a/src/pocketmine/network/protocol/DataPacket.php b/src/pocketmine/network/mcpe/protocol/DataPacket.php similarity index 61% rename from src/pocketmine/network/protocol/DataPacket.php rename to src/pocketmine/network/mcpe/protocol/DataPacket.php index 94d989ee1..a320f5898 100644 --- a/src/pocketmine/network/protocol/DataPacket.php +++ b/src/pocketmine/network/mcpe/protocol/DataPacket.php @@ -19,12 +19,13 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include use pocketmine\entity\Entity; use pocketmine\item\Item; +use pocketmine\network\mcpe\NetworkSession; use pocketmine\utils\BinaryStream; use pocketmine\utils\Utils; @@ -39,10 +40,20 @@ abstract class DataPacket extends BinaryStream{ return $this::NETWORK_ID; } + public function canBeBatched() : bool{ + return true; + } + + public function canBeSentBeforeLogin() : bool{ + return false; + } + abstract public function encode(); abstract public function decode(); + abstract public function handle(NetworkSession $session) : bool; + public function reset(){ $this->buffer = chr($this::NETWORK_ID); $this->offset = 0; @@ -109,17 +120,15 @@ abstract class DataPacket extends BinaryStream{ $value[2] = $item->getDamage(); break; case Entity::DATA_TYPE_POS: - $value = []; - $value[0] = $this->getVarInt(); //x - $value[1] = $this->getVarInt(); //y (SIGNED) - $value[2] = $this->getVarInt(); //z + $value = [0, 0, 0]; + $this->getSignedBlockPosition(...$value); break; case Entity::DATA_TYPE_LONG: - $value = $this->getVarInt(); //TODO: varint64 proper support + $value = $this->getVarLong(); break; case Entity::DATA_TYPE_VECTOR3F: $value = [0.0, 0.0, 0.0]; - $this->getVector3f($value[0], $value[1], $value[2]); + $this->getVector3f(...$value); break; default: $value = []; @@ -166,17 +175,119 @@ abstract class DataPacket extends BinaryStream{ break; case Entity::DATA_TYPE_POS: //TODO: change this implementation (use objects) - $this->putVarInt($d[1][0]); //x - $this->putVarInt($d[1][1]); //y (SIGNED) - $this->putVarInt($d[1][2]); //z + $this->putSignedBlockPosition(...$d[1]); break; case Entity::DATA_TYPE_LONG: - $this->putVarInt($d[1]); //TODO: varint64 support + $this->putVarLong($d[1]); break; case Entity::DATA_TYPE_VECTOR3F: //TODO: change this implementation (use objects) - $this->putVector3f($d[1][0], $d[1][1], $d[1][2]); //x, y, z + $this->putVector3f(...$d[1]); //x, y, z } } } + + /** + * Reads and returns an EntityUniqueID + * @return int|string + */ + public function getEntityUniqueId(){ + return $this->getVarLong(); + } + + /** + * Writes an EntityUniqueID + * @param int|string $eid + */ + public function putEntityUniqueId($eid){ + $this->putVarLong($eid); + } + + /** + * Reads and returns an EntityRuntimeID + * @return int|string + */ + public function getEntityRuntimeId(){ + return $this->getUnsignedVarLong(); + } + + /** + * Writes an EntityUniqueID + * @param int|string $eid + */ + public function putEntityRuntimeId($eid){ + $this->putUnsignedVarLong($eid); + } + + /** + * Reads an block position with unsigned Y coordinate. + * @param int $x + * @param int $y 0-255 + * @param int $z + */ + public function getBlockPosition(&$x, &$y, &$z){ + $x = $this->getVarInt(); + $y = $this->getUnsignedVarInt(); + $z = $this->getVarInt(); + } + + /** + * Writes a block position with unsigned Y coordinate. + * @param int &$x + * @param int &$y + * @param int &$z + */ + public function putBlockPosition($x, $y, $z){ + $this->putVarInt($x); + $this->putUnsignedVarInt($y); + $this->putVarInt($z); + } + + /** + * Reads a block position with a signed Y coordinate. + * @param int &$x + * @param int &$y + * @param int &$z + */ + public function getSignedBlockPosition(&$x, &$y, &$z){ + $x = $this->getVarInt(); + $y = $this->getVarInt(); + $z = $this->getVarInt(); + } + + /** + * Writes a block position with a signed Y coordinate. + * @param int $x + * @param int $y + * @param int $z + */ + public function putSignedBlockPosition($x, $y, $z){ + $this->putVarInt($x); + $this->putVarInt($y); + $this->putVarInt($z); + } + + /** + * Reads a floating-point vector3 rounded to 4dp. + * @param float $x + * @param float $y + * @param float $z + */ + public function getVector3f(&$x, &$y, &$z){ + $x = $this->getLFloat(4); + $y = $this->getLFloat(4); + $z = $this->getLFloat(4); + } + + /** + * Writes a floating-point vector3 + * @param float $x + * @param float $y + * @param float $z + */ + public function putVector3f($x, $y, $z){ + $this->putLFloat($x); + $this->putLFloat($y); + $this->putLFloat($z); + } } diff --git a/src/pocketmine/network/protocol/DisconnectPacket.php b/src/pocketmine/network/mcpe/protocol/DisconnectPacket.php similarity index 77% rename from src/pocketmine/network/protocol/DisconnectPacket.php rename to src/pocketmine/network/mcpe/protocol/DisconnectPacket.php index e3873eeab..627e76015 100644 --- a/src/pocketmine/network/protocol/DisconnectPacket.php +++ b/src/pocketmine/network/mcpe/protocol/DisconnectPacket.php @@ -19,17 +19,23 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class DisconnectPacket extends DataPacket{ - const NETWORK_ID = Info::DISCONNECT_PACKET; + const NETWORK_ID = ProtocolInfo::DISCONNECT_PACKET; public $hideDisconnectionScreen = false; public $message; + public function canBeSentBeforeLogin() : bool{ + return true; + } + public function decode(){ $this->hideDisconnectionScreen = $this->getBool(); $this->message = $this->getString(); @@ -41,4 +47,8 @@ class DisconnectPacket extends DataPacket{ $this->putString($this->message); } + public function handle(NetworkSession $session) : bool{ + return $session->handleDisconnect($this); + } + } diff --git a/src/pocketmine/network/protocol/DropItemPacket.php b/src/pocketmine/network/mcpe/protocol/DropItemPacket.php similarity index 79% rename from src/pocketmine/network/protocol/DropItemPacket.php rename to src/pocketmine/network/mcpe/protocol/DropItemPacket.php index 49a53f82a..98fff9df9 100644 --- a/src/pocketmine/network/protocol/DropItemPacket.php +++ b/src/pocketmine/network/mcpe/protocol/DropItemPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class DropItemPacket extends DataPacket{ - const NETWORK_ID = Info::DROP_ITEM_PACKET; + const NETWORK_ID = ProtocolInfo::DROP_ITEM_PACKET; public $type; public $item; @@ -39,4 +41,8 @@ class DropItemPacket extends DataPacket{ } + public function handle(NetworkSession $session) : bool{ + return $session->handleDropItem($this); + } + } diff --git a/src/pocketmine/network/protocol/EntityEventPacket.php b/src/pocketmine/network/mcpe/protocol/EntityEventPacket.php similarity index 78% rename from src/pocketmine/network/protocol/EntityEventPacket.php rename to src/pocketmine/network/mcpe/protocol/EntityEventPacket.php index 64510980b..d8d35edba 100644 --- a/src/pocketmine/network/protocol/EntityEventPacket.php +++ b/src/pocketmine/network/mcpe/protocol/EntityEventPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class EntityEventPacket extends DataPacket{ - const NETWORK_ID = Info::ENTITY_EVENT_PACKET; + const NETWORK_ID = ProtocolInfo::ENTITY_EVENT_PACKET; const HURT_ANIMATION = 2; const DEATH_ANIMATION = 3; @@ -41,25 +43,30 @@ class EntityEventPacket extends DataPacket{ const FISH_HOOK_TEASE = 14; const SQUID_INK_CLOUD = 15; const AMBIENT_SOUND = 16; - const RESPAWN = 17; - //TODO add new events + const RESPAWN = 18; + + //TODO: add more events public $eid; public $event; public $unknown; public function decode(){ - $this->eid = $this->getEntityId(); + $this->eid = $this->getEntityRuntimeId(); $this->event = $this->getByte(); $this->unknown = $this->getVarInt(); } public function encode(){ $this->reset(); - $this->putEntityId($this->eid); + $this->putEntityRuntimeId($this->eid); $this->putByte($this->event); $this->putVarInt($this->unknown); } + public function handle(NetworkSession $session) : bool{ + return $session->handleEntityEvent($this); + } + } diff --git a/src/pocketmine/network/protocol/ExplodePacket.php b/src/pocketmine/network/mcpe/protocol/ExplodePacket.php similarity index 80% rename from src/pocketmine/network/protocol/ExplodePacket.php rename to src/pocketmine/network/mcpe/protocol/ExplodePacket.php index 7804b5ca2..c298e6351 100644 --- a/src/pocketmine/network/protocol/ExplodePacket.php +++ b/src/pocketmine/network/mcpe/protocol/ExplodePacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class ExplodePacket extends DataPacket{ - const NETWORK_ID = Info::EXPLODE_PACKET; + const NETWORK_ID = ProtocolInfo::EXPLODE_PACKET; public $x; public $y; @@ -49,9 +51,13 @@ class ExplodePacket extends DataPacket{ $this->putUnsignedVarInt(count($this->records)); if(count($this->records) > 0){ foreach($this->records as $record){ - $this->putBlockCoords($record->x, $record->y, $record->z); + $this->putBlockPosition($record->x, $record->y, $record->z); } } } + public function handle(NetworkSession $session) : bool{ + return $session->handleExplode($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/FullChunkDataPacket.php b/src/pocketmine/network/mcpe/protocol/FullChunkDataPacket.php similarity index 80% rename from src/pocketmine/network/protocol/FullChunkDataPacket.php rename to src/pocketmine/network/mcpe/protocol/FullChunkDataPacket.php index 02d9870fe..6610492b6 100644 --- a/src/pocketmine/network/protocol/FullChunkDataPacket.php +++ b/src/pocketmine/network/mcpe/protocol/FullChunkDataPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class FullChunkDataPacket extends DataPacket{ - const NETWORK_ID = Info::FULL_CHUNK_DATA_PACKET; + const NETWORK_ID = ProtocolInfo::FULL_CHUNK_DATA_PACKET; public $chunkX; public $chunkZ; @@ -42,4 +44,8 @@ class FullChunkDataPacket extends DataPacket{ $this->putString($this->data); } + public function handle(NetworkSession $session) : bool{ + return $session->handleFullChunkData($this); + } + } diff --git a/src/pocketmine/network/protocol/HurtArmorPacket.php b/src/pocketmine/network/mcpe/protocol/HurtArmorPacket.php similarity index 78% rename from src/pocketmine/network/protocol/HurtArmorPacket.php rename to src/pocketmine/network/mcpe/protocol/HurtArmorPacket.php index e16147283..644c0bf82 100644 --- a/src/pocketmine/network/protocol/HurtArmorPacket.php +++ b/src/pocketmine/network/mcpe/protocol/HurtArmorPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class HurtArmorPacket extends DataPacket{ - const NETWORK_ID = Info::HURT_ARMOR_PACKET; + const NETWORK_ID = ProtocolInfo::HURT_ARMOR_PACKET; public $health; @@ -38,4 +40,8 @@ class HurtArmorPacket extends DataPacket{ $this->putVarInt($this->health); } + public function handle(NetworkSession $session) : bool{ + return $session->handleHurtArmor($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/InteractPacket.php b/src/pocketmine/network/mcpe/protocol/InteractPacket.php similarity index 76% rename from src/pocketmine/network/protocol/InteractPacket.php rename to src/pocketmine/network/mcpe/protocol/InteractPacket.php index fada11c3d..604342505 100644 --- a/src/pocketmine/network/protocol/InteractPacket.php +++ b/src/pocketmine/network/mcpe/protocol/InteractPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class InteractPacket extends DataPacket{ - const NETWORK_ID = Info::INTERACT_PACKET; + const NETWORK_ID = ProtocolInfo::INTERACT_PACKET; const ACTION_RIGHT_CLICK = 1; const ACTION_LEFT_CLICK = 2; @@ -37,13 +39,17 @@ class InteractPacket extends DataPacket{ public function decode(){ $this->action = $this->getByte(); - $this->target = $this->getEntityId(); + $this->target = $this->getEntityRuntimeId(); } public function encode(){ $this->reset(); $this->putByte($this->action); - $this->putEntityId($this->target); + $this->putEntityRuntimeId($this->target); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleInteract($this); } } diff --git a/src/pocketmine/network/protocol/InventoryActionPacket.php b/src/pocketmine/network/mcpe/protocol/InventoryActionPacket.php similarity index 78% rename from src/pocketmine/network/protocol/InventoryActionPacket.php rename to src/pocketmine/network/mcpe/protocol/InventoryActionPacket.php index f31c569a4..ac13818f7 100644 --- a/src/pocketmine/network/protocol/InventoryActionPacket.php +++ b/src/pocketmine/network/mcpe/protocol/InventoryActionPacket.php @@ -19,12 +19,14 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class InventoryActionPacket extends DataPacket{ - const NETWORK_ID = Info::INVENTORY_ACTION_PACKET; + const NETWORK_ID = ProtocolInfo::INVENTORY_ACTION_PACKET; public $unknown; public $item; @@ -37,4 +39,8 @@ class InventoryActionPacket extends DataPacket{ $this->putUnsignedVarInt($this->unknown); $this->putSlot($this->item); } + + public function handle(NetworkSession $session) : bool{ + return $session->handleInventoryAction($this); + } } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/ItemFrameDropItemPacket.php b/src/pocketmine/network/mcpe/protocol/ItemFrameDropItemPacket.php similarity index 73% rename from src/pocketmine/network/protocol/ItemFrameDropItemPacket.php rename to src/pocketmine/network/mcpe/protocol/ItemFrameDropItemPacket.php index 267d79a27..9a101c420 100644 --- a/src/pocketmine/network/protocol/ItemFrameDropItemPacket.php +++ b/src/pocketmine/network/mcpe/protocol/ItemFrameDropItemPacket.php @@ -19,24 +19,30 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class ItemFrameDropItemPacket extends DataPacket{ - const NETWORK_ID = Info::ITEM_FRAME_DROP_ITEM_PACKET; + const NETWORK_ID = ProtocolInfo::ITEM_FRAME_DROP_ITEM_PACKET; public $x; public $y; public $z; public function decode(){ - $this->getBlockCoords($this->x, $this->y, $this->z); + $this->getBlockPosition($this->x, $this->y, $this->z); } public function encode(){ } + public function handle(NetworkSession $session) : bool{ + return $session->handleItemFrameDropItem($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/LevelEventPacket.php b/src/pocketmine/network/mcpe/protocol/LevelEventPacket.php similarity index 92% rename from src/pocketmine/network/protocol/LevelEventPacket.php rename to src/pocketmine/network/mcpe/protocol/LevelEventPacket.php index 86aa244cb..a044a9954 100644 --- a/src/pocketmine/network/protocol/LevelEventPacket.php +++ b/src/pocketmine/network/mcpe/protocol/LevelEventPacket.php @@ -19,12 +19,14 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class LevelEventPacket extends DataPacket{ - const NETWORK_ID = Info::LEVEL_EVENT_PACKET; + const NETWORK_ID = ProtocolInfo::LEVEL_EVENT_PACKET; const EVENT_SOUND_CLICK = 1000; const EVENT_SOUND_CLICK_FAIL = 1001; @@ -107,4 +109,8 @@ class LevelEventPacket extends DataPacket{ $this->putVarInt($this->data); } + public function handle(NetworkSession $session) : bool{ + return $session->handleLevelEvent($this); + } + } diff --git a/src/pocketmine/network/protocol/LevelSoundEventPacket.php b/src/pocketmine/network/mcpe/protocol/LevelSoundEventPacket.php similarity index 94% rename from src/pocketmine/network/protocol/LevelSoundEventPacket.php rename to src/pocketmine/network/mcpe/protocol/LevelSoundEventPacket.php index 27f675627..63d07cc51 100644 --- a/src/pocketmine/network/protocol/LevelSoundEventPacket.php +++ b/src/pocketmine/network/mcpe/protocol/LevelSoundEventPacket.php @@ -19,12 +19,14 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class LevelSoundEventPacket extends DataPacket{ - const NETWORK_ID = Info::LEVEL_SOUND_EVENT_PACKET; + const NETWORK_ID = ProtocolInfo::LEVEL_SOUND_EVENT_PACKET; const SOUND_ITEM_USE_ON = 0; const SOUND_HIT = 1; @@ -150,4 +152,8 @@ class LevelSoundEventPacket extends DataPacket{ $this->putBool($this->unknownBool); $this->putBool($this->unknownBool2); } + + public function handle(NetworkSession $session) : bool{ + return $session->handleLevelSoundEvent($this); + } } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/LoginPacket.php b/src/pocketmine/network/mcpe/protocol/LoginPacket.php similarity index 84% rename from src/pocketmine/network/protocol/LoginPacket.php rename to src/pocketmine/network/mcpe/protocol/LoginPacket.php index 2b50f78fe..b8a596475 100644 --- a/src/pocketmine/network/protocol/LoginPacket.php +++ b/src/pocketmine/network/mcpe/protocol/LoginPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class LoginPacket extends DataPacket{ - const NETWORK_ID = Info::LOGIN_PACKET; + const NETWORK_ID = ProtocolInfo::LOGIN_PACKET; const EDITION_POCKET = 0; @@ -38,14 +40,22 @@ class LoginPacket extends DataPacket{ public $serverAddress; public $skinId; - public $skin = null; + public $skin = ""; public $clientData = []; + public function canBeBatched() : bool{ + return false; + } + + public function canBeSentBeforeLogin() : bool{ + return true; + } + public function decode(){ $this->protocol = $this->getInt(); - if($this->protocol !== Info::CURRENT_PROTOCOL){ + if($this->protocol !== ProtocolInfo::CURRENT_PROTOCOL){ $this->buffer = null; return; //Do not attempt to decode for non-accepted protocols } @@ -93,4 +103,8 @@ class LoginPacket extends DataPacket{ return json_decode(base64_decode($payloadB64), true); } + + public function handle(NetworkSession $session) : bool{ + return $session->handleLogin($this); + } } \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/MapInfoRequestPacket.php b/src/pocketmine/network/mcpe/protocol/MapInfoRequestPacket.php new file mode 100644 index 000000000..98b65a970 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/MapInfoRequestPacket.php @@ -0,0 +1,47 @@ + + + +use pocketmine\network\mcpe\NetworkSession; + +class MapInfoRequestPacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::MAP_INFO_REQUEST_PACKET; + + public $mapId; + + public function decode(){ + $this->mapId = $this->getVarInt(); //signed var-long, actually entity ID (needs fixing) + } + + public function encode(){ + $this->reset(); + $this->putVarInt($this->mapId); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleMapInfoRequest($this); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/protocol/MobArmorEquipmentPacket.php b/src/pocketmine/network/mcpe/protocol/MobArmorEquipmentPacket.php similarity index 77% rename from src/pocketmine/network/protocol/MobArmorEquipmentPacket.php rename to src/pocketmine/network/mcpe/protocol/MobArmorEquipmentPacket.php index 465376dbf..63cf63587 100644 --- a/src/pocketmine/network/protocol/MobArmorEquipmentPacket.php +++ b/src/pocketmine/network/mcpe/protocol/MobArmorEquipmentPacket.php @@ -19,19 +19,21 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class MobArmorEquipmentPacket extends DataPacket{ - const NETWORK_ID = Info::MOB_ARMOR_EQUIPMENT_PACKET; + const NETWORK_ID = ProtocolInfo::MOB_ARMOR_EQUIPMENT_PACKET; public $eid; public $slots = []; public function decode(){ - $this->eid = $this->getEntityId(); + $this->eid = $this->getEntityRuntimeId(); $this->slots[0] = $this->getSlot(); $this->slots[1] = $this->getSlot(); $this->slots[2] = $this->getSlot(); @@ -40,11 +42,15 @@ class MobArmorEquipmentPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->eid); + $this->putEntityRuntimeId($this->eid); $this->putSlot($this->slots[0]); $this->putSlot($this->slots[1]); $this->putSlot($this->slots[2]); $this->putSlot($this->slots[3]); } + public function handle(NetworkSession $session) : bool{ + return $session->handleMobArmorEquipment($this); + } + } diff --git a/src/pocketmine/network/protocol/MobEffectPacket.php b/src/pocketmine/network/mcpe/protocol/MobEffectPacket.php similarity index 81% rename from src/pocketmine/network/protocol/MobEffectPacket.php rename to src/pocketmine/network/mcpe/protocol/MobEffectPacket.php index 13deabae6..be64a6c25 100644 --- a/src/pocketmine/network/protocol/MobEffectPacket.php +++ b/src/pocketmine/network/mcpe/protocol/MobEffectPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class MobEffectPacket extends DataPacket{ - const NETWORK_ID = Info::MOB_EFFECT_PACKET; + const NETWORK_ID = ProtocolInfo::MOB_EFFECT_PACKET; const EVENT_ADD = 1; const EVENT_MODIFY = 2; @@ -44,7 +46,7 @@ class MobEffectPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->eid); + $this->putEntityRuntimeId($this->eid); $this->putByte($this->eventId); $this->putVarInt($this->effectId); $this->putVarInt($this->amplifier); @@ -52,4 +54,8 @@ class MobEffectPacket extends DataPacket{ $this->putVarInt($this->duration); } + public function handle(NetworkSession $session) : bool{ + return $session->handleMobEffect($this); + } + } diff --git a/src/pocketmine/network/protocol/MobEquipmentPacket.php b/src/pocketmine/network/mcpe/protocol/MobEquipmentPacket.php similarity index 79% rename from src/pocketmine/network/protocol/MobEquipmentPacket.php rename to src/pocketmine/network/mcpe/protocol/MobEquipmentPacket.php index eea4c6583..d87d294a1 100644 --- a/src/pocketmine/network/protocol/MobEquipmentPacket.php +++ b/src/pocketmine/network/mcpe/protocol/MobEquipmentPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class MobEquipmentPacket extends DataPacket{ - const NETWORK_ID = Info::MOB_EQUIPMENT_PACKET; + const NETWORK_ID = ProtocolInfo::MOB_EQUIPMENT_PACKET; public $eid; public $item; @@ -34,7 +36,7 @@ class MobEquipmentPacket extends DataPacket{ public $unknownByte; public function decode(){ - $this->eid = $this->getEntityId(); //EntityRuntimeID + $this->eid = $this->getEntityRuntimeId(); $this->item = $this->getSlot(); $this->inventorySlot = $this->getByte(); $this->hotbarSlot = $this->getByte(); @@ -43,11 +45,15 @@ class MobEquipmentPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->eid); //EntityRuntimeID + $this->putEntityRuntimeId($this->eid); $this->putSlot($this->item); $this->putByte($this->inventorySlot); $this->putByte($this->hotbarSlot); $this->putByte($this->unknownByte); } + public function handle(NetworkSession $session) : bool{ + return $session->handleMobEquipment($this); + } + } diff --git a/src/pocketmine/network/protocol/MoveEntityPacket.php b/src/pocketmine/network/mcpe/protocol/MoveEntityPacket.php similarity index 80% rename from src/pocketmine/network/protocol/MoveEntityPacket.php rename to src/pocketmine/network/mcpe/protocol/MoveEntityPacket.php index d9a047a42..fbda02c9e 100644 --- a/src/pocketmine/network/protocol/MoveEntityPacket.php +++ b/src/pocketmine/network/mcpe/protocol/MoveEntityPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class MoveEntityPacket extends DataPacket{ - const NETWORK_ID = Info::MOVE_ENTITY_PACKET; + const NETWORK_ID = ProtocolInfo::MOVE_ENTITY_PACKET; public $eid; public $x; @@ -36,7 +38,7 @@ class MoveEntityPacket extends DataPacket{ public $pitch; public function decode(){ - $this->eid = $this->getEntityId(); + $this->eid = $this->getEntityRuntimeId(); $this->getVector3f($this->x, $this->y, $this->z); $this->pitch = $this->getByte() * (360.0 / 256); $this->yaw = $this->getByte() * (360.0 / 256); @@ -45,11 +47,15 @@ class MoveEntityPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->eid); + $this->putEntityRuntimeId($this->eid); $this->putVector3f($this->x, $this->y, $this->z); $this->putByte($this->pitch / (360.0 / 256)); $this->putByte($this->yaw / (360.0 / 256)); $this->putByte($this->headYaw / (360.0 / 256)); } + public function handle(NetworkSession $session) : bool{ + return $session->handleMoveEntity($this); + } + } diff --git a/src/pocketmine/network/protocol/MovePlayerPacket.php b/src/pocketmine/network/mcpe/protocol/MovePlayerPacket.php similarity index 82% rename from src/pocketmine/network/protocol/MovePlayerPacket.php rename to src/pocketmine/network/mcpe/protocol/MovePlayerPacket.php index a48218ada..24a5644e6 100644 --- a/src/pocketmine/network/protocol/MovePlayerPacket.php +++ b/src/pocketmine/network/mcpe/protocol/MovePlayerPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class MovePlayerPacket extends DataPacket{ - const NETWORK_ID = Info::MOVE_PLAYER_PACKET; + const NETWORK_ID = ProtocolInfo::MOVE_PLAYER_PACKET; const MODE_NORMAL = 0; const MODE_RESET = 1; @@ -42,7 +44,7 @@ class MovePlayerPacket extends DataPacket{ public $onGround; public function decode(){ - $this->eid = $this->getEntityId(); //EntityRuntimeID + $this->eid = $this->getEntityRuntimeId(); $this->getVector3f($this->x, $this->y, $this->z); $this->pitch = $this->getLFloat(); $this->yaw = $this->getLFloat(); @@ -53,7 +55,7 @@ class MovePlayerPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->eid); //EntityRuntimeID + $this->putEntityRuntimeId($this->eid); $this->putVector3f($this->x, $this->y, $this->z); $this->putLFloat($this->pitch); $this->putLFloat($this->yaw); @@ -62,4 +64,8 @@ class MovePlayerPacket extends DataPacket{ $this->putBool($this->onGround); } + public function handle(NetworkSession $session) : bool{ + return $session->handleMovePlayer($this); + } + } diff --git a/src/pocketmine/network/mcpe/protocol/PlaySoundPacket.php b/src/pocketmine/network/mcpe/protocol/PlaySoundPacket.php new file mode 100644 index 000000000..ba40a6a82 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/PlaySoundPacket.php @@ -0,0 +1,58 @@ + + + +use pocketmine\network\mcpe\NetworkSession; + +class PlaySoundPacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::PLAY_SOUND_PACKET; + + public $string1; + public $x; + public $y; + public $z; + public $float1; + public $float2; + + public function decode(){ + $this->string1 = $this->getString(); + $this->getBlockPosition($this->x, $this->y, $this->z); + $this->float1 = $this->getLFloat(); + $this->float2 = $this->getLFloat(); + } + + public function encode(){ + $this->reset(); + $this->putString($this->string1); + $this->putBlockPosition($this->x, $this->y, $this->z); + $this->putLFloat($this->float1); + $this->putLFloat($this->float2); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handlePlaySound($this); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/protocol/PlayStatusPacket.php b/src/pocketmine/network/mcpe/protocol/PlayStatusPacket.php similarity index 72% rename from src/pocketmine/network/protocol/PlayStatusPacket.php rename to src/pocketmine/network/mcpe/protocol/PlayStatusPacket.php index 47b695ff6..621f6c05c 100644 --- a/src/pocketmine/network/protocol/PlayStatusPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayStatusPacket.php @@ -19,18 +19,22 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class PlayStatusPacket extends DataPacket{ - const NETWORK_ID = Info::PLAY_STATUS_PACKET; + const NETWORK_ID = ProtocolInfo::PLAY_STATUS_PACKET; const LOGIN_SUCCESS = 0; const LOGIN_FAILED_CLIENT = 1; const LOGIN_FAILED_SERVER = 2; const PLAYER_SPAWN = 3; + const LOGIN_FAILED_INVALID_TENANT = 4; + const LOGIN_FAILED_EDITION_MISMATCH = 5; public $status; @@ -38,9 +42,17 @@ class PlayStatusPacket extends DataPacket{ } + public function canBeSentBeforeLogin() : bool{ + return true; + } + public function encode(){ $this->reset(); $this->putInt($this->status); } + public function handle(NetworkSession $session) : bool{ + return $session->handlePlayStatus($this); + } + } diff --git a/src/pocketmine/network/protocol/PlayerActionPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerActionPacket.php similarity index 77% rename from src/pocketmine/network/protocol/PlayerActionPacket.php rename to src/pocketmine/network/mcpe/protocol/PlayerActionPacket.php index ff0c6d760..6d04ec3d5 100644 --- a/src/pocketmine/network/protocol/PlayerActionPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerActionPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class PlayerActionPacket extends DataPacket{ - const NETWORK_ID = Info::PLAYER_ACTION_PACKET; + const NETWORK_ID = ProtocolInfo::PLAYER_ACTION_PACKET; const ACTION_START_BREAK = 0; const ACTION_ABORT_BREAK = 1; @@ -53,18 +55,22 @@ class PlayerActionPacket extends DataPacket{ public $face; public function decode(){ - $this->eid = $this->getEntityId(); + $this->eid = $this->getEntityRuntimeId(); $this->action = $this->getVarInt(); - $this->getBlockCoords($this->x, $this->y, $this->z); + $this->getBlockPosition($this->x, $this->y, $this->z); $this->face = $this->getVarInt(); } public function encode(){ $this->reset(); - $this->putEntityId($this->eid); + $this->putEntityRuntimeId($this->eid); $this->putVarInt($this->action); - $this->putBlockCoords($this->x, $this->y, $this->z); + $this->putBlockPosition($this->x, $this->y, $this->z); $this->putVarInt($this->face); } + public function handle(NetworkSession $session) : bool{ + return $session->handlePlayerAction($this); + } + } diff --git a/src/pocketmine/network/protocol/PlayerFallPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerFallPacket.php similarity index 78% rename from src/pocketmine/network/protocol/PlayerFallPacket.php rename to src/pocketmine/network/mcpe/protocol/PlayerFallPacket.php index 91333e6c9..d254f21ed 100644 --- a/src/pocketmine/network/protocol/PlayerFallPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerFallPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class PlayerFallPacket extends DataPacket{ - const NETWORK_ID = Info::PLAYER_FALL_PACKET; + const NETWORK_ID = ProtocolInfo::PLAYER_FALL_PACKET; public $fallDistance; @@ -36,4 +38,8 @@ class PlayerFallPacket extends DataPacket{ public function encode(){ } + + public function handle(NetworkSession $session) : bool{ + return $session->handlePlayerFall($this); + } } diff --git a/src/pocketmine/network/protocol/PlayerInputPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerInputPacket.php similarity index 81% rename from src/pocketmine/network/protocol/PlayerInputPacket.php rename to src/pocketmine/network/mcpe/protocol/PlayerInputPacket.php index 6ae9edd33..f80d636c4 100644 --- a/src/pocketmine/network/protocol/PlayerInputPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerInputPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class PlayerInputPacket extends DataPacket{ - const NETWORK_ID = Info::PLAYER_INPUT_PACKET; + const NETWORK_ID = ProtocolInfo::PLAYER_INPUT_PACKET; public $motionX; public $motionY; @@ -43,4 +45,8 @@ class PlayerInputPacket extends DataPacket{ } + public function handle(NetworkSession $session) : bool{ + return $session->handlePlayerInput($this); + } + } diff --git a/src/pocketmine/network/protocol/PlayerListPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php similarity index 83% rename from src/pocketmine/network/protocol/PlayerListPacket.php rename to src/pocketmine/network/mcpe/protocol/PlayerListPacket.php index 27c14e918..07e95483f 100644 --- a/src/pocketmine/network/protocol/PlayerListPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class PlayerListPacket extends DataPacket{ - const NETWORK_ID = Info::PLAYER_LIST_PACKET; + const NETWORK_ID = ProtocolInfo::PLAYER_LIST_PACKET; const TYPE_ADD = 0; const TYPE_REMOVE = 1; @@ -51,7 +53,7 @@ class PlayerListPacket extends DataPacket{ foreach($this->entries as $d){ if($this->type === self::TYPE_ADD){ $this->putUUID($d[0]); - $this->putEntityId($d[1]); + $this->putEntityUniqueId($d[1]); $this->putString($d[2]); $this->putString($d[3]); $this->putString($d[4]); @@ -61,4 +63,8 @@ class PlayerListPacket extends DataPacket{ } } + public function handle(NetworkSession $session) : bool{ + return $session->handlePlayerList($this); + } + } diff --git a/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php b/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php new file mode 100644 index 000000000..e789512ff --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php @@ -0,0 +1,127 @@ + +use pocketmine\network\mcpe\NetworkSession; + class RemoveBlockPacket extends DataPacket{ - const NETWORK_ID = Info::REMOVE_BLOCK_PACKET; + const NETWORK_ID = ProtocolInfo::REMOVE_BLOCK_PACKET; public $x; public $y; public $z; public function decode(){ - $this->getBlockCoords($this->x, $this->y, $this->z); + $this->getBlockPosition($this->x, $this->y, $this->z); } public function encode(){ } + public function handle(NetworkSession $session) : bool{ + return $session->handleRemoveBlock($this); + } + } diff --git a/src/pocketmine/network/protocol/RemoveEntityPacket.php b/src/pocketmine/network/mcpe/protocol/RemoveEntityPacket.php similarity index 75% rename from src/pocketmine/network/protocol/RemoveEntityPacket.php rename to src/pocketmine/network/mcpe/protocol/RemoveEntityPacket.php index 13c5c8072..a75da9742 100644 --- a/src/pocketmine/network/protocol/RemoveEntityPacket.php +++ b/src/pocketmine/network/mcpe/protocol/RemoveEntityPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class RemoveEntityPacket extends DataPacket{ - const NETWORK_ID = Info::REMOVE_ENTITY_PACKET; + const NETWORK_ID = ProtocolInfo::REMOVE_ENTITY_PACKET; public $eid; @@ -35,7 +37,11 @@ class RemoveEntityPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->eid); + $this->putEntityUniqueId($this->eid); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleRemoveEntity($this); } } diff --git a/src/pocketmine/network/protocol/ReplaceItemInSlotPacket.php b/src/pocketmine/network/mcpe/protocol/ReplaceItemInSlotPacket.php similarity index 77% rename from src/pocketmine/network/protocol/ReplaceItemInSlotPacket.php rename to src/pocketmine/network/mcpe/protocol/ReplaceItemInSlotPacket.php index fa0bd3141..1a2c9317b 100644 --- a/src/pocketmine/network/protocol/ReplaceItemInSlotPacket.php +++ b/src/pocketmine/network/mcpe/protocol/ReplaceItemInSlotPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class ReplaceItemInSlotPacket extends DataPacket{ - const NETWORK_ID = Info::REPLACE_ITEM_IN_SLOT_PACKET; + const NETWORK_ID = ProtocolInfo::REPLACE_ITEM_IN_SLOT_PACKET; public $item; @@ -38,4 +40,7 @@ class ReplaceItemInSlotPacket extends DataPacket{ $this->putSlot($this->item); } + public function handle(NetworkSession $session) : bool{ + return $session->handleReplaceItemInSlot($this); + } } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/RequestChunkRadiusPacket.php b/src/pocketmine/network/mcpe/protocol/RequestChunkRadiusPacket.php similarity index 77% rename from src/pocketmine/network/protocol/RequestChunkRadiusPacket.php rename to src/pocketmine/network/mcpe/protocol/RequestChunkRadiusPacket.php index 51bda5f6c..5a3d1bbe6 100644 --- a/src/pocketmine/network/protocol/RequestChunkRadiusPacket.php +++ b/src/pocketmine/network/mcpe/protocol/RequestChunkRadiusPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class RequestChunkRadiusPacket extends DataPacket{ - const NETWORK_ID = Info::REQUEST_CHUNK_RADIUS_PACKET; + const NETWORK_ID = ProtocolInfo::REQUEST_CHUNK_RADIUS_PACKET; public $radius; @@ -34,6 +36,11 @@ class RequestChunkRadiusPacket extends DataPacket{ } public function encode(){ + + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleRequestChunkRadius($this); } } diff --git a/src/pocketmine/network/mcpe/protocol/ResourcePackChunkDataPacket.php b/src/pocketmine/network/mcpe/protocol/ResourcePackChunkDataPacket.php new file mode 100644 index 000000000..9612449db --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/ResourcePackChunkDataPacket.php @@ -0,0 +1,57 @@ + + + +use pocketmine\network\mcpe\NetworkSession; + +class ResourcePackChunkDataPacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::RESOURCE_PACK_CHUNK_DATA_PACKET; + + public $packId; + public $chunkIndex; + public $progress; + public $data; + + public function decode(){ + $this->packId = $this->getString(); + $this->chunkIndex = $this->getLInt(); + $this->progress = $this->getLLong(); + $this->data = $this->get($this->getLInt()); + } + + public function encode(){ + $this->reset(); + $this->putString($this->packId); + $this->putLInt($this->chunkIndex); + $this->putLLong($this->progress); + $this->putLInt(strlen($this->data)); + $this->put($this->data); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleResourcePackChunkData($this); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/ResourcePackChunkRequestPacket.php b/src/pocketmine/network/mcpe/protocol/ResourcePackChunkRequestPacket.php new file mode 100644 index 000000000..d9b89bcef --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/ResourcePackChunkRequestPacket.php @@ -0,0 +1,50 @@ + + + +use pocketmine\network\mcpe\NetworkSession; + +class ResourcePackChunkRequestPacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::RESOURCE_PACK_CHUNK_REQUEST_PACKET; + + public $packId; + public $chunkIndex; + + public function decode(){ + $this->packId = $this->getString(); + $this->chunkIndex = $this->getLInt(); + } + + public function encode(){ + $this->reset(); + $this->putString($this->packId); + $this->putLInt($this->chunkIndex); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleResourcePackChunkRequest($this); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/ResourcePackClientResponsePacket.php b/src/pocketmine/network/mcpe/protocol/ResourcePackClientResponsePacket.php new file mode 100644 index 000000000..78ced44fb --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/ResourcePackClientResponsePacket.php @@ -0,0 +1,61 @@ + + + +use pocketmine\network\mcpe\NetworkSession; + +class ResourcePackClientResponsePacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::RESOURCE_PACK_CLIENT_RESPONSE_PACKET; + + const STATUS_REFUSED = 1; + const STATUS_SEND_PACKS = 2; + const STATUS_HAVE_ALL_PACKS = 3; + const STATUS_COMPLETED = 4; + + public $status; + public $packIds = []; + + public function decode(){ + $this->status = $this->getByte(); + $entryCount = $this->getLShort(); + while($entryCount-- > 0){ + $this->packIds[] = $this->getString(); + } + } + + public function encode(){ + $this->reset(); + $this->putByte($this->status); + $this->putLShort(count($this->packIds)); + foreach($this->packIds as $id){ + $this->putString($id); + } + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleResourcePackClientResponse($this); + } + +} diff --git a/src/pocketmine/network/mcpe/protocol/ResourcePackDataInfoPacket.php b/src/pocketmine/network/mcpe/protocol/ResourcePackDataInfoPacket.php new file mode 100644 index 000000000..c8970f55e --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/ResourcePackDataInfoPacket.php @@ -0,0 +1,60 @@ + + + +use pocketmine\network\mcpe\NetworkSession; + +class ResourcePackDataInfoPacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::RESOURCE_PACK_DATA_INFO_PACKET; + + public $packId; + public $maxChunkSize; + public $chunkCount; + public $compressedPackSize; + public $sha256; + + public function decode(){ + $this->packId = $this->getString(); + $this->maxChunkSize = $this->getLInt(); + $this->chunkCount = $this->getLInt(); + $this->compressedPackSize = $this->getLLong(); + $this->sha256 = $this->getString(); + } + + public function encode(){ + $this->reset(); + $this->putString($this->packId); + $this->putLInt($this->maxChunkSize); + $this->putLInt($this->chunkCount); + $this->putLLong($this->compressedPackSize); + $this->putString($this->sha256); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleResourcePackDataInfo($this); + } + +} \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/ResourcePackStackPacket.php b/src/pocketmine/network/mcpe/protocol/ResourcePackStackPacket.php new file mode 100644 index 000000000..32633a7fb --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/ResourcePackStackPacket.php @@ -0,0 +1,79 @@ + + + +use pocketmine\network\mcpe\NetworkSession; +use pocketmine\resourcepacks\ResourcePack; +use pocketmine\resourcepacks\ResourcePackInfoEntry; + +class ResourcePackStackPacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::RESOURCE_PACK_STACK_PACKET; + + public $mustAccept = false; + + /** @var ResourcePack[] */ + public $behaviorPackStack = []; + /** @var ResourcePack[] */ + public $resourcePackStack = []; + + public function decode(){ + /*$this->mustAccept = $this->getBool(); + $behaviorPackCount = $this->getLShort(); + while($behaviorPackCount-- > 0){ + $packId = $this->getString(); + $version = $this->getString(); + $this->behaviorPackStack[] = new ResourcePackInfoEntry($packId, $version); + } + + $resourcePackCount = $this->getLShort(); + while($resourcePackCount-- > 0){ + $packId = $this->getString(); + $version = $this->getString(); + $this->resourcePackStack[] = new ResourcePackInfoEntry($packId, $version); + }*/ + } + + public function encode(){ + $this->reset(); + $this->putBool($this->mustAccept); + + $this->putLShort(count($this->behaviorPackStack)); + foreach($this->behaviorPackStack as $entry){ + $this->putString($entry->getPackId()); + $this->putString($entry->getPackVersion()); + } + + $this->putLShort(count($this->resourcePackStack)); + foreach($this->resourcePackStack as $entry){ + $this->putString($entry->getPackId()); + $this->putString($entry->getPackVersion()); + } + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleResourcePackStack($this); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/protocol/ResourcePacksInfoPacket.php b/src/pocketmine/network/mcpe/protocol/ResourcePacksInfoPacket.php similarity index 53% rename from src/pocketmine/network/protocol/ResourcePacksInfoPacket.php rename to src/pocketmine/network/mcpe/protocol/ResourcePacksInfoPacket.php index 26aa79beb..6162056ae 100644 --- a/src/pocketmine/network/protocol/ResourcePacksInfoPacket.php +++ b/src/pocketmine/network/mcpe/protocol/ResourcePacksInfoPacket.php @@ -19,41 +19,66 @@ * */ -namespace pocketmine\network\protocol; - -use pocketmine\resourcepacks\ResourcePackInfoEntry; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; +use pocketmine\resourcepacks\ResourcePack; +use pocketmine\resourcepacks\ResourcePackInfoEntry; + class ResourcePacksInfoPacket extends DataPacket{ - const NETWORK_ID = Info::RESOURCE_PACKS_INFO_PACKET; + const NETWORK_ID = ProtocolInfo::RESOURCE_PACKS_INFO_PACKET; public $mustAccept = false; //if true, forces client to use selected resource packs - /** @var ResourcePackInfoEntry[] */ + /** @var ResourcePack[] */ public $behaviorPackEntries = []; - /** @var ResourcePackInfoEntry[] */ + /** @var ResourcePack[] */ public $resourcePackEntries = []; public function decode(){ + /*$this->mustAccept = $this->getBool(); + $behaviorPackCount = $this->getLShort(); + while($behaviorPackCount-- > 0){ + $id = $this->getString(); + $version = $this->getString(); + $size = $this->getLLong(); + $this->behaviorPackEntries[] = new ResourcePackInfoEntry($id, $version, $size); + $this->getString(); + } + $resourcePackCount = $this->getLShort(); + while($resourcePackCount-- > 0){ + $id = $this->getString(); + $version = $this->getString(); + $size = $this->getLLong(); + $this->resourcePackEntries[] = new ResourcePackInfoEntry($id, $version, $size); + $this->getString(); + }*/ } public function encode(){ $this->reset(); $this->putBool($this->mustAccept); - $this->putShort(count($this->behaviorPackEntries)); + $this->putLShort(count($this->behaviorPackEntries)); foreach($this->behaviorPackEntries as $entry){ $this->putString($entry->getPackId()); - $this->putString($entry->getVersion()); + $this->putString($entry->getPackVersion()); $this->putLLong($entry->getPackSize()); + $this->putString(""); //TODO } - $this->putShort(count($this->resourcePackEntries)); + $this->putLShort(count($this->resourcePackEntries)); foreach($this->resourcePackEntries as $entry){ $this->putString($entry->getPackId()); - $this->putString($entry->getVersion()); + $this->putString($entry->getPackVersion()); $this->putLLong($entry->getPackSize()); + $this->putString(""); //TODO } } + + public function handle(NetworkSession $session) : bool{ + return $session->handleResourcePacksInfo($this); + } } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/RespawnPacket.php b/src/pocketmine/network/mcpe/protocol/RespawnPacket.php similarity index 81% rename from src/pocketmine/network/protocol/RespawnPacket.php rename to src/pocketmine/network/mcpe/protocol/RespawnPacket.php index b7dd15dfc..1af41d7e8 100644 --- a/src/pocketmine/network/protocol/RespawnPacket.php +++ b/src/pocketmine/network/mcpe/protocol/RespawnPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class RespawnPacket extends DataPacket{ - const NETWORK_ID = Info::RESPAWN_PACKET; + const NETWORK_ID = ProtocolInfo::RESPAWN_PACKET; public $x; public $y; @@ -44,4 +46,8 @@ class RespawnPacket extends DataPacket{ $this->putLFloat($this->z); } + public function handle(NetworkSession $session) : bool{ + return $session->handleRespawn($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/BatchPacket.php b/src/pocketmine/network/mcpe/protocol/RiderJumpPacket.php similarity index 68% rename from src/pocketmine/network/protocol/BatchPacket.php rename to src/pocketmine/network/mcpe/protocol/RiderJumpPacket.php index f8a6ecbda..f13917fc8 100644 --- a/src/pocketmine/network/protocol/BatchPacket.php +++ b/src/pocketmine/network/mcpe/protocol/RiderJumpPacket.php @@ -19,23 +19,29 @@ * */ -namespace pocketmine\network\protocol; + +namespace pocketmine\network\mcpe\protocol; #include -class BatchPacket extends DataPacket{ - const NETWORK_ID = Info::BATCH_PACKET; +use pocketmine\network\mcpe\NetworkSession; - public $payload; +class RiderJumpPacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::RIDER_JUMP_PACKET; + + public $unknown; public function decode(){ - $this->payload = $this->getString(); + $this->unknown = $this->getVarInt(); } public function encode(){ $this->reset(); - $this->putString($this->payload); + $this->putVarInt($this->unknown); } + public function handle(NetworkSession $session) : bool{ + return $session->handleRiderJump($this); + } } \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/ServerToClientHandshakePacket.php b/src/pocketmine/network/mcpe/protocol/ServerToClientHandshakePacket.php new file mode 100644 index 000000000..ce6a236b0 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/ServerToClientHandshakePacket.php @@ -0,0 +1,53 @@ + + + +use pocketmine\network\mcpe\NetworkSession; + +class ServerToClientHandshakePacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::SERVER_TO_CLIENT_HANDSHAKE_PACKET; + + public $publicKey; + public $serverToken; + + public function canBeSentBeforeLogin() : bool{ + return true; + } + + public function decode(){ + $this->publicKey = $this->getString(); + $this->serverToken = $this->getString(); + } + + public function encode(){ + $this->reset(); + $this->putString($this->publicKey); + $this->putString($this->serverToken); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleServerToClientHandshake($this); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/protocol/SetCommandsEnabledPacket.php b/src/pocketmine/network/mcpe/protocol/SetCommandsEnabledPacket.php similarity index 77% rename from src/pocketmine/network/protocol/SetCommandsEnabledPacket.php rename to src/pocketmine/network/mcpe/protocol/SetCommandsEnabledPacket.php index b23edeb0a..7c7529bcb 100644 --- a/src/pocketmine/network/protocol/SetCommandsEnabledPacket.php +++ b/src/pocketmine/network/mcpe/protocol/SetCommandsEnabledPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class SetCommandsEnabledPacket extends DataPacket{ - const NETWORK_ID = Info::SET_COMMANDS_ENABLED_PACKET; + const NETWORK_ID = ProtocolInfo::SET_COMMANDS_ENABLED_PACKET; public $enabled; @@ -38,4 +40,8 @@ class SetCommandsEnabledPacket extends DataPacket{ $this->putBool($this->enabled); } + public function handle(NetworkSession $session) : bool{ + return $session->handleSetCommandsEnabled($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/SetDifficultyPacket.php b/src/pocketmine/network/mcpe/protocol/SetDifficultyPacket.php similarity index 79% rename from src/pocketmine/network/protocol/SetDifficultyPacket.php rename to src/pocketmine/network/mcpe/protocol/SetDifficultyPacket.php index c56031f50..700a37d22 100644 --- a/src/pocketmine/network/protocol/SetDifficultyPacket.php +++ b/src/pocketmine/network/mcpe/protocol/SetDifficultyPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class SetDifficultyPacket extends DataPacket{ - const NETWORK_ID = Info::SET_DIFFICULTY_PACKET; + const NETWORK_ID = ProtocolInfo::SET_DIFFICULTY_PACKET; public $difficulty; @@ -38,4 +40,8 @@ class SetDifficultyPacket extends DataPacket{ $this->putUnsignedVarInt($this->difficulty); } + public function handle(NetworkSession $session) : bool{ + return $session->handleSetDifficulty($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/SetEntityDataPacket.php b/src/pocketmine/network/mcpe/protocol/SetEntityDataPacket.php similarity index 76% rename from src/pocketmine/network/protocol/SetEntityDataPacket.php rename to src/pocketmine/network/mcpe/protocol/SetEntityDataPacket.php index 32b369746..25b5437b5 100644 --- a/src/pocketmine/network/protocol/SetEntityDataPacket.php +++ b/src/pocketmine/network/mcpe/protocol/SetEntityDataPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class SetEntityDataPacket extends DataPacket{ - const NETWORK_ID = Info::SET_ENTITY_DATA_PACKET; + const NETWORK_ID = ProtocolInfo::SET_ENTITY_DATA_PACKET; public $eid; public $metadata; @@ -36,8 +38,11 @@ class SetEntityDataPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->eid); + $this->putEntityRuntimeId($this->eid); $this->putEntityMetadata($this->metadata); } + public function handle(NetworkSession $session) : bool{ + return $session->handleSetEntityData($this); + } } diff --git a/src/pocketmine/network/protocol/SetEntityLinkPacket.php b/src/pocketmine/network/mcpe/protocol/SetEntityLinkPacket.php similarity index 73% rename from src/pocketmine/network/protocol/SetEntityLinkPacket.php rename to src/pocketmine/network/mcpe/protocol/SetEntityLinkPacket.php index 6f1caf861..486df6b17 100644 --- a/src/pocketmine/network/protocol/SetEntityLinkPacket.php +++ b/src/pocketmine/network/mcpe/protocol/SetEntityLinkPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class SetEntityLinkPacket extends DataPacket{ - const NETWORK_ID = Info::SET_ENTITY_LINK_PACKET; + const NETWORK_ID = ProtocolInfo::SET_ENTITY_LINK_PACKET; public $from; public $to; @@ -37,9 +39,13 @@ class SetEntityLinkPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->from); - $this->putEntityId($this->to); + $this->putEntityUniqueId($this->from); + $this->putEntityUniqueId($this->to); $this->putByte($this->type); } + public function handle(NetworkSession $session) : bool{ + return $session->handleSetEntityLink($this); + } + } diff --git a/src/pocketmine/network/protocol/SetEntityMotionPacket.php b/src/pocketmine/network/mcpe/protocol/SetEntityMotionPacket.php similarity index 77% rename from src/pocketmine/network/protocol/SetEntityMotionPacket.php rename to src/pocketmine/network/mcpe/protocol/SetEntityMotionPacket.php index 15a735ea8..ec66b3953 100644 --- a/src/pocketmine/network/protocol/SetEntityMotionPacket.php +++ b/src/pocketmine/network/mcpe/protocol/SetEntityMotionPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class SetEntityMotionPacket extends DataPacket{ - const NETWORK_ID = Info::SET_ENTITY_MOTION_PACKET; + const NETWORK_ID = ProtocolInfo::SET_ENTITY_MOTION_PACKET; public $eid; public $motionX; @@ -38,8 +40,12 @@ class SetEntityMotionPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->eid); + $this->putEntityRuntimeId($this->eid); $this->putVector3f($this->motionX, $this->motionY, $this->motionZ); } + public function handle(NetworkSession $session) : bool{ + return $session->handleSetEntityMotion($this); + } + } diff --git a/src/pocketmine/network/protocol/SetHealthPacket.php b/src/pocketmine/network/mcpe/protocol/SetHealthPacket.php similarity index 79% rename from src/pocketmine/network/protocol/SetHealthPacket.php rename to src/pocketmine/network/mcpe/protocol/SetHealthPacket.php index 9d1ccbae5..cc3d1b582 100644 --- a/src/pocketmine/network/protocol/SetHealthPacket.php +++ b/src/pocketmine/network/mcpe/protocol/SetHealthPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class SetHealthPacket extends DataPacket{ - const NETWORK_ID = Info::SET_HEALTH_PACKET; + const NETWORK_ID = ProtocolInfo::SET_HEALTH_PACKET; public $health; @@ -38,4 +40,8 @@ class SetHealthPacket extends DataPacket{ $this->putVarInt($this->health); } + public function handle(NetworkSession $session) : bool{ + return $session->handleSetHealth($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/SetPlayerGameTypePacket.php b/src/pocketmine/network/mcpe/protocol/SetPlayerGameTypePacket.php similarity index 78% rename from src/pocketmine/network/protocol/SetPlayerGameTypePacket.php rename to src/pocketmine/network/mcpe/protocol/SetPlayerGameTypePacket.php index b47e08825..66373af8f 100644 --- a/src/pocketmine/network/protocol/SetPlayerGameTypePacket.php +++ b/src/pocketmine/network/mcpe/protocol/SetPlayerGameTypePacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class SetPlayerGameTypePacket extends DataPacket{ - const NETWORK_ID = Info::SET_PLAYER_GAME_TYPE_PACKET; + const NETWORK_ID = ProtocolInfo::SET_PLAYER_GAME_TYPE_PACKET; public $gamemode; @@ -38,4 +40,8 @@ class SetPlayerGameTypePacket extends DataPacket{ $this->putVarInt($this->gamemode); } + public function handle(NetworkSession $session) : bool{ + return $session->handleSetPlayerGameType($this); + } + } diff --git a/src/pocketmine/network/protocol/SetSpawnPositionPacket.php b/src/pocketmine/network/mcpe/protocol/SetSpawnPositionPacket.php similarity index 76% rename from src/pocketmine/network/protocol/SetSpawnPositionPacket.php rename to src/pocketmine/network/mcpe/protocol/SetSpawnPositionPacket.php index 8638ef283..0c1acd0bc 100644 --- a/src/pocketmine/network/protocol/SetSpawnPositionPacket.php +++ b/src/pocketmine/network/mcpe/protocol/SetSpawnPositionPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class SetSpawnPositionPacket extends DataPacket{ - const NETWORK_ID = Info::SET_SPAWN_POSITION_PACKET; + const NETWORK_ID = ProtocolInfo::SET_SPAWN_POSITION_PACKET; public $unknown; public $x; @@ -40,8 +42,12 @@ class SetSpawnPositionPacket extends DataPacket{ public function encode(){ $this->reset(); $this->putVarInt($this->unknown); - $this->putBlockCoords($this->x, $this->y, $this->z); + $this->putBlockPosition($this->x, $this->y, $this->z); $this->putBool($this->unknownBool); } + public function handle(NetworkSession $session) : bool{ + return $session->handleSetSpawnPosition($this); + } + } diff --git a/src/pocketmine/network/protocol/SetTimePacket.php b/src/pocketmine/network/mcpe/protocol/SetTimePacket.php similarity index 80% rename from src/pocketmine/network/protocol/SetTimePacket.php rename to src/pocketmine/network/mcpe/protocol/SetTimePacket.php index ab399a5a8..1aa00412c 100644 --- a/src/pocketmine/network/protocol/SetTimePacket.php +++ b/src/pocketmine/network/mcpe/protocol/SetTimePacket.php @@ -19,12 +19,14 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class SetTimePacket extends DataPacket{ - const NETWORK_ID = Info::SET_TIME_PACKET; + const NETWORK_ID = ProtocolInfo::SET_TIME_PACKET; public $time; public $started = true; @@ -39,4 +41,8 @@ class SetTimePacket extends DataPacket{ $this->putBool($this->started); } + public function handle(NetworkSession $session) : bool{ + return $session->handleSetTime($this); + } + } diff --git a/src/pocketmine/network/mcpe/protocol/SetTitlePacket.php b/src/pocketmine/network/mcpe/protocol/SetTitlePacket.php new file mode 100644 index 000000000..3ecf312d4 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/SetTitlePacket.php @@ -0,0 +1,66 @@ + + + +use pocketmine\network\mcpe\NetworkSession; + +class SetTitlePacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::SET_TITLE_PACKET; + + const TYPE_CLEAR_TITLE = 0; + const TYPE_RESET_TITLE = 1; + const TYPE_SET_TITLE = 2; + const TYPE_SET_SUBTITLE = 3; + const TYPE_SET_ACTIONBAR_MESSAGE = 4; + const TYPE_SET_ANIMATION_TIMES = 5; + + public $type; + public $text; + public $fadeInTime; + public $stayTime; + public $fadeOutTime; + + public function decode(){ + $this->type = $this->getVarInt(); + $this->text = $this->getString(); + $this->fadeInTime = $this->getVarInt(); + $this->stayTime = $this->getVarInt(); + $this->fadeOutTime = $this->getVarInt(); + } + + public function encode(){ + $this->reset(); + $this->putVarInt($this->type); + $this->putString($this->text); + $this->putVarInt($this->fadeInTime); + $this->putVarInt($this->stayTime); + $this->putVarInt($this->fadeOutTime); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleSetTitle($this); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/ShowCreditsPacket.php b/src/pocketmine/network/mcpe/protocol/ShowCreditsPacket.php new file mode 100644 index 000000000..03b888b0e --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/ShowCreditsPacket.php @@ -0,0 +1,53 @@ + + + +use pocketmine\network\mcpe\NetworkSession; + +class ShowCreditsPacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::SHOW_CREDITS_PACKET; + + const STATUS_START_CREDITS = 0; + const STATUS_END_CREDITS = 1; + + public $playerEid; + public $status; + + public function decode(){ + $this->playerEid = $this->getEntityRuntimeId(); + $this->status = $this->getVarInt(); + } + + public function encode(){ + $this->reset(); + $this->putEntityRuntimeId($this->playerEid); + $this->putVarInt($this->status); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleShowCredits($this); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/protocol/SpawnExperienceOrbPacket.php b/src/pocketmine/network/mcpe/protocol/SpawnExperienceOrbPacket.php similarity index 79% rename from src/pocketmine/network/protocol/SpawnExperienceOrbPacket.php rename to src/pocketmine/network/mcpe/protocol/SpawnExperienceOrbPacket.php index c3b04545c..a29fc4bd9 100644 --- a/src/pocketmine/network/protocol/SpawnExperienceOrbPacket.php +++ b/src/pocketmine/network/mcpe/protocol/SpawnExperienceOrbPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class SpawnExperienceOrbPacket extends DataPacket{ - const NETWORK_ID = Info::SPAWN_EXPERIENCE_ORB_PACKET; + const NETWORK_ID = ProtocolInfo::SPAWN_EXPERIENCE_ORB_PACKET; public $x; public $y; @@ -41,4 +43,8 @@ class SpawnExperienceOrbPacket extends DataPacket{ $this->putVector3f($this->x, $this->y, $this->z); $this->putVarInt($this->amount); } + + public function handle(NetworkSession $session) : bool{ + return $session->handleSpawnExperienceOrb($this); + } } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/StartGamePacket.php b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php similarity index 84% rename from src/pocketmine/network/protocol/StartGamePacket.php rename to src/pocketmine/network/mcpe/protocol/StartGamePacket.php index 83080aa69..6e534f96e 100644 --- a/src/pocketmine/network/protocol/StartGamePacket.php +++ b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class StartGamePacket extends DataPacket{ - const NETWORK_ID = Info::START_GAME_PACKET; + const NETWORK_ID = ProtocolInfo::START_GAME_PACKET; public $entityUniqueId; public $entityRuntimeId; @@ -56,8 +58,8 @@ class StartGamePacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->entityUniqueId); //EntityUniqueID - $this->putEntityId($this->entityRuntimeId); //EntityRuntimeID + $this->putEntityUniqueId($this->entityUniqueId); + $this->putEntityRuntimeId($this->entityRuntimeId); $this->putVector3f($this->x, $this->y, $this->z); $this->putLFloat(0); //TODO: find out what these are (yaw/pitch?) $this->putLFloat(0); @@ -66,7 +68,7 @@ class StartGamePacket extends DataPacket{ $this->putVarInt($this->generator); $this->putVarInt($this->gamemode); $this->putVarInt($this->difficulty); - $this->putBlockCoords($this->spawnX, $this->spawnY, $this->spawnZ); + $this->putBlockPosition($this->spawnX, $this->spawnY, $this->spawnZ); $this->putBool($this->hasAchievementsDisabled); $this->putVarInt($this->dayCycleStopTime); $this->putBool($this->eduMode); @@ -78,4 +80,8 @@ class StartGamePacket extends DataPacket{ $this->putString($this->worldName); } + public function handle(NetworkSession $session) : bool{ + return $session->handleStartGame($this); + } + } diff --git a/src/pocketmine/network/mcpe/protocol/StopSoundPacket.php b/src/pocketmine/network/mcpe/protocol/StopSoundPacket.php new file mode 100644 index 000000000..a339f9121 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/StopSoundPacket.php @@ -0,0 +1,50 @@ + + + +use pocketmine\network\mcpe\NetworkSession; + +class StopSoundPacket extends DataPacket{ + const NETWORK_ID = ProtocolInfo::STOP_SOUND_PACKET; + + public $string1; + public $stopAll; + + public function decode(){ + $this->string1 = $this->getString(); + $this->stopAll = $this->getBool(); + } + + public function encode(){ + $this->reset(); + $this->putString($this->string1); + $this->putBool($this->stopAll); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleStopSound($this); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/protocol/TakeItemEntityPacket.php b/src/pocketmine/network/mcpe/protocol/TakeItemEntityPacket.php similarity index 72% rename from src/pocketmine/network/protocol/TakeItemEntityPacket.php rename to src/pocketmine/network/mcpe/protocol/TakeItemEntityPacket.php index 61799541b..d4b4b4fc1 100644 --- a/src/pocketmine/network/protocol/TakeItemEntityPacket.php +++ b/src/pocketmine/network/mcpe/protocol/TakeItemEntityPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class TakeItemEntityPacket extends DataPacket{ - const NETWORK_ID = Info::TAKE_ITEM_ENTITY_PACKET; + const NETWORK_ID = ProtocolInfo::TAKE_ITEM_ENTITY_PACKET; public $target; public $eid; @@ -36,8 +38,11 @@ class TakeItemEntityPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->target); - $this->putEntityId($this->eid); + $this->putEntityRuntimeId($this->target); + $this->putEntityRuntimeId($this->eid); } + public function handle(NetworkSession $session) : bool{ + return $session->handleTakeItemEntity($this); + } } diff --git a/src/pocketmine/network/protocol/TextPacket.php b/src/pocketmine/network/mcpe/protocol/TextPacket.php similarity index 90% rename from src/pocketmine/network/protocol/TextPacket.php rename to src/pocketmine/network/mcpe/protocol/TextPacket.php index c9a6bd75d..f0c4d39d6 100644 --- a/src/pocketmine/network/protocol/TextPacket.php +++ b/src/pocketmine/network/mcpe/protocol/TextPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class TextPacket extends DataPacket{ - const NETWORK_ID = Info::TEXT_PACKET; + const NETWORK_ID = ProtocolInfo::TEXT_PACKET; const TYPE_RAW = 0; const TYPE_CHAT = 1; @@ -87,4 +89,8 @@ class TextPacket extends DataPacket{ } } + public function handle(NetworkSession $session) : bool{ + return $session->handleText($this); + } + } diff --git a/src/pocketmine/network/protocol/TransferPacket.php b/src/pocketmine/network/mcpe/protocol/TransferPacket.php similarity index 80% rename from src/pocketmine/network/protocol/TransferPacket.php rename to src/pocketmine/network/mcpe/protocol/TransferPacket.php index 5696e7bbe..1e6da8000 100644 --- a/src/pocketmine/network/protocol/TransferPacket.php +++ b/src/pocketmine/network/mcpe/protocol/TransferPacket.php @@ -19,12 +19,14 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class TransferPacket extends DataPacket{ - const NETWORK_ID = Info::TRANSFER_PACKET; + const NETWORK_ID = ProtocolInfo::TRANSFER_PACKET; public $address; public $port = 19132; @@ -39,4 +41,8 @@ class TransferPacket extends DataPacket{ $this->putLShort($this->port); } + public function handle(NetworkSession $session) : bool{ + return $session->handleTransfer($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/ResourcePackClientResponsePacket.php b/src/pocketmine/network/mcpe/protocol/UnknownPacket.php similarity index 54% rename from src/pocketmine/network/protocol/ResourcePackClientResponsePacket.php rename to src/pocketmine/network/mcpe/protocol/UnknownPacket.php index 42a714f03..87034a35e 100644 --- a/src/pocketmine/network/protocol/ResourcePackClientResponsePacket.php +++ b/src/pocketmine/network/mcpe/protocol/UnknownPacket.php @@ -19,24 +19,35 @@ * */ -namespace pocketmine\network\protocol; -#include +namespace pocketmine\network\mcpe\protocol; -class ResourcePackClientResponsePacket extends DataPacket{ - const NETWORK_ID = Info::RESOURCE_PACK_CLIENT_RESPONSE_PACKET; +use pocketmine\network\mcpe\NetworkSession; - public $unknownByte; - public $unknownShort; +class UnknownPacket extends DataPacket{ + const NETWORK_ID = -1; //Invalid, do not try to write this + + public $payload; + + public function pid(){ + if(strlen($this->payload ?? "") > 0){ + return ord($this->payload{0}); + } + return self::NETWORK_ID; + } public function decode(){ - $this->unknownByte = $this->getByte(); - $this->unknownShort = $this->getShort(); + $this->offset -= 1; //Rewind one byte so we can read the PID + $this->payload = $this->get(true); } public function encode(){ - + //Do not reset the buffer, this class does not have a valid NETWORK_ID constant. + $this->put($this->payload); } + public function handle(NetworkSession $session) : bool{ + return $session->handleUnknown($this); + } } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/UpdateAttributesPacket.php b/src/pocketmine/network/mcpe/protocol/UpdateAttributesPacket.php similarity index 80% rename from src/pocketmine/network/protocol/UpdateAttributesPacket.php rename to src/pocketmine/network/mcpe/protocol/UpdateAttributesPacket.php index fe7fc6095..9f369579f 100644 --- a/src/pocketmine/network/protocol/UpdateAttributesPacket.php +++ b/src/pocketmine/network/mcpe/protocol/UpdateAttributesPacket.php @@ -19,15 +19,16 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include use pocketmine\entity\Attribute; +use pocketmine\network\mcpe\NetworkSession; class UpdateAttributesPacket extends DataPacket{ - const NETWORK_ID = Info::UPDATE_ATTRIBUTES_PACKET; + const NETWORK_ID = ProtocolInfo::UPDATE_ATTRIBUTES_PACKET; public $entityId; @@ -40,7 +41,7 @@ class UpdateAttributesPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putEntityId($this->entityId); + $this->putEntityRuntimeId($this->entityId); $this->putUnsignedVarInt(count($this->entries)); foreach($this->entries as $entry){ $this->putLFloat($entry->getMinValue()); @@ -51,4 +52,8 @@ class UpdateAttributesPacket extends DataPacket{ } } + public function handle(NetworkSession $session) : bool{ + return $session->handleUpdateAttributes($this); + } + } diff --git a/src/pocketmine/network/protocol/UpdateBlockPacket.php b/src/pocketmine/network/mcpe/protocol/UpdateBlockPacket.php similarity index 81% rename from src/pocketmine/network/protocol/UpdateBlockPacket.php rename to src/pocketmine/network/mcpe/protocol/UpdateBlockPacket.php index b1595b279..b7ac09198 100644 --- a/src/pocketmine/network/protocol/UpdateBlockPacket.php +++ b/src/pocketmine/network/mcpe/protocol/UpdateBlockPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class UpdateBlockPacket extends DataPacket{ - const NETWORK_ID = Info::UPDATE_BLOCK_PACKET; + const NETWORK_ID = ProtocolInfo::UPDATE_BLOCK_PACKET; const FLAG_NONE = 0b0000; const FLAG_NEIGHBORS = 0b0001; @@ -49,9 +51,13 @@ class UpdateBlockPacket extends DataPacket{ public function encode(){ $this->reset(); - $this->putBlockCoords($this->x, $this->y, $this->z); + $this->putBlockPosition($this->x, $this->y, $this->z); $this->putUnsignedVarInt($this->blockId); $this->putUnsignedVarInt(($this->flags << 4) | $this->blockData); } + public function handle(NetworkSession $session) : bool{ + return $session->handleUpdateBlock($this); + } + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/UpdateTradePacket.php b/src/pocketmine/network/mcpe/protocol/UpdateTradePacket.php similarity index 65% rename from src/pocketmine/network/protocol/UpdateTradePacket.php rename to src/pocketmine/network/mcpe/protocol/UpdateTradePacket.php index e45528bd0..6a8f76e2e 100644 --- a/src/pocketmine/network/protocol/UpdateTradePacket.php +++ b/src/pocketmine/network/mcpe/protocol/UpdateTradePacket.php @@ -20,17 +20,20 @@ */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\types\WindowTypes; + class UpdateTradePacket extends DataPacket{ - const NETWORK_ID = Info::UPDATE_TRADE_PACKET; + const NETWORK_ID = ProtocolInfo::UPDATE_TRADE_PACKET; //TODO: find fields - public $byte1; - public $byte2; + public $windowId; + public $windowType = WindowTypes::TRADING; //Mojang hardcoded this -_- public $varint1; public $varint2; public $isWilling; @@ -40,27 +43,31 @@ class UpdateTradePacket extends DataPacket{ public $offers; public function decode(){ - $this->byte1 = $this->getByte(); - $this->byte2 = $this->getByte(); + $this->windowId = $this->getByte(); + $this->windowType = $this->getByte(); $this->varint1 = $this->getVarInt(); $this->varint2 = $this->getVarInt(); $this->isWilling = $this->getBool(); - $this->traderEid = $this->getEntityId(); - $this->playerEid = $this->getEntityId(); + $this->traderEid = $this->getEntityUniqueId(); + $this->playerEid = $this->getEntityUniqueId(); $this->displayName = $this->getString(); $this->offers = $this->get(true); } public function encode(){ $this->reset(); - $this->putByte($this->byte1); - $this->putByte($this->byte2); + $this->putByte($this->windowId); + $this->putByte($this->windowType); $this->putVarInt($this->varint1); $this->putVarInt($this->varint2); $this->putBool($this->isWilling); - $this->putEntityId($this->traderEid); //UniqueID - $this->putEntityId($this->playerEid); //UniqueID + $this->putEntityUniqueId($this->traderEid); + $this->putEntityUniqueId($this->playerEid); $this->putString($this->displayName); $this->put($this->offers); } + + public function handle(NetworkSession $session) : bool{ + return $session->handleUpdateTrade($this); + } } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/UseItemPacket.php b/src/pocketmine/network/mcpe/protocol/UseItemPacket.php similarity index 81% rename from src/pocketmine/network/protocol/UseItemPacket.php rename to src/pocketmine/network/mcpe/protocol/UseItemPacket.php index 184a490c1..505123d02 100644 --- a/src/pocketmine/network/protocol/UseItemPacket.php +++ b/src/pocketmine/network/mcpe/protocol/UseItemPacket.php @@ -19,13 +19,15 @@ * */ -namespace pocketmine\network\protocol; +namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\network\mcpe\NetworkSession; + class UseItemPacket extends DataPacket{ - const NETWORK_ID = Info::USE_ITEM_PACKET; + const NETWORK_ID = ProtocolInfo::USE_ITEM_PACKET; public $x; public $y; @@ -42,7 +44,7 @@ class UseItemPacket extends DataPacket{ public $slot; public function decode(){ - $this->getBlockCoords($this->x, $this->y, $this->z); + $this->getBlockPosition($this->x, $this->y, $this->z); $this->blockId = $this->getUnsignedVarInt(); $this->face = $this->getVarInt(); $this->getVector3f($this->fx, $this->fy, $this->fz); @@ -55,4 +57,8 @@ class UseItemPacket extends DataPacket{ } + public function handle(NetworkSession $session) : bool{ + return $session->handleUseItem($this); + } + } diff --git a/src/pocketmine/network/protocol/types/InventoryNetworkIds.php b/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php similarity index 85% rename from src/pocketmine/network/protocol/types/InventoryNetworkIds.php rename to src/pocketmine/network/mcpe/protocol/types/WindowTypes.php index 8903b1f20..667b69674 100644 --- a/src/pocketmine/network/protocol/types/InventoryNetworkIds.php +++ b/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php @@ -20,10 +20,10 @@ */ -namespace pocketmine\network\protocol\types; +namespace pocketmine\network\mcpe\protocol\types; -interface InventoryNetworkIds{ +interface WindowTypes{ const INVENTORY = -1; const CONTAINER = 0; @@ -39,4 +39,9 @@ interface InventoryNetworkIds{ const MINECART_CHEST = 10; const MINECART_HOPPER = 11; const HORSE = 12; + const BEACON = 13; + const STRUCTURE_EDITOR = 14; + const TRADING = 15; + const COMMAND_BLOCK = 16; + } \ No newline at end of file diff --git a/src/pocketmine/network/protocol/Info.php b/src/pocketmine/network/protocol/Info.php deleted file mode 100644 index 61d63501a..000000000 --- a/src/pocketmine/network/protocol/Info.php +++ /dev/null @@ -1,121 +0,0 @@ -getPluginManager(); foreach(array_keys($this->permissions) as $name){ - Server::getInstance()->getPluginManager()->unsubscribeFromPermission($name, $this->parent !== null ? $this->parent : $this); + $pluginManager->unsubscribeFromPermission($name, $this->parent ?? $this); } - Server::getInstance()->getPluginManager()->unsubscribeFromDefaultPerms(false, $this->parent !== null ? $this->parent : $this); - Server::getInstance()->getPluginManager()->unsubscribeFromDefaultPerms(true, $this->parent !== null ? $this->parent : $this); + $pluginManager->unsubscribeFromDefaultPerms(false, $this->parent ?? $this); + $pluginManager->unsubscribeFromDefaultPerms(true, $this->parent ?? $this); $this->permissions = []; } diff --git a/src/pocketmine/resourcepacks/ResourcePack.php b/src/pocketmine/resourcepacks/ResourcePack.php new file mode 100644 index 000000000..d4eeeb0e6 --- /dev/null +++ b/src/pocketmine/resourcepacks/ResourcePack.php @@ -0,0 +1,70 @@ +packId = $packId; $this->version = $version; $this->packSize = $packSize; diff --git a/src/pocketmine/resourcepacks/ResourcePackManager.php b/src/pocketmine/resourcepacks/ResourcePackManager.php new file mode 100644 index 000000000..125c31ce0 --- /dev/null +++ b/src/pocketmine/resourcepacks/ResourcePackManager.php @@ -0,0 +1,142 @@ +server = $server; + $this->path = $path; + + if(!file_exists($this->path)){ + $this->server->getLogger()->debug("Resource packs path $path does not exist, creating directory"); + mkdir($this->path); + }elseif(!is_dir($this->path)){ + throw new \InvalidArgumentException("Resource packs path $path exists and is not a directory"); + } + + if(!file_exists($this->path . "resource_packs.yml")){ + file_put_contents($this->path . "resource_packs.yml", file_get_contents($this->server->getFilePath() . "src/pocketmine/resources/resource_packs.yml")); + } + + $this->resourcePacksConfig = new Config($this->path . "resource_packs.yml", Config::YAML, []); + + $this->serverForceResources = (bool) $this->resourcePacksConfig->get("force_resources", false); + + $this->server->getLogger()->info("Loading resource packs..."); + + foreach($this->resourcePacksConfig->get("resource_stack", []) as $pos => $pack){ + try{ + $packPath = $this->path . DIRECTORY_SEPARATOR . $pack; + if(file_exists($packPath)){ + $newPack = null; + //Detect the type of resource pack. + if(is_dir($packPath)){ + $this->server->getLogger()->warning("Skipped resource entry $pack due to directory resource packs currently unsupported"); + }else{ + $info = new \SplFileInfo($packPath); + switch($info->getExtension()){ + case "zip": + $newPack = new ZippedResourcePack($packPath); + break; + default: + $this->server->getLogger()->warning("Skipped resource entry $pack due to format not recognized"); + break; + } + } + + if($newPack instanceof ResourcePack){ + $this->resourcePacks[] = $newPack; + $this->uuidList[$newPack->getPackId()] = $newPack; + } + }else{ + $this->server->getLogger()->warning("Skipped resource entry $pack due to file or directory not found"); + } + }catch(\Throwable $e){ + $this->server->getLogger()->logException($e); + } + } + + $this->server->getLogger()->debug("Successfully loaded " . count($this->resourcePacks) . " resource packs"); + } + + /** + * Returns whether players must accept resource packs in order to join. + * @return bool + */ + public function resourcePacksRequired() : bool{ + return $this->serverForceResources; + } + + /** + * Returns an array of resource packs in use, sorted in order of priority. + * @return ResourcePack[] + */ + public function getResourceStack() : array{ + return $this->resourcePacks; + } + + /** + * Returns the resource pack matching the specified UUID string, or null if the ID was not recognized. + * + * @param string $id + * @return ResourcePack|null + */ + public function getPackById(string $id){ + return $this->uuidList[$id] ?? null; + } + + /** + * Returns an array of pack IDs for packs currently in use. + * @return string[] + */ + public function getPackIdList() : array{ + return array_keys($this->uuidList); + } +} \ No newline at end of file diff --git a/src/pocketmine/resourcepacks/ZippedResourcePack.php b/src/pocketmine/resourcepacks/ZippedResourcePack.php new file mode 100644 index 000000000..80af52660 --- /dev/null +++ b/src/pocketmine/resourcepacks/ZippedResourcePack.php @@ -0,0 +1,126 @@ +format_version) or !isset($manifest->header) or !isset($manifest->modules)){ + return false; + } + + //Right now we don't care about anything else, only the stuff we're sending to clients. + return + isset($manifest->header->description) and + isset($manifest->header->name) and + isset($manifest->header->uuid) and + isset($manifest->header->version) and + count($manifest->header->version) === 3; + } + + /** @var string */ + protected $path; + + /** @var \stdClass */ + protected $manifest; + + /** @var string */ + protected $sha256 = null; + + /** @var resource */ + protected $fileResource; + + /** + * @param string $zipPath Path to the resource pack zip + */ + public function __construct(string $zipPath){ + $this->path = $zipPath; + + if(!file_exists($zipPath)){ + throw new \InvalidArgumentException("Could not open resource pack $zipPath: file not found"); + } + + $archive = new \ZipArchive(); + if(($openResult = $archive->open($zipPath)) !== true){ + throw new \InvalidStateException("Encountered ZipArchive error code $openResult while trying to open $zipPath"); + } + + if(($manifestData = $archive->getFromName("manifest.json")) === false){ + throw new \InvalidStateException("Could not load resource pack from $zipPath: manifest.json not found"); + } + + $archive->close(); + + $manifest = json_decode($manifestData); + if(!self::verifyManifest($manifest)){ + throw new \InvalidStateException("Could not load resource pack from $zipPath: manifest.json is invalid or incomplete"); + } + + $this->manifest = $manifest; + + $this->fileResource = fopen($zipPath, "rb"); + } + + public function __destruct(){ + fclose($this->fileResource); + } + + public function getPackName() : string{ + return $this->manifest->header->name; + } + + public function getPackVersion() : string{ + return implode(".", $this->manifest->header->version); + } + + public function getPackId() : string{ + return $this->manifest->header->uuid; + } + + public function getPackSize() : int{ + return filesize($this->path); + } + + public function getSha256(bool $cached = true) : string{ + if($this->sha256 === null or !$cached){ + $this->sha256 = hash_file("sha256", $this->path, true); + } + return $this->sha256; + } + + public function getPackChunk(int $start, int $length) : string{ + fseek($this->fileResource, $start); + if(feof($this->fileResource)){ + throw new \RuntimeException("Requested a resource pack chunk with invalid start offset"); + } + return fread($this->fileResource, $length); + } +} \ No newline at end of file diff --git a/src/pocketmine/resources/effects.json b/src/pocketmine/resources/effects.json new file mode 100644 index 000000000..0ae688c51 --- /dev/null +++ b/src/pocketmine/resources/effects.json @@ -0,0 +1,135 @@ +{ + "speed": { + "id": 1, + "color": "7cafc6", + "name": "potion.moveSpeed" + }, + "slowness": { + "id": 2, + "color": "5a6c81", + "name": "potion.moveSlowdown", + "isBad": true + }, + "haste": { + "id": 3, + "color": "d9c043", + "name": "potion.digSpeed" + }, + "mining_fatigue": { + "id": 4, + "color": "4a4217", + "name": "potion.digSlowDown", + "isBad": true + }, + "strength": { + "id": 5, + "color": "932423", + "name": "potion.damageBoost" + }, + "instant_health": { + "id": 6, + "color": "f82423", + "name": "potion.heal", + "default_duration": 1, + "has_bubbles": false + }, + "instant_damage": { + "id": 7, + "color": "430a09", + "name": "potion.harm", + "isBad": true, + "default_duration": 1, + "has_bubbles": false + }, + "jump_boost": { + "id": 8, + "color": "22ff4c", + "name": "potion.jump" + }, + "nausea": { + "id": 9, + "color": "551d4a", + "name": "potion.confusion", + "isBad": true + }, + "regeneration": { + "id": 10, + "color": "cd5cab", + "name": "potion.regeneration" + }, + "resistance": { + "id": 11, + "color": "99453a", + "name": "potion.resistance" + }, + "fire_resistance": { + "id": 12, + "color": "e49a3a", + "name": "potion.fireResistance" + }, + "water_breathing": { + "id": 13, + "color": "2e5299", + "name": "potion.waterBreathing" + }, + "invisibility": { + "id": 14, + "color": "7f8392", + "name": "potion.invisibility" + }, + "blindness": { + "id": 15, + "color": "1f1f23", + "name": "potion.blindness", + "isBad": true + }, + "night_vision": { + "id": 16, + "color": "1f1fa1", + "name": "potion.nightVision" + }, + "hunger": { + "id": 17, + "color": "587653", + "name": "potion.hunger", + "isBad": true + }, + "weakness": { + "id": 18, + "color": "484d48", + "name": "potion.weakness", + "isBad": true + }, + "poison": { + "id": 19, + "color": "4e9331", + "name": "potion.poison", + "isBad": true + }, + "wither": { + "id": 20, + "color": "352a27", + "name": "potion.wither", + "isBad": true + }, + "health_boost": { + "id": 21, + "color": "f87d23", + "name": "potion.healthBoost" + }, + "absorption": { + "id": 22, + "color": "2552a5", + "name": "potion.absorption" + }, + "saturation": { + "id": 23, + "color": "f82423", + "name": "potion.saturation" + }, + "levitation": { + "id": 24, + "color": "ceffff", + "name": "potion.levitation" + } +} \ No newline at end of file diff --git a/src/pocketmine/resources/resource_packs.yml b/src/pocketmine/resources/resource_packs.yml new file mode 100644 index 000000000..331760e97 --- /dev/null +++ b/src/pocketmine/resources/resource_packs.yml @@ -0,0 +1,12 @@ +#This configuration file controls global resources used on your PocketMine-MP server. + +#Choose whether players must use your chosen resource packs to join the server. +#NOTE: This will do nothing if there are no resource packs in the stack below. +force_resources: false +resource_stack: + #Resource packs here are applied from bottom to top. This means that resources in higher packs will override those in lower packs. + #Entries here must indicate the filename of the resource pack. + #Example + # - natural.zip + # - vanilla.zip + #If you want to force clients to use vanilla resources, you must place a vanilla resource pack in your resources folder and add it to the stack here. \ No newline at end of file diff --git a/src/pocketmine/scheduler/SendUsageTask.php b/src/pocketmine/scheduler/SendUsageTask.php index bd7d3003d..688ed7432 100644 --- a/src/pocketmine/scheduler/SendUsageTask.php +++ b/src/pocketmine/scheduler/SendUsageTask.php @@ -21,7 +21,7 @@ namespace pocketmine\scheduler; -use pocketmine\network\protocol\Info; +use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\Server; use pocketmine\utils\Utils; use pocketmine\utils\UUID; @@ -58,7 +58,7 @@ class SendUsageTask extends AsyncTask{ "build" => $version->getBuild(), "api" => $server->getApiVersion(), "minecraftVersion" => $server->getVersion(), - "protocol" => Info::CURRENT_PROTOCOL + "protocol" => ProtocolInfo::CURRENT_PROTOCOL ]; $data["system"] = [ diff --git a/src/pocketmine/tile/Furnace.php b/src/pocketmine/tile/Furnace.php index 637acee47..cfb8e9da4 100644 --- a/src/pocketmine/tile/Furnace.php +++ b/src/pocketmine/tile/Furnace.php @@ -35,7 +35,7 @@ use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ShortTag; use pocketmine\nbt\tag\StringTag; -use pocketmine\network\protocol\ContainerSetDataPacket; +use pocketmine\network\mcpe\protocol\ContainerSetDataPacket; class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{ /** @var FurnaceInventory */ diff --git a/src/pocketmine/tile/Spawnable.php b/src/pocketmine/tile/Spawnable.php index 555cd75db..0d85b7ea3 100644 --- a/src/pocketmine/tile/Spawnable.php +++ b/src/pocketmine/tile/Spawnable.php @@ -24,7 +24,7 @@ namespace pocketmine\tile; use pocketmine\level\Level; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\CompoundTag; -use pocketmine\network\protocol\BlockEntityDataPacket; +use pocketmine\network\mcpe\protocol\BlockEntityDataPacket; use pocketmine\Player; abstract class Spawnable extends Tile{ diff --git a/src/pocketmine/tile/Tile.php b/src/pocketmine/tile/Tile.php index daddd775a..a66c5f512 100644 --- a/src/pocketmine/tile/Tile.php +++ b/src/pocketmine/tile/Tile.php @@ -148,6 +148,17 @@ abstract class Tile extends Position{ $this->namedtag->z = new IntTag("z", $this->z); } + public function getCleanedNBT(){ + $this->saveNBT(); + $tag = clone $this->namedtag; + unset($tag->x, $tag->y, $tag->z, $tag->id); + if($tag->getCount() > 0){ + return $tag; + }else{ + return null; + } + } + /** * @return \pocketmine\block\Block */ diff --git a/src/pocketmine/updater/AutoUpdater.php b/src/pocketmine/updater/AutoUpdater.php index 5d206290e..23461e4fe 100644 --- a/src/pocketmine/updater/AutoUpdater.php +++ b/src/pocketmine/updater/AutoUpdater.php @@ -24,7 +24,6 @@ namespace pocketmine\updater; use pocketmine\Player; use pocketmine\Server; use pocketmine\utils\TextFormat; -use pocketmine\utils\Utils; use pocketmine\utils\VersionString; class AutoUpdater{ diff --git a/src/pocketmine/utils/Binary.php b/src/pocketmine/utils/Binary.php index 7899e5383..60099b7c4 100644 --- a/src/pocketmine/utils/Binary.php +++ b/src/pocketmine/utils/Binary.php @@ -336,8 +336,15 @@ class Binary{ return strrev(self::writeLong($value)); } - //TODO: proper varlong support + + /** + * Reads a 32-bit zigzag-encoded variable-length integer from the supplied stream. + * + * @param \pocketmine\nbt\NBT|BinaryStream $stream + * + * @return int + */ public static function readVarInt($stream){ $shift = PHP_INT_SIZE === 8 ? 63 : 31; $raw = self::readUnsignedVarInt($stream); @@ -345,25 +352,252 @@ class Binary{ return $temp ^ ($raw & (1 << $shift)); } + /** + * Reads a 32-bit variable-length unsigned integer from the supplied stream. + * + * @param \pocketmine\nbt\NBT|BinaryStream $stream + * + * @return int + */ public static function readUnsignedVarInt($stream){ $value = 0; - $i = 0; - do{ - if($i > 63){ - throw new \InvalidArgumentException("Varint did not terminate after 10 bytes!"); + for($i = 0; $i <= 35; $i += 7){ + $b = $stream->getByte(); + $value |= (($b & 0x7f) << $i); + + if(($b & 0x80) === 0){ + return $value; } - $value |= ((($b = $stream->getByte()) & 0x7f) << $i); - $i += 7; - }while($b & 0x80); + } - return $value; + throw new \InvalidArgumentException("VarInt did not terminate after 5 bytes!"); } + /** + * Writes a 32-bit integer as a zigzag-encoded variable-length integer. + * + * @param int $v + * + * @return string + */ public static function writeVarInt($v){ - return self::writeUnsignedVarInt(($v << 1) ^ ($v >> (PHP_INT_SIZE === 8 ? 63 : 31))); + if(PHP_INT_SIZE === 8){ + $v = ($v << 32 >> 32); + } + return self::writeUnsignedVarInt(($v << 1) ^ ($v >> 31)); } + /** + * Writes a 32-bit unsigned integer as a variable-length integer. + * @param int $value + * + * @return string up to 5 bytes + */ public static function writeUnsignedVarInt($value){ + $buf = ""; + $value &= 0xffffffff; + for($i = 0; $i < 5; ++$i){ + if(($value >> 7) !== 0){ + $buf .= chr($value | 0x80); //Let chr() take the last byte of this, it's faster than adding another & 0x7f. + }else{ + $buf .= chr($value & 0x7f); + return $buf; + } + + $value = (($value >> 7) & (PHP_INT_MAX >> 6)); //PHP really needs a logical right-shift operator + } + + throw new \InvalidArgumentException("Value too large to be encoded as a VarInt"); + } + + + + /** + * Reads a 64-bit zigzag-encoded variable-length integer from the supplied stream. + * @param \pocketmine\nbt\NBT|BinaryStream $stream + * + * @return int|string + */ + public static function readVarLong($stream){ + if(PHP_INT_SIZE === 8){ + return self::readVarLong_64($stream); + }else{ + return self::readVarLong_32($stream); + } + } + + /** + * Legacy BC Math zigzag VarLong reader. Will work on 32-bit or 64-bit, but will be slower than the regular 64-bit method. + * @param \pocketmine\nbt\NBT|BinaryStream $stream + * + * @return string + */ + public static function readVarLong_32($stream){ + /** @var string $raw */ + $raw = self::readUnsignedVarLong_32($stream); + $result = bcdiv($raw, "2"); + if(bcmod($raw, "2") === "1"){ + $result = bcsub(bcmul($result, "-1"), "1"); + } + + return $result; + } + + /** + * 64-bit zizgag VarLong reader. + * @param \pocketmine\nbt\NBT|BinaryStream $stream + * + * @return int + */ + public static function readVarLong_64($stream){ + $raw = self::readUnsignedVarLong_64($stream); + $temp = ((($raw << 63) >> 63) ^ $raw) >> 1; + return $temp ^ ($raw & (1 << 63)); + } + + /** + * Reads an unsigned VarLong from the supplied stream. + * @param \pocketmine\nbt\NBT|BinaryStream $stream + * + * @return int|string + */ + public static function readUnsignedVarLong($stream){ + if(PHP_INT_SIZE === 8){ + return self::readUnsignedVarLong_64($stream); + }else{ + return self::readUnsignedVarLong_32($stream); + } + } + + /** + * Legacy BC Math unsigned VarLong reader. + * @param \pocketmine\nbt\NBT|BinaryStream $stream + * + * @return string + */ + public static function readUnsignedVarLong_32($stream){ + $value = "0"; + for($i = 0; $i <= 63; $i += 7){ + $b = $stream->getByte(); + $value = bcadd($value, bcmul($b & 0x7f, bcpow("2", "$i"))); + + if(($b & 0x80) === 0){ + return $value; + } + } + + throw new \InvalidArgumentException("VarLong did not terminate after 10 bytes!"); + } + + /** + * 64-bit unsigned VarLong reader. + * @param \pocketmine\nbt\NBT|BinaryStream $stream + * + * @return int + */ + public static function readUnsignedVarLong_64($stream){ + $value = 0; + for($i = 0; $i <= 63; $i += 7){ + $b = $stream->getByte(); + $value |= (($b & 0x7f) << $i); + + if(($b & 0x80) === 0){ + return $value; + } + } + + throw new \InvalidArgumentException("VarLong did not terminate after 10 bytes!"); + } + + + + /** + * Writes a 64-bit integer as a variable-length long. + * @param int|string $v + * + * @return string up to 10 bytes + */ + public static function writeVarLong($v){ + if(PHP_INT_SIZE === 8){ + return self::writeVarLong_64($v); + }else{ + return self::writeVarLong_32($v); + } + } + + /** + * Legacy BC Math zigzag VarLong encoder. + * @param string $v + * + * @return string + */ + public static function writeVarLong_32($v){ + $v = bcmod(bcmul($v, "2"), "18446744073709551616"); + if(bccomp($v, "0") == -1){ + $v = bcsub(bcmul($v, "-1"), "1"); + } + + return self::writeUnsignedVarLong_32($v); + } + + /** + * 64-bit VarLong encoder. + * @param int $v + * + * @return string + */ + public static function writeVarLong_64($v){ + return self::writeUnsignedVarLong_64(($v << 1) ^ ($v >> 63)); + } + + /** + * Writes a 64-bit integer as a variable-length long + * @param int|string $v + * + * @return string up to 10 bytes + */ + public static function writeUnsignedVarLong($v){ + if(PHP_INT_SIZE === 8){ + return self::writeUnsignedVarLong_64($v); + }else{ + return self::writeUnsignedVarLong_32($v); + } + } + + /** + * Legacy BC Math unsigned VarLong encoder. + * @param string $value + * + * @return string + */ + public static function writeUnsignedVarLong_32($value){ + $buf = ""; + + if(bccomp($value, "0") == -1){ + $value = bcadd($value, "18446744073709551616"); + } + + for($i = 0; $i < 10; ++$i){ + $byte = (int) bcmod($value, "128"); + $value = bcdiv($value, "128"); + if($value !== "0"){ + $buf .= chr($byte | 0x80); + }else{ + $buf .= chr($byte); + return $buf; + } + } + + throw new \InvalidArgumentException("Value too large to be encoded as a VarLong"); + } + + /** + * 64-bit unsigned VarLong encoder. + * @param int $value + * + * @return string + */ + public static function writeUnsignedVarLong_64($value){ $buf = ""; for($i = 0; $i < 10; ++$i){ if(($value >> 7) !== 0){ @@ -376,6 +610,6 @@ class Binary{ $value = (($value >> 7) & (PHP_INT_MAX >> 6)); //PHP really needs a logical right-shift operator } - throw new \InvalidArgumentException("Value too large to be encoded as a varint"); + throw new \InvalidArgumentException("Value too large to be encoded as a VarLong"); } } diff --git a/src/pocketmine/utils/BinaryStream.php b/src/pocketmine/utils/BinaryStream.php index 506a162f0..156ce4523 100644 --- a/src/pocketmine/utils/BinaryStream.php +++ b/src/pocketmine/utils/BinaryStream.php @@ -233,66 +233,68 @@ class BinaryStream extends \stdClass{ $this->put($v); } - //TODO: varint64 - /** - * Reads an unsigned varint32 from the stream. + * Reads a 32-bit variable-length unsigned integer from the buffer and returns it. + * @return int */ public function getUnsignedVarInt(){ return Binary::readUnsignedVarInt($this); } /** - * Writes an unsigned varint32 to the stream. + * Writes a 32-bit variable-length unsigned integer to the end of the buffer. + * @param int $v */ public function putUnsignedVarInt($v){ $this->put(Binary::writeUnsignedVarInt($v)); } /** - * Reads a signed varint32 from the stream. + * Reads a 32-bit zigzag-encoded variable-length integer from the buffer and returns it. + * @return int */ public function getVarInt(){ return Binary::readVarInt($this); } /** - * Writes a signed varint32 to the stream. + * Writes a 32-bit zigzag-encoded variable-length integer to the end of the buffer. + * @param int $v */ public function putVarInt($v){ $this->put(Binary::writeVarInt($v)); } - public function getEntityId(){ - return $this->getVarInt(); + /** + * Reads a 64-bit variable-length integer from the buffer and returns it. + * @return int|string int, or the string representation of an int64 on 32-bit platforms + */ + public function getUnsignedVarLong(){ + return Binary::readUnsignedVarLong($this); } - public function putEntityId($v){ - $this->putVarInt($v); + /** + * Writes a 64-bit variable-length integer to the end of the buffer. + * @param int|string $v int, or the string representation of an int64 on 32-bit platforms + */ + public function putUnsignedVarLong($v){ + $this->buffer .= Binary::writeUnsignedVarLong($v); } - public function getBlockCoords(&$x, &$y, &$z){ - $x = $this->getVarInt(); - $y = $this->getUnsignedVarInt(); - $z = $this->getVarInt(); + /** + * Reads a 64-bit zigzag-encoded variable-length integer from the buffer and returns it. + * @return int|string int, or the string representation of an int64 on 32-bit platforms + */ + public function getVarLong(){ + return Binary::readVarLong($this); } - public function putBlockCoords($x, $y, $z){ - $this->putVarInt($x); - $this->putUnsignedVarInt($y); - $this->putVarInt($z); - } - - public function getVector3f(&$x, &$y, &$z){ - $x = $this->getLFloat(4); - $y = $this->getLFloat(4); - $z = $this->getLFloat(4); - } - - public function putVector3f($x, $y, $z){ - $this->putLFloat($x); - $this->putLFloat($y); - $this->putLFloat($z); + /** + * Writes a 64-bit zigzag-encoded variable-length integer to the end of the buffer. + * @param int|string $v int, or the string representation of an int64 on 32-bit platforms + */ + public function putVarLong($v){ + $this->buffer .= Binary::writeVarLong($v); } public function feof(){ diff --git a/src/pocketmine/utils/Color.php b/src/pocketmine/utils/Color.php new file mode 100644 index 000000000..6913e7a92 --- /dev/null +++ b/src/pocketmine/utils/Color.php @@ -0,0 +1,158 @@ +r = $r & 0xff; + $this->g = $g & 0xff; + $this->b = $b & 0xff; + $this->a = $a & 0xff; + } + + /** + * Returns the alpha (transparency) value of this colour. + * @return int + */ + public function getA() : int{ + return $this->a; + } + + /** + * Sets the alpha (opacity) value of this colour, lower = more transparent + * @param int $a + */ + public function setA(int $a){ + $this->a = $a & 0xff; + } + + /** + * Retuns the red value of this colour. + * @return int + */ + public function getR() : int{ + return $this->r; + } + + /** + * Sets the red value of this colour. + * @param int $r + */ + public function setR(int $r){ + $this->r = $r & 0xff; + } + + /** + * Returns the green value of this colour. + * @return int + */ + public function getG() : int{ + return $this->g; + } + + /** + * Sets the green value of this colour. + * @param int $g + */ + public function setG(int $g){ + $this->g = $g & 0xff; + } + + /** + * Returns the blue value of this colour. + * @return int + */ + public function getB() : int{ + return $this->b; + } + + /** + * Sets the blue value of this colour. + * @param int $b + */ + public function setB(int $b){ + $this->b = $b & 0xff; + } + + /** + * Returns a Color from the supplied RGB colour code (24-bit) + * @param int $code + * + * @return Color + */ + public static function fromRGB(int $code){ + return new Color(($code >> 16) & 0xff, ($code >> 8) & 0xff, $code & 0xff); + } + + /** + * Returns a Color from the supplied ARGB colour code (32-bit) + * + * @param int $code + * + * @return Color + */ + public static function fromARGB(int $code){ + return new Color(($code >> 16) & 0xff, ($code >> 8) & 0xff, $code & 0xff, ($code >> 24) & 0xff); + } + + /** + * Returns an ARGB 32-bit colour value. + * @return int + */ + public function toARGB() : int{ + return ($this->a << 24) | ($this->r << 16) | ($this->g << 8) | $this->b; + } + + /** + * Returns a little-endian ARGB 32-bit colour value. + * @return int + */ + public function toBGRA() : int{ + return ($this->b << 24) | ($this->g << 16) | ($this->r << 8) | $this->a; + } + + /** + * Returns an RGBA 32-bit colour value. + * @return int + */ + public function toRGBA() : int{ + return ($this->r << 24) | ($this->g << 16) | ($this->b << 8) | $this->a; + } + + /** + * Returns a little-endian RGBA colour value. + * @return int + */ + public function toABGR() : int{ + return ($this->a << 24) | ($this->b << 16) | ($this->g << 8) | $this->r; + } + + public static function fromABGR(int $code){ + return new Color($code & 0xff, ($code >> 8) & 0xff, ($code >> 16) & 0xff, ($code >> 24) & 0xff); + } +} \ No newline at end of file diff --git a/tests/plugins/PocketMine-DevTools b/tests/plugins/PocketMine-DevTools index db894896e..dc4e7c35f 160000 --- a/tests/plugins/PocketMine-DevTools +++ b/tests/plugins/PocketMine-DevTools @@ -1 +1 @@ -Subproject commit db894896ee4932a86f8655ca9f8cabef726787b3 +Subproject commit dc4e7c35f287419e3af2352db3f9ed80bddb7bcd