87 private const FORMAT_VERSION = 4;
89 public const PLUGIN_INVOLVEMENT_NONE =
"none";
90 public const PLUGIN_INVOLVEMENT_DIRECT =
"direct";
91 public const PLUGIN_INVOLVEMENT_INDIRECT =
"indirect";
93 public const FATAL_ERROR_MASK =
94 E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR;
97 private string $encodedData;
99 public function __construct(
103 $now = microtime(
true);
106 $this->data->format_version = self::FORMAT_VERSION;
107 $this->data->time = $now;
108 $this->data->uptime = $now - $this->
server->getStartTime();
111 $this->generalData();
112 $this->pluginsData();
116 $json = json_encode($this->data, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
117 $this->encodedData = Utils::assumeNotFalse(zlib_encode($json, ZLIB_ENCODING_DEFLATE, 9),
"ZLIB compression failed");
120 public function getEncodedData() :
string{
121 return $this->encodedData;
129 $renderer->addLine();
130 $renderer->addLine(
"----------------------REPORT THE DATA BELOW THIS LINE-----------------------");
131 $renderer->addLine();
132 $renderer->addLine(
"===BEGIN CRASH DUMP===");
133 foreach(str_split(base64_encode($this->encodedData), 76) as $line){
134 $renderer->addLine($line);
136 $renderer->addLine(
"===END CRASH DUMP===");
139 private function pluginsData() :
void{
140 if($this->pluginManager !==
null){
141 $plugins = $this->pluginManager->getPlugins();
142 ksort($plugins, SORT_STRING);
143 foreach($plugins as $p){
144 $d = $p->getDescription();
147 version: $d->getVersion(),
148 authors: $d->getAuthors(),
149 api: $d->getCompatibleApis(),
150 enabled: $p->isEnabled(),
151 depends: $d->getDepend(),
152 softDepends: $d->getSoftDepend(),
154 load: mb_strtoupper($d->getOrder()->name),
155 website: $d->getWebsite()
161 private function extraData() :
void{
164 if($this->
server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_REPORT_SEND_SETTINGS,
true)){
165 $this->data->parameters = (array) $argv;
166 if(($serverDotProperties = @file_get_contents(Path::join($this->
server->getDataPath(),
"server.properties"))) !==
false){
167 $this->data->serverDotProperties = preg_replace(
"#^rcon\\.password=(.*)$#m",
"rcon.password=******", $serverDotProperties) ??
throw new AssumptionFailedError(
"Pattern is valid");
169 if(($pocketmineDotYml = @file_get_contents(Path::join($this->
server->getDataPath(),
"pocketmine.yml"))) !==
false){
170 $this->data->pocketmineDotYml = $pocketmineDotYml;
174 foreach(get_loaded_extensions() as $ext){
175 $version = phpversion($ext);
176 $extensions[$ext] = $version !==
false ? $version :
"**UNKNOWN**";
178 $this->data->extensions = $extensions;
180 $this->data->jit_mode = Utils::getOpcacheJitMode();
182 if($this->
server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_REPORT_SEND_PHPINFO,
true)){
185 $this->data->phpinfo = ob_get_contents();
190 private function baseCrash() :
void{
191 global $lastExceptionError, $lastError;
193 if(isset($lastExceptionError)){
194 $error = $lastExceptionError;
196 $error = error_get_last();
197 if($error ===
null || ($error[
"type"] & self::FATAL_ERROR_MASK) === 0){
198 throw new \RuntimeException(
"Crash error information missing - did something use exit()?");
200 $error[
"trace"] = Utils::printableTrace(Utils::currentTrace(3));
201 $error[
"fullFile"] = $error[
"file"];
202 $error[
"file"] = Filesystem::cleanPath($error[
"file"]);
205 }
catch(\InvalidArgumentException $e){
208 if(($pos = strpos($error[
"message"],
"\n")) !==
false){
209 $error[
"message"] = substr($error[
"message"], 0, $pos);
211 $error[
"thread"] =
"Main";
213 $error[
"message"] = mb_scrub($error[
"message"],
'UTF-8');
215 if(isset($lastError)){
216 $this->data->lastError = $lastError;
217 $this->data->lastError[
"message"] = mb_scrub($this->data->lastError[
"message"],
'UTF-8');
218 $this->data->lastError[
"trace"] = array_map(array: $lastError[
"trace"], callback: fn(
ThreadCrashInfoFrame $frame) => $frame->getPrintableFrame());
221 $this->data->error = $error;
222 unset($this->data->error[
"fullFile"]);
223 unset($this->data->error[
"trace"]);
225 $this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_NONE;
226 if(!$this->determinePluginFromFile($error[
"fullFile"],
true)){
227 foreach($error[
"trace"] as $frame){
228 $frameFile = $frame->getFile();
229 if($frameFile ===
null){
232 if($this->determinePluginFromFile($frameFile,
false)){
238 if($this->
server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_REPORT_SEND_CODE,
true) && file_exists($error[
"fullFile"])){
239 $file = @file($error[
"fullFile"], FILE_IGNORE_NEW_LINES);
241 for($l = max(0, $error[
"line"] - 10); $l < $error[
"line"] + 10 && isset($file[$l]); ++$l){
242 $this->data->code[$l + 1] = $file[$l];
247 $this->data->trace = array_map(array: $error[
"trace"], callback: fn(
ThreadCrashInfoFrame $frame) => $frame->getPrintableFrame());
248 $this->data->thread = $error[
"thread"];
251 private function determinePluginFromFile(
string $filePath,
bool $crashFrame) :
bool{
252 $frameCleanPath = Filesystem::cleanPath($filePath);
253 if(!str_starts_with($frameCleanPath, Filesystem::CLEAN_PATH_SRC_PREFIX)){
255 $this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_DIRECT;
257 $this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_INDIRECT;
260 if(file_exists($filePath)){
261 foreach($this->
server->getPluginManager()->getPlugins() as $plugin){
262 $filePath = Filesystem::cleanPath($plugin->getFile());
263 if(str_starts_with($frameCleanPath, $filePath)){
264 $this->data->plugin = $plugin->getName();
274 private function generalData() :
void{
275 $composerLibraries = [];
276 foreach(InstalledVersions::getInstalledPackages() as $package){
277 $composerLibraries[$package] = sprintf(
279 InstalledVersions::getPrettyVersion($package) ??
"unknown",
280 InstalledVersions::getReference($package) ??
"unknown"
285 name: $this->
server->getName(),
286 base_version: VersionInfo::BASE_VERSION,
287 build: VersionInfo::BUILD_NUMBER(),
288 is_dev: VersionInfo::IS_DEVELOPMENT_BUILD,
289 protocol: ProtocolInfo::CURRENT_PROTOCOL,
290 git: VersionInfo::GIT_HASH(),
291 uname: php_uname(
"a"),
293 zend: zend_version(),
296 composer_libraries: $composerLibraries,