88    private const FORMAT_VERSION = 4;
 
   90    public const PLUGIN_INVOLVEMENT_NONE = 
"none";
 
   91    public const PLUGIN_INVOLVEMENT_DIRECT = 
"direct";
 
   92    public const PLUGIN_INVOLVEMENT_INDIRECT = 
"indirect";
 
   94    public const FATAL_ERROR_MASK =
 
   95        E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR;
 
   98    private string $encodedData;
 
  100    public function __construct(
 
  104        $now = microtime(
true);
 
  107        $this->data->format_version = self::FORMAT_VERSION;
 
  108        $this->data->time = $now;
 
  109        $this->data->uptime = $now - $this->
server->getStartTime();
 
  112        $this->generalData();
 
  113        $this->pluginsData();
 
  117        $json = json_encode($this->data, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
 
  118        $this->encodedData = Utils::assumeNotFalse(zlib_encode($json, ZLIB_ENCODING_DEFLATE, 9), 
"ZLIB compression failed");
 
  121    public function getEncodedData() : 
string{
 
  122        return $this->encodedData;
 
  130        $renderer->addLine();
 
  131        $renderer->addLine(
"----------------------REPORT THE DATA BELOW THIS LINE-----------------------");
 
  132        $renderer->addLine();
 
  133        $renderer->addLine(
"===BEGIN CRASH DUMP===");
 
  134        foreach(str_split(base64_encode($this->encodedData), 76) as $line){
 
  135            $renderer->addLine($line);
 
  137        $renderer->addLine(
"===END CRASH DUMP===");
 
  140    private function pluginsData() : 
void{
 
  141        if($this->pluginManager !== 
null){
 
  142            $plugins = $this->pluginManager->getPlugins();
 
  143            ksort($plugins, SORT_STRING);
 
  144            foreach($plugins as $p){
 
  145                $d = $p->getDescription();
 
  148                    version: $d->getVersion(),
 
  149                    authors: $d->getAuthors(),
 
  150                    api: $d->getCompatibleApis(),
 
  151                    enabled: $p->isEnabled(),
 
  152                    depends: $d->getDepend(),
 
  153                    softDepends: $d->getSoftDepend(),
 
  155                    load: mb_strtoupper($d->getOrder()->name),
 
  156                    website: $d->getWebsite()
 
  162    private function extraData() : 
void{
 
  165        if($this->
server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_REPORT_SEND_SETTINGS, 
true)){
 
  166            $this->data->parameters = (array) $argv;
 
  167            if(($serverDotProperties = @file_get_contents(Path::join($this->
server->getDataPath(), 
"server.properties"))) !== 
false){
 
  168                $this->data->serverDotProperties = preg_replace(
"#^rcon\\.password=(.*)$#m", 
"rcon.password=******", $serverDotProperties) ?? 
throw new AssumptionFailedError(
"Pattern is valid");
 
  170            if(($pocketmineDotYml = @file_get_contents(Path::join($this->
server->getDataPath(), 
"pocketmine.yml"))) !== 
false){
 
  171                $this->data->pocketmineDotYml = $pocketmineDotYml;
 
  175        foreach(get_loaded_extensions() as $ext){
 
  176            $version = phpversion($ext);
 
  177            $extensions[$ext] = $version !== 
false ? $version : 
"**UNKNOWN**";
 
  179        $this->data->extensions = $extensions;
 
  181        $this->data->jit_mode = Utils::getOpcacheJitMode();
 
  183        if($this->
server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_REPORT_SEND_PHPINFO, 
true)){
 
  186            $this->data->phpinfo = ob_get_contents(); 
 
  191    private function baseCrash() : 
void{
 
  192        global $lastExceptionError, $lastError;
 
  194        if(isset($lastExceptionError)){
 
  195            $error = $lastExceptionError;
 
  197            $error = error_get_last();
 
  198            if($error === 
null || ($error[
"type"] & self::FATAL_ERROR_MASK) === 0){
 
  199                throw new \RuntimeException(
"Crash error information missing - did something use exit()?");
 
  201            $error[
"trace"] = Utils::printableTrace(Utils::currentTrace(3)); 
 
  202            $error[
"fullFile"] = $error[
"file"];
 
  203            $error[
"file"] = Filesystem::cleanPath($error[
"file"]);
 
  206            }
catch(\InvalidArgumentException $e){
 
  209            if(($pos = strpos($error[
"message"], 
"\n")) !== 
false){
 
  210                $error[
"message"] = substr($error[
"message"], 0, $pos);
 
  212            $error[
"thread"] = 
"Main";
 
  214        $error[
"message"] = mb_scrub($error[
"message"], 
'UTF-8');
 
  216        if(isset($lastError)){
 
  217            $this->data->lastError = $lastError;
 
  218            $this->data->lastError[
"message"] = mb_scrub($this->data->lastError[
"message"], 
'UTF-8');
 
  219            $this->data->lastError[
"trace"] = array_map(array: $lastError[
"trace"], callback: fn(
ThreadCrashInfoFrame $frame) => $frame->getPrintableFrame());
 
  222        $this->data->error = $error;
 
  223        unset($this->data->error[
"fullFile"]);
 
  224        unset($this->data->error[
"trace"]);
 
  226        $this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_NONE;
 
  227        if(!$this->determinePluginFromFile($error[
"fullFile"], 
true)){ 
 
  228            foreach($error[
"trace"] as $frame){
 
  229                $frameFile = $frame->getFile();
 
  230                if($frameFile === 
null){
 
  233                if($this->determinePluginFromFile($frameFile, 
false)){
 
  239        if($this->
server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_REPORT_SEND_CODE, 
true) && file_exists($error[
"fullFile"])){
 
  240            $file = @file($error[
"fullFile"], FILE_IGNORE_NEW_LINES);
 
  242                for($l = max(0, $error[
"line"] - 10); $l < $error[
"line"] + 10 && isset($file[$l]); ++$l){
 
  243                    $this->data->code[$l + 1] = $file[$l];
 
  248        $this->data->trace = array_map(array: $error[
"trace"], callback: fn(
ThreadCrashInfoFrame $frame) => $frame->getPrintableFrame());
 
  249        $this->data->thread = $error[
"thread"];
 
  252    private function determinePluginFromFile(
string $filePath, 
bool $crashFrame) : 
bool{
 
  253        $frameCleanPath = Filesystem::cleanPath($filePath);
 
  254        if(!str_starts_with($frameCleanPath, Filesystem::CLEAN_PATH_SRC_PREFIX)){
 
  256                $this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_DIRECT;
 
  258                $this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_INDIRECT;
 
  261            if(file_exists($filePath)){
 
  262                $reflection = new \ReflectionClass(PluginBase::class);
 
  263                $file = $reflection->getProperty(
"file");
 
  264                foreach($this->
server->getPluginManager()->getPlugins() as $plugin){
 
  265                    $filePath = Filesystem::cleanPath($file->getValue($plugin));
 
  266                    if(str_starts_with($frameCleanPath, $filePath)){
 
  267                        $this->data->plugin = $plugin->getName();
 
  277    private function generalData() : 
void{
 
  278        $composerLibraries = [];
 
  279        foreach(InstalledVersions::getInstalledPackages() as $package){
 
  280            $composerLibraries[$package] = sprintf(
 
  282                InstalledVersions::getPrettyVersion($package) ?? 
"unknown",
 
  283                InstalledVersions::getReference($package) ?? 
"unknown" 
  288            name: $this->
server->getName(),
 
  289            base_version: VersionInfo::BASE_VERSION,
 
  290            build: VersionInfo::BUILD_NUMBER(),
 
  291            is_dev: VersionInfo::IS_DEVELOPMENT_BUILD,
 
  292            protocol: ProtocolInfo::CURRENT_PROTOCOL,
 
  293            git: VersionInfo::GIT_HASH(),
 
  294            uname: php_uname(
"a"),
 
  296            zend: zend_version(),
 
  299            composer_libraries: $composerLibraries,