|
42 | 42 | import org.jackhuang.hmcl.task.Task; |
43 | 43 | import org.jackhuang.hmcl.ui.construct.TwoLineListItem; |
44 | 44 | import org.jackhuang.hmcl.util.*; |
45 | | -import org.jackhuang.hmcl.util.logging.Logger; |
46 | 45 | import org.jackhuang.hmcl.util.io.FileUtils; |
| 46 | +import org.jackhuang.hmcl.util.io.JarUtils; |
| 47 | +import org.jackhuang.hmcl.util.logging.Logger; |
47 | 48 | import org.jackhuang.hmcl.util.platform.*; |
| 49 | +import org.jackhuang.hmcl.util.platform.hardware.CentralProcessor; |
| 50 | +import org.jackhuang.hmcl.util.platform.hardware.GraphicsCard; |
48 | 51 |
|
49 | 52 | import java.io.IOException; |
| 53 | +import java.lang.management.ManagementFactory; |
| 54 | +import java.lang.management.MemoryPoolMXBean; |
50 | 55 | import java.nio.file.Files; |
51 | 56 | import java.nio.file.Path; |
52 | 57 | import java.nio.file.Paths; |
|
59 | 64 | import java.util.stream.Collectors; |
60 | 65 |
|
61 | 66 | import static org.jackhuang.hmcl.util.DataSizeUnit.MEGABYTES; |
62 | | -import static org.jackhuang.hmcl.util.logging.Logger.LOG; |
63 | 67 | import static org.jackhuang.hmcl.util.Pair.pair; |
64 | 68 | import static org.jackhuang.hmcl.util.i18n.I18n.i18n; |
| 69 | +import static org.jackhuang.hmcl.util.logging.Logger.LOG; |
65 | 70 |
|
66 | 71 | public class GameCrashWindow extends Stage { |
67 | 72 | private final Version version; |
@@ -263,11 +268,121 @@ private void showLogWindow() { |
263 | 268 |
|
264 | 269 | private void exportGameCrashInfo() { |
265 | 270 | Path logFile = Paths.get("minecraft-exported-crash-info-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH-mm-ss")) + ".zip").toAbsolutePath(); |
266 | | - |
267 | | - CompletableFuture.supplyAsync(() -> |
268 | | - logs.stream().map(Log::getLog).collect(Collectors.joining("\n"))) |
| 271 | + Path crashReportFile = repository.getRunDirectory(version.getId()).toPath().resolve("hmcl-game-crash-report.log"); |
| 272 | + |
| 273 | + CompletableFuture.supplyAsync(() -> { |
| 274 | + try { |
| 275 | + StringBuilder report = new StringBuilder(); |
| 276 | + report.append("=== HMCL Game Crash Report ===\n"); |
| 277 | + report.append("HMCL Version: ").append(Metadata.VERSION).append("\n"); |
| 278 | + report.append("Current Directory: ").append(Metadata.CURRENT_DIRECTORY).append("\n"); |
| 279 | + report.append("HMCL Global Directory: ").append(Metadata.HMCL_GLOBAL_DIRECTORY).append("\n"); |
| 280 | + report.append("HMCL Current Directory: ").append(Metadata.HMCL_CURRENT_DIRECTORY).append("\n"); |
| 281 | + report.append("HMCL Jar Path: ").append(Lang.requireNonNullElse(JarUtils.thisJarPath(), "Not Found")).append("\n"); |
| 282 | + report.append("HMCL Log File: ").append(Lang.requireNonNullElse(LOG.getLogFile(), "In Memory")).append("\n\n"); |
| 283 | + |
| 284 | + report.append("== System Information ==\n"); |
| 285 | + report.append("Operating System: ").append(OperatingSystem.OS_RELEASE_PRETTY_NAME == null |
| 286 | + ? OperatingSystem.SYSTEM_NAME + ' ' + OperatingSystem.SYSTEM_VERSION |
| 287 | + : OperatingSystem.OS_RELEASE_PRETTY_NAME + " (" + OperatingSystem.SYSTEM_NAME + ' ' + OperatingSystem.SYSTEM_VERSION + ')').append("\n"); |
| 288 | + |
| 289 | + if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { |
| 290 | + report.append("Processor Identifier: ").append(System.getenv("PROCESSOR_IDENTIFIER")).append("\n"); |
| 291 | + } |
| 292 | + |
| 293 | + report.append("System Architecture: ").append(Architecture.SYSTEM_ARCH.getDisplayName()).append("\n"); |
| 294 | + report.append("Native Encoding: ").append(OperatingSystem.NATIVE_CHARSET).append("\n"); |
| 295 | + report.append("JNU Encoding: ").append(System.getProperty("sun.jnu.encoding")).append("\n"); |
| 296 | + |
| 297 | + if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { |
| 298 | + report.append("Code Page: ").append(OperatingSystem.CODE_PAGE).append("\n"); |
| 299 | + } |
| 300 | + |
| 301 | + if (OperatingSystem.CURRENT_OS.isLinuxOrBSD()) { |
| 302 | + report.append("XDG Session Type: ").append(System.getenv("XDG_SESSION_TYPE")).append("\n"); |
| 303 | + report.append("XDG Current Desktop: ").append(System.getenv("XDG_CURRENT_DESKTOP")).append("\n"); |
| 304 | + } |
| 305 | + |
| 306 | + report.append("Total Memory: ").append(total_memory).append("\n\n"); |
| 307 | + |
| 308 | + report.append("== Hardware Information ==\n"); |
| 309 | + |
| 310 | + CentralProcessor cpu = SystemInfo.getCentralProcessor(); |
| 311 | + if (cpu != null) { |
| 312 | + report.append("CPU: ").append(cpu.toString().replace("\n", " ")).append("\n"); |
| 313 | + } |
| 314 | + |
| 315 | + List<GraphicsCard> graphicsCards = SystemInfo.getGraphicsCards(); |
| 316 | + if (graphicsCards != null) { |
| 317 | + if (graphicsCards.isEmpty()) { |
| 318 | + report.append("GPU: Not Found\n"); |
| 319 | + } else if (graphicsCards.size() == 1) { |
| 320 | + report.append("GPU: ").append(graphicsCards.get(0).toString().replace("\n", " ")).append("\n"); |
| 321 | + } else { |
| 322 | + int index = 1; |
| 323 | + for (GraphicsCard graphicsCard : graphicsCards) { |
| 324 | + report.append("GPU ").append(index++).append(": ") |
| 325 | + .append(graphicsCard.toString().replace("\n", " ")).append("\n"); |
| 326 | + } |
| 327 | + } |
| 328 | + } |
| 329 | + |
| 330 | + long totalMemorySize = SystemInfo.getTotalMemorySize(); |
| 331 | + long usedMemorySize = SystemInfo.getUsedMemorySize(); |
| 332 | + report.append("Memory: ") |
| 333 | + .append(DataSizeUnit.format(usedMemorySize)) |
| 334 | + .append(" / ") |
| 335 | + .append(DataSizeUnit.format(totalMemorySize)); |
| 336 | + if (totalMemorySize > 0 && usedMemorySize > 0) { |
| 337 | + report.append(" (").append((int) (((double) usedMemorySize / totalMemorySize) * 100)).append("%)\n"); |
| 338 | + } else { |
| 339 | + report.append("\n"); |
| 340 | + } |
| 341 | + |
| 342 | + report.append("\n"); |
| 343 | + |
| 344 | + report.append("== Java Information ==\n"); |
| 345 | + report.append("Java Architecture: ").append(Architecture.CURRENT_ARCH.getDisplayName()).append("\n"); |
| 346 | + report.append("Java Version: ").append(System.getProperty("java.version")).append(", ").append(System.getProperty("java.vendor")).append("\n"); |
| 347 | + report.append("Java VM Version: ").append(System.getProperty("java.vm.name")).append(" (").append(System.getProperty("java.vm.info")).append("), ").append(System.getProperty("java.vm.vendor")).append("\n"); |
| 348 | + report.append("Java Home: ").append(System.getProperty("java.home")).append("\n"); |
| 349 | + report.append("Game Java Version: ").append(java).append("\n"); |
| 350 | + report.append("Game Java Path: ").append(launchOptions.getJava().getBinary().toAbsolutePath()).append("\n"); |
| 351 | + report.append("JVM Max Memory: ").append(MEGABYTES.formatBytes(Runtime.getRuntime().maxMemory())).append("\n"); |
| 352 | + report.append("Allocated Memory: ").append(memory).append("\n"); |
| 353 | + |
| 354 | + report.append("== Game Information ==\n"); |
| 355 | + report.append("Game Version: ").append(version.getId()).append("\n"); |
| 356 | + report.append("Game Directory: ").append(launchOptions.getGameDir().getAbsolutePath()).append("\n"); |
| 357 | + |
| 358 | + report.append("\n== Mod Loader Information ==\n"); |
| 359 | + for (LibraryAnalyzer.LibraryType type : LibraryAnalyzer.LibraryType.values()) { |
| 360 | + if (!type.getPatchId().isEmpty()) { |
| 361 | + analyzer.getVersion(type).ifPresent(ver -> { |
| 362 | + report.append(type.getPatchId()).append(": ").append(ver).append("\n"); |
| 363 | + }); |
| 364 | + } |
| 365 | + } |
| 366 | + |
| 367 | + report.append("\n== Crash Analysis ==\n"); |
| 368 | + for (Node node : reasonTextFlow.getChildren()) { |
| 369 | + if (node instanceof Text) { |
| 370 | + report.append(((Text) node).getText()); |
| 371 | + } |
| 372 | + } |
| 373 | + |
| 374 | + // Structure of game mod directory |
| 375 | + report.append(FileUtils.printFileStructure(repository.getModManager(version.getId()).getModsDirectory(), 10)); |
| 376 | + |
| 377 | + Files.write(crashReportFile, report.toString().getBytes()); |
| 378 | + } catch (IOException e) { |
| 379 | + LOG.warning("Failed to write crash report file", e); |
| 380 | + } |
| 381 | + return logs.stream().map(Log::getLog).collect(Collectors.joining("\n")); |
| 382 | + }) |
269 | 383 | .thenComposeAsync(logs -> |
270 | | - LogExporter.exportLogs(logFile, repository, launchOptions.getVersionName(), logs, new CommandBuilder().addAll(managedProcess.getCommands()).toString())) |
| 384 | + LogExporter.exportLogs(logFile, repository, launchOptions.getVersionName(), logs, new CommandBuilder().addAll(managedProcess.getCommands()).toString(), crashReportFile) |
| 385 | + ) |
271 | 386 | .handleAsync((result, exception) -> { |
272 | 387 | Alert alert; |
273 | 388 |
|
|
0 commit comments