diff --git a/src/pocketmine/CrashDump.php b/src/pocketmine/CrashDump.php index 5bfc6b569..45504df64 100644 --- a/src/pocketmine/CrashDump.php +++ b/src/pocketmine/CrashDump.php @@ -24,6 +24,7 @@ namespace pocketmine; use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\plugin\PluginBase; use pocketmine\plugin\PluginLoadOrder; +use pocketmine\plugin\PluginManager; use pocketmine\utils\Utils; use pocketmine\utils\VersionString; use raklib\RakLib; @@ -86,7 +87,7 @@ class CrashDump{ } private function pluginsData(){ - if(class_exists("pocketmine\\plugin\\PluginManager", false)){ + if($this->server->getPluginManager() instanceof PluginManager){ $this->addLine(); $this->addLine("Loaded plugins:"); $this->data["plugins"] = []; @@ -224,6 +225,7 @@ class CrashDump{ private function generalData(){ $version = new VersionString(); $this->data["general"] = []; + $this->data["general"]["name"] = $this->server->getName(); $this->data["general"]["version"] = $version->get(false); $this->data["general"]["build"] = $version->getBuild(); $this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL; @@ -235,7 +237,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 " . ProtocolInfo::CURRENT_PROTOCOL . "; API " . API_VERSION . "]"); + $this->addLine($this->server->getName() . " 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/MemoryManager.php b/src/pocketmine/MemoryManager.php index b7e149b2a..8b291dd9c 100644 --- a/src/pocketmine/MemoryManager.php +++ b/src/pocketmine/MemoryManager.php @@ -214,6 +214,8 @@ class MemoryManager{ } public function dumpServerMemory($outputFolder, $maxNesting, $maxStringSize){ + $hardLimit = ini_get('memory_limit'); + ini_set('memory_limit', -1); gc_disable(); if(!file_exists($outputFolder)){ @@ -232,6 +234,32 @@ class MemoryManager{ $refCounts = []; + $instanceCounts = []; + + $staticCount = 0; + foreach($this->server->getLoader()->getClasses() as $className){ + $reflection = new \ReflectionClass($className); + $staticProperties[$className] = []; + foreach($reflection->getProperties() as $property){ + if(!$property->isStatic() or $property->getDeclaringClass()->getName() !== $className){ + continue; + } + + if(!$property->isPublic()){ + $property->setAccessible(true); + } + + $staticCount++; + $this->continueDump($property->getValue(), $staticProperties[$className][$property->getName()], $objects, $refCounts, 0, $maxNesting, $maxStringSize); + } + + if(count($staticProperties[$className]) === 0){ + unset($staticProperties[$className]); + } + } + + echo "[Dump] Wrote $staticCount static properties\n"; + $this->continueDump($this->server, $data, $objects, $refCounts, 0, $maxNesting, $maxStringSize); do{ @@ -243,6 +271,11 @@ class MemoryManager{ $continue = true; $className = get_class($object); + if(!isset($instanceCounts[$className])){ + $instanceCounts[$className] = 1; + }else{ + $instanceCounts[$className]++; + } $objects[$hash] = true; @@ -273,33 +306,23 @@ class MemoryManager{ } fwrite($obData, "$hash@$className: " . json_encode($info, JSON_UNESCAPED_SLASHES) . "\n"); - - if(!isset($objects["staticProperties"][$className])){ - $staticProperties[$className] = []; - foreach($reflection->getProperties() as $property){ - if(!$property->isStatic() or $property->getDeclaringClass()->getName() !== $className){ - continue; - } - - if(!$property->isPublic()){ - $property->setAccessible(true); - } - $this->continueDump($property->getValue($object), $staticProperties[$className][$property->getName()], $objects, $refCounts, 0, $maxNesting, $maxStringSize); - } - } } echo "[Dump] Wrote " . count($objects) . " objects\n"; }while($continue); - + fclose($obData); file_put_contents($outputFolder . "/staticProperties.js", json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); file_put_contents($outputFolder . "/serverEntry.js", json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); file_put_contents($outputFolder . "/referenceCounts.js", json_encode($refCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); + arsort($instanceCounts, SORT_NUMERIC); + file_put_contents($outputFolder . "/instanceCounts.js", json_encode($instanceCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); + echo "[Dump] Finished!\n"; + ini_set('memory_limit', $hardLimit); gc_enable(); } diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index bf2940113..8666e0e0e 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -184,7 +184,6 @@ 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; @@ -3464,11 +3463,20 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade 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->addSubTitle($subtitle); } $this->sendTitleText($title, SetTitlePacket::TYPE_SET_TITLE); } + /** + * Sets the subtitle message, without sending a title. + * + * @param string $subtitle + */ + public function addSubTitle(string $subtitle){ + $this->sendTitleText($subtitle, SetTitlePacket::TYPE_SET_SUBTITLE); + } + /** * Adds small text to the user's screen. * @@ -3487,6 +3495,15 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->dataPacket($pk); } + /** + * Resets the title duration settings. + */ + public function resetTitles(){ + $pk = new SetTitlePacket(); + $pk->type = SetTitlePacket::TYPE_RESET_TITLE; + $this->dataPacket($pk); + } + /** * Sets the title duration. * diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index ee7fac6cf..a8b150619 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1940,8 +1940,10 @@ class Server{ UPnP::RemovePortForward($this->getPort()); } - $this->getLogger()->debug("Disabling all plugins"); - $this->pluginManager->disablePlugins(); + if($this->pluginManager instanceof PluginManager){ + $this->getLogger()->debug("Disabling all plugins"); + $this->pluginManager->disablePlugins(); + } foreach($this->players as $player){ $player->close($player->getLeaveMessage(), $this->getProperty("settings.shutdown-message", "Server closed")); @@ -2084,7 +2086,8 @@ class Server{ try{ $dump = new CrashDump($this); }catch(\Throwable $e){ - $this->logger->critical($this->getLanguage()->translateString("pocketmine.crash.error", $e->getMessage())); + $this->logger->logException($e); + $this->logger->critical($this->getLanguage()->translateString("pocketmine.crash.error", [$e->getMessage()])); return; } @@ -2199,9 +2202,6 @@ class Server{ $pk = new PlayerListPacket(); $pk->type = PlayerListPacket::TYPE_ADD; foreach($this->playerList as $player){ - if($p === $player){ - continue; //fixes duplicates - } $pk->entries[] = [$player->getUniqueId(), $player->getId(), $player->getDisplayName(), $player->getSkinId(), $player->getSkinData()]; } diff --git a/src/pocketmine/command/Command.php b/src/pocketmine/command/Command.php index 189a3b03b..0795665ac 100644 --- a/src/pocketmine/command/Command.php +++ b/src/pocketmine/command/Command.php @@ -46,11 +46,6 @@ abstract class Command{ /** @var string */ private $label; - /** - * @var string[] - */ - private $aliases = []; - /** * @var string[] */ @@ -79,11 +74,11 @@ abstract class Command{ */ public function __construct($name, $description = "", $usageMessage = null, array $aliases = []){ $this->commandData = self::generateDefaultData(); - $this->name = $this->nextLabel = $this->label = $name; + $this->name = $name; + $this->setLabel($name); $this->setDescription($description); $this->usageMessage = $usageMessage === null ? "/" . $name : $usageMessage; $this->setAliases($aliases); - $this->timings = new TimingsHandler("** Command: " . $name); } /** @@ -208,6 +203,9 @@ abstract class Command{ public function setLabel($name){ $this->nextLabel = $name; if(!$this->isRegistered()){ + if($this->timings instanceof TimingsHandler){ + $this->timings->remove(); + } $this->timings = new TimingsHandler("** Command: " . $name); $this->label = $name; diff --git a/src/pocketmine/command/SimpleCommandMap.php b/src/pocketmine/command/SimpleCommandMap.php index ae959c8ad..9d934bede 100644 --- a/src/pocketmine/command/SimpleCommandMap.php +++ b/src/pocketmine/command/SimpleCommandMap.php @@ -57,6 +57,7 @@ use pocketmine\command\defaults\TeleportCommand; use pocketmine\command\defaults\TellCommand; use pocketmine\command\defaults\TimeCommand; use pocketmine\command\defaults\TimingsCommand; +use pocketmine\command\defaults\TitleCommand; use pocketmine\command\defaults\TransferServerCommand; use pocketmine\command\defaults\VanillaCommand; use pocketmine\command\defaults\VersionCommand; @@ -115,6 +116,7 @@ class SimpleCommandMap implements CommandMap{ $this->register("pocketmine", new TeleportCommand("tp")); $this->register("pocketmine", new TimeCommand("time")); $this->register("pocketmine", new TimingsCommand("timings")); + $this->register("pocketmine", new TitleCommand("title")); $this->register("pocketmine", new ReloadCommand("reload")); $this->register("pocketmine", new TransferServerCommand("transferserver")); @@ -302,4 +304,4 @@ class SimpleCommandMap implements CommandMap{ } -} \ No newline at end of file +} diff --git a/src/pocketmine/command/defaults/EffectCommand.php b/src/pocketmine/command/defaults/EffectCommand.php index 76e761b38..559d5576a 100644 --- a/src/pocketmine/command/defaults/EffectCommand.php +++ b/src/pocketmine/command/defaults/EffectCommand.php @@ -75,7 +75,6 @@ class EffectCommand extends VanillaCommand{ return true; } - $duration = 300; $amplification = 0; if(count($args) >= 3){ diff --git a/src/pocketmine/command/defaults/TitleCommand.php b/src/pocketmine/command/defaults/TitleCommand.php new file mode 100644 index 000000000..68423c842 --- /dev/null +++ b/src/pocketmine/command/defaults/TitleCommand.php @@ -0,0 +1,102 @@ +setPermission("pocketmine.command.title"); + } + + public function execute(CommandSender $sender, $currentAlias, array $args){ + if(!$this->testPermission($sender)){ + return true; + } + + if(count($args) < 2){ + $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); + return true; + } + + $player = $sender->getServer()->getPlayer($args[0]); + if($player === null){ + $sender->sendMessage(new TranslationContainer("commands.generic.player.notFound")); + return true; + } + + switch($args[1]){ + case "clear": + $player->removeTitles(); + break; + case "reset": + $player->resetTitles(); + break; + case "title": + if(count($args) < 3){ + $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); + return false; + } + + $player->addTitle(implode(" ", array_slice($args, 2))); + break; + case "subtitle": + if(count($args) < 3){ + $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); + return false; + } + + $player->addSubTitle(implode(" ", array_slice($args, 2))); + break; + case "actionbar": + if(count($args) < 3){ + $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); + return false; + } + + $player->addActionBarMessage(implode(" ", array_slice($args, 2))); + break; + case "times": + if(count($args) < 4){ + $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); + return false; + } + + $player->setTitleDuration($this->getInteger($sender, $args[2]), $this->getInteger($sender, $args[3]), $this->getInteger($sender, $args[4])); + break; + default: + $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); + return false; + } + + $sender->sendMessage(new TranslationContainer("commands.title.success")); + + return true; + } +} diff --git a/src/pocketmine/level/format/io/region/McRegion.php b/src/pocketmine/level/format/io/region/McRegion.php index b8fe71378..5d9e29758 100644 --- a/src/pocketmine/level/format/io/region/McRegion.php +++ b/src/pocketmine/level/format/io/region/McRegion.php @@ -30,7 +30,6 @@ use pocketmine\level\format\io\ChunkUtils; use pocketmine\level\format\SubChunk; use pocketmine\level\generator\Generator; use pocketmine\level\Level; -use pocketmine\level\LevelException; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\{ ByteArrayTag, ByteTag, CompoundTag, IntArrayTag, IntTag, ListTag, LongTag, StringTag diff --git a/src/pocketmine/level/format/io/region/RegionLoader.php b/src/pocketmine/level/format/io/region/RegionLoader.php index c76278364..c98a2a34b 100644 --- a/src/pocketmine/level/format/io/region/RegionLoader.php +++ b/src/pocketmine/level/format/io/region/RegionLoader.php @@ -25,7 +25,6 @@ namespace pocketmine\level\format\io\region; use pocketmine\level\format\Chunk; use pocketmine\level\format\io\ChunkException; -use pocketmine\level\LevelException; use pocketmine\utils\Binary; use pocketmine\utils\MainLogger; diff --git a/src/pocketmine/network/mcpe/NetworkSession.php b/src/pocketmine/network/mcpe/NetworkSession.php index b9d56098e..7157846f4 100644 --- a/src/pocketmine/network/mcpe/NetworkSession.php +++ b/src/pocketmine/network/mcpe/NetworkSession.php @@ -103,7 +103,6 @@ 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; diff --git a/src/pocketmine/network/mcpe/protocol/DropItemPacket.php b/src/pocketmine/network/mcpe/protocol/DropItemPacket.php index 98fff9df9..be25e0c7b 100644 --- a/src/pocketmine/network/mcpe/protocol/DropItemPacket.php +++ b/src/pocketmine/network/mcpe/protocol/DropItemPacket.php @@ -24,12 +24,14 @@ namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\item\Item; use pocketmine\network\mcpe\NetworkSession; class DropItemPacket extends DataPacket{ const NETWORK_ID = ProtocolInfo::DROP_ITEM_PACKET; public $type; + /** @var Item */ public $item; public function decode(){ diff --git a/src/pocketmine/permission/DefaultPermissions.php b/src/pocketmine/permission/DefaultPermissions.php index 079db04ff..10852f80a 100644 --- a/src/pocketmine/permission/DefaultPermissions.php +++ b/src/pocketmine/permission/DefaultPermissions.php @@ -124,6 +124,7 @@ abstract class DefaultPermissions{ self::registerPermission(new Permission(self::ROOT . ".command.spawnpoint", "Allows the user to change player's spawnpoint", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.setworldspawn", "Allows the user to change the world spawn", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.transferserver", "Allows the user to transfer self to another server", Permission::DEFAULT_OP), $commands); + self::registerPermission(new Permission(self::ROOT . ".command.title", "Allows the user to send a title to the specified player", Permission::DEFAULT_OP), $commands); $commands->recalculatePermissibles(); diff --git a/src/pocketmine/utils/BinaryStream.php b/src/pocketmine/utils/BinaryStream.php index a1234ceb2..ebba43124 100644 --- a/src/pocketmine/utils/BinaryStream.php +++ b/src/pocketmine/utils/BinaryStream.php @@ -177,11 +177,19 @@ class BinaryStream extends \stdClass{ } public function getUUID(){ - return UUID::fromBinary($this->get(16)); + //This is actually two little-endian longs: UUID Most followed by UUID Least + $part1 = $this->getLInt(); + $part0 = $this->getLInt(); + $part3 = $this->getLInt(); + $part2 = $this->getLInt(); + return new UUID($part0, $part1, $part2, $part3); } public function putUUID(UUID $uuid){ - $this->put($uuid->toBinary()); + $this->putLInt($uuid->getPart(1)); + $this->putLInt($uuid->getPart(0)); + $this->putLInt($uuid->getPart(3)); + $this->putLInt($uuid->getPart(2)); } public function getSlot(){ diff --git a/src/pocketmine/utils/UUID.php b/src/pocketmine/utils/UUID.php index 552ea3376..3b6118257 100644 --- a/src/pocketmine/utils/UUID.php +++ b/src/pocketmine/utils/UUID.php @@ -102,4 +102,15 @@ class UUID{ public function __toString(){ return $this->toString(); } + + public function getPart(int $partNumber){ + if($partNumber < 0 or $partNumber > 3){ + throw new \InvalidArgumentException("Invalid UUID part index $partNumber"); + } + return $this->parts[$partNumber]; + } + + public function getParts() : array{ + return $this->parts; + } } \ No newline at end of file