diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8886e69c..13b14830 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,17 +30,17 @@ jobs: echo "Current ref: ${{ github.ref }}" - name: Clone Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup JavaJDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'liberica' cache: 'gradle' - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v3 with: gradle-version: 8.1.1 @@ -48,21 +48,21 @@ jobs: run: gradle clean distAll - name: Upload Exe - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ ! startsWith(github.ref, 'refs/tags/') }} with: name: ArkPets.exe path: desktop/build/dist/*.exe - name: Upload Zip - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ ! startsWith(github.ref, 'refs/tags/') }} with: name: ArkPets.zip path: desktop/build/dist/*.zip - name: Upload Jar - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ ! startsWith(github.ref, 'refs/tags/') }} with: name: ArkPets.jar diff --git a/.gitignore b/.gitignore index 13559cee..e59da0b3 100644 --- a/.gitignore +++ b/.gitignore @@ -22,5 +22,8 @@ test*/ *.jar # Exclude runtime files -/ArkPetsConfig.json -/models_data.json +ArkPetsConfig.json +models_data.json +models*/ +logs*/ +err** diff --git a/.idea/Ark-Pets.iml b/.idea/Ark-Pets.iml index eb0c48ed..76089bcf 100644 --- a/.idea/Ark-Pets.iml +++ b/.idea/Ark-Pets.iml @@ -7,6 +7,7 @@ + \ No newline at end of file diff --git a/.idea/GitCommitMessageStorage.xml b/.idea/GitCommitMessageStorage.xml new file mode 100644 index 00000000..e4fd56aa --- /dev/null +++ b/.idea/GitCommitMessageStorage.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/core/src/cn/harryh/arkpets/concurrent/ProcessPool.java b/core/src/cn/harryh/arkpets/concurrent/ProcessPool.java index 0c45efc1..5acb0de1 100644 --- a/core/src/cn/harryh/arkpets/concurrent/ProcessPool.java +++ b/core/src/cn/harryh/arkpets/concurrent/ProcessPool.java @@ -22,11 +22,14 @@ public final class ProcessPool implements Executor { return thread; }); - private static ProcessPool instance = null; + private static volatile ProcessPool instance = null; - public static synchronized ProcessPool getInstance() { + public static ProcessPool getInstance() { if (instance == null) - instance = new ProcessPool(); + synchronized (ProcessPool.class) { + if (instance == null) + instance = new ProcessPool(); + } return instance; } @@ -82,14 +85,20 @@ public boolean isSuccess() { public static class UnexpectedExitCodeException extends Exception { private final int exitCode; + private final long processId; - public UnexpectedExitCodeException(int exitCode) { + public UnexpectedExitCodeException(int exitCode, long processId) { this.exitCode = exitCode; + this.processId = processId; } @Override public String getMessage() { return "The process exited with a non-zero exit code: " + exitCode; } + + public long getProcessId() { + return processId; + } } } diff --git a/core/src/cn/harryh/arkpets/concurrent/SocketServer.java b/core/src/cn/harryh/arkpets/concurrent/SocketServer.java index 94630c8e..34159c62 100644 --- a/core/src/cn/harryh/arkpets/concurrent/SocketServer.java +++ b/core/src/cn/harryh/arkpets/concurrent/SocketServer.java @@ -15,6 +15,7 @@ import java.util.UUID; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; import static cn.harryh.arkpets.Const.serverPorts; @@ -24,12 +25,15 @@ public final class SocketServer { private ServerSocket serverSocket = null; private final Set sessionList = new CopyOnWriteArraySet<>(); private Thread listener; + private final AtomicBoolean running = new AtomicBoolean(false); + private static volatile SocketServer instance = null; - private static SocketServer instance = null; - - public static synchronized SocketServer getInstance() { + public static SocketServer getInstance() { if (instance == null) - instance = new SocketServer(); + synchronized (SocketServer.class) { + if (instance == null) + instance = new SocketServer(); + } return instance; } @@ -43,6 +47,8 @@ private SocketServer() { */ public synchronized void startServer(HostTray hostTray) throws PortUtils.NoPortAvailableException, PortUtils.ServerCollisionException { + if (running.get()) + return; Logger.info("SocketServer", "Request to start server"); this.port = PortUtils.getAvailablePort(serverPorts); listener = new Thread(() -> { @@ -65,15 +71,19 @@ public synchronized void startServer(HostTray hostTray) } }); ProcessPool.getInstance().execute(listener); + running.set(true); } /** Stops the server and close all the sessions. */ public synchronized void stopServer() { + if (!running.get()) + return; Logger.info("SocketServer", "Request to stop server"); if (listener != null) listener.interrupt(); sessionList.forEach(SocketSession::close); + running.set(false); } @Override diff --git a/core/src/cn/harryh/arkpets/utils/Logger.java b/core/src/cn/harryh/arkpets/utils/Logger.java index 1eaf6b51..5430410a 100644 --- a/core/src/cn/harryh/arkpets/utils/Logger.java +++ b/core/src/cn/harryh/arkpets/utils/Logger.java @@ -7,10 +7,12 @@ import org.apache.log4j.helpers.LogLog; import org.apache.log4j.varia.LevelRangeFilter; -import java.io.*; +import java.io.File; +import java.io.IOException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.*; +import java.util.Arrays; +import java.util.List; import static cn.harryh.arkpets.Const.charsetDefault; @@ -95,6 +97,13 @@ public String getHeader() { }; } + /** Get log file path + * @return log file path + */ + public static String getLogFilePath() { + return logFilePath; + } + /** Sets a new log level. * @param level The new level. */ diff --git a/desktop/src/cn/harryh/arkpets/controllers/RootModule.java b/desktop/src/cn/harryh/arkpets/controllers/RootModule.java index f77a0a07..1351afe0 100644 --- a/desktop/src/cn/harryh/arkpets/controllers/RootModule.java +++ b/desktop/src/cn/harryh/arkpets/controllers/RootModule.java @@ -166,7 +166,7 @@ protected Boolean call() throws InterruptedException, ExecutionException { if (!future.get().isSuccess()) { int exitCode = future.get().exitValue(); Logger.warn("Launcher", "Detected an abnormal finalization of an ArkPets thread (exit code " + exitCode + "). Please check the log file for details."); - lastLaunchFailed = new ProcessPool.UnexpectedExitCodeException(exitCode); + lastLaunchFailed = new ProcessPool.UnexpectedExitCodeException(exitCode, future.get().processId()); return false; } Logger.debug("Launcher", "Detected a successful finalization of an ArkPets thread."); diff --git a/desktop/src/cn/harryh/arkpets/guitasks/ZipTask.java b/desktop/src/cn/harryh/arkpets/guitasks/ZipTask.java index 81ab34ef..b246841d 100644 --- a/desktop/src/cn/harryh/arkpets/guitasks/ZipTask.java +++ b/desktop/src/cn/harryh/arkpets/guitasks/ZipTask.java @@ -10,7 +10,10 @@ import javafx.scene.layout.StackPane; import java.io.File; +import java.nio.file.Paths; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; public class ZipTask extends GuiTask { @@ -23,6 +26,16 @@ public ZipTask(StackPane root, GuiTaskStyle style, String zipPath, Map contents) { + super(root, style); + this.zipPath = zipPath; + this.contents = contents.stream() + .collect(Collectors.toMap( + path -> path, + path -> Paths.get(path).getFileName().toString() + )); + } + @Override protected String getHeader() { return "正在创建压缩文件..."; diff --git a/desktop/src/cn/harryh/arkpets/utils/GuiPrefabs.java b/desktop/src/cn/harryh/arkpets/utils/GuiPrefabs.java index 5e70a818..201af2f2 100644 --- a/desktop/src/cn/harryh/arkpets/utils/GuiPrefabs.java +++ b/desktop/src/cn/harryh/arkpets/utils/GuiPrefabs.java @@ -5,6 +5,8 @@ import cn.harryh.arkpets.Const; import cn.harryh.arkpets.concurrent.ProcessPool; +import cn.harryh.arkpets.guitasks.GuiTask; +import cn.harryh.arkpets.guitasks.ZipTask; import com.jfoenix.controls.*; import javafx.animation.FadeTransition; import javafx.animation.KeyFrame; @@ -20,18 +22,20 @@ import javafx.scene.layout.VBox; import javafx.scene.paint.Paint; import javafx.scene.shape.SVGPath; +import javafx.stage.FileChooser; import javafx.stage.Window; import javafx.util.Duration; import javax.net.ssl.SSLException; +import java.io.File; import java.io.FileNotFoundException; import java.net.ConnectException; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; import java.util.zip.ZipException; import static cn.harryh.arkpets.Const.durationNormal; @@ -43,14 +47,14 @@ public class GuiPrefabs { public static class Colors { - public static final String COLOR_INFO = "#37B"; - public static final String COLOR_SUCCESS = "#5B5"; - public static final String COLOR_WARNING = "#E93"; - public static final String COLOR_DANGER = "#F54"; - public static final String COLOR_WHITE = "#FFF"; - public static final String COLOR_BLACK = "#000"; - public static final String COLOR_DARK_GRAY = "#222"; - public static final String COLOR_GRAY = "#444"; + public static final String COLOR_INFO = "#37B"; + public static final String COLOR_SUCCESS = "#5B5"; + public static final String COLOR_WARNING = "#E93"; + public static final String COLOR_DANGER = "#F54"; + public static final String COLOR_WHITE = "#FFF"; + public static final String COLOR_BLACK = "#000"; + public static final String COLOR_DARK_GRAY = "#222"; + public static final String COLOR_GRAY = "#444"; public static final String COLOR_LIGHT_GRAY = "#666"; private Colors() { @@ -109,17 +113,17 @@ public static void replaceStyleClass(Node node, String from, String to) { public static class Icons { - public static final String ICON_INFO = "m12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-.001 5.75c.69 0 1.251.56 1.251 1.25s-.561 1.25-1.251 1.25-1.249-.56-1.249-1.25.559-1.25 1.249-1.25zm2.001 12.25h-4v-1c.484-.179 1-.201 1-.735v-4.467c0-.534-.516-.618-1-.797v-1h3v6.265c0 .535.517.558 1 .735v.999z"; - public static final String ICON_INFO_ALT = "m12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-.001 5.75c.69 0 1.251.56 1.251 1.25s-.561 1.25-1.251 1.25-1.249-.56-1.249-1.25.559-1.25 1.249-1.25zm2.001 12.25h-4v-1c.484-.179 1-.201 1-.735v-4.467c0-.534-.516-.618-1-.797v-1h3v6.265c0 .535.517.558 1 .735v.999z"; - public static final String ICON_HELP = "m12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm1.25 17c0 .69-.559 1.25-1.25 1.25-.689 0-1.25-.56-1.25-1.25s.561-1.25 1.25-1.25c.691 0 1.25.56 1.25 1.25zm1.393-9.998c-.608-.616-1.515-.955-2.551-.955-2.18 0-3.59 1.55-3.59 3.95h2.011c0-1.486.829-2.013 1.538-2.013.634 0 1.307.421 1.364 1.226.062.847-.39 1.277-.962 1.821-1.412 1.343-1.438 1.993-1.432 3.468h2.005c-.013-.664.03-1.203.935-2.178.677-.73 1.519-1.638 1.536-3.022.011-.924-.284-1.719-.854-2.297z"; - public static final String ICON_HELP_ALT = "m12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm0 18.25c-.69 0-1.25-.56-1.25-1.25s.56-1.25 1.25-1.25c.691 0 1.25.56 1.25 1.25s-.559 1.25-1.25 1.25zm1.961-5.928c-.904.975-.947 1.514-.935 2.178h-2.005c-.007-1.475.02-2.125 1.431-3.468.573-.544 1.025-.975.962-1.821-.058-.805-.73-1.226-1.365-1.226-.709 0-1.538.527-1.538 2.013h-2.01c0-2.4 1.409-3.95 3.59-3.95 1.036 0 1.942.339 2.55.955.57.578.865 1.372.854 2.298-.016 1.383-.857 2.291-1.534 3.021z"; - public static final String ICON_SUCCESS = "m12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm4.393 7.5l-5.643 5.784-2.644-2.506-1.856 1.858 4.5 4.364 7.5-7.643-1.857-1.857z"; + public static final String ICON_INFO = "m12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-.001 5.75c.69 0 1.251.56 1.251 1.25s-.561 1.25-1.251 1.25-1.249-.56-1.249-1.25.559-1.25 1.249-1.25zm2.001 12.25h-4v-1c.484-.179 1-.201 1-.735v-4.467c0-.534-.516-.618-1-.797v-1h3v6.265c0 .535.517.558 1 .735v.999z"; + public static final String ICON_INFO_ALT = "m12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-.001 5.75c.69 0 1.251.56 1.251 1.25s-.561 1.25-1.251 1.25-1.249-.56-1.249-1.25.559-1.25 1.249-1.25zm2.001 12.25h-4v-1c.484-.179 1-.201 1-.735v-4.467c0-.534-.516-.618-1-.797v-1h3v6.265c0 .535.517.558 1 .735v.999z"; + public static final String ICON_HELP = "m12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm1.25 17c0 .69-.559 1.25-1.25 1.25-.689 0-1.25-.56-1.25-1.25s.561-1.25 1.25-1.25c.691 0 1.25.56 1.25 1.25zm1.393-9.998c-.608-.616-1.515-.955-2.551-.955-2.18 0-3.59 1.55-3.59 3.95h2.011c0-1.486.829-2.013 1.538-2.013.634 0 1.307.421 1.364 1.226.062.847-.39 1.277-.962 1.821-1.412 1.343-1.438 1.993-1.432 3.468h2.005c-.013-.664.03-1.203.935-2.178.677-.73 1.519-1.638 1.536-3.022.011-.924-.284-1.719-.854-2.297z"; + public static final String ICON_HELP_ALT = "m12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm0 18.25c-.69 0-1.25-.56-1.25-1.25s.56-1.25 1.25-1.25c.691 0 1.25.56 1.25 1.25s-.559 1.25-1.25 1.25zm1.961-5.928c-.904.975-.947 1.514-.935 2.178h-2.005c-.007-1.475.02-2.125 1.431-3.468.573-.544 1.025-.975.962-1.821-.058-.805-.73-1.226-1.365-1.226-.709 0-1.538.527-1.538 2.013h-2.01c0-2.4 1.409-3.95 3.59-3.95 1.036 0 1.942.339 2.55.955.57.578.865 1.372.854 2.298-.016 1.383-.857 2.291-1.534 3.021z"; + public static final String ICON_SUCCESS = "m12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm4.393 7.5l-5.643 5.784-2.644-2.506-1.856 1.858 4.5 4.364 7.5-7.643-1.857-1.857z"; public static final String ICON_SUCCESS_ALT = "m12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-1.25 17.292l-4.5-4.364 1.857-1.858 2.643 2.506 5.643-5.784 1.857 1.857-7.5 7.643z"; - public static final String ICON_WARNING = "m12 5.177l8.631 15.823h-17.262l8.631-15.823zm0-4.177l-12 22h24l-12-22zm-1 9h2v6h-2v-6zm1 9.75c-.689 0-1.25-.56-1.25-1.25s.561-1.25 1.25-1.25 1.25.56 1.25 1.25-.561 1.25-1.25 1.25z"; + public static final String ICON_WARNING = "m12 5.177l8.631 15.823h-17.262l8.631-15.823zm0-4.177l-12 22h24l-12-22zm-1 9h2v6h-2v-6zm1 9.75c-.689 0-1.25-.56-1.25-1.25s.561-1.25 1.25-1.25 1.25.56 1.25 1.25-.561 1.25-1.25 1.25z"; public static final String ICON_WARNING_ALT = "m12 1l-12 22h24l-12-22zm-1 8h2v7h-2v-7zm1 11.25c-.69 0-1.25-.56-1.25-1.25s.56-1.25 1.25-1.25 1.25.56 1.25 1.25-.56 1.25-1.25 1.25z"; - public static final String ICON_DANGER = "m16.142 2l5.858 5.858v8.284l-5.858 5.858h-8.284l-5.858-5.858v-8.284l5.858-5.858h8.284zm.829-2h-9.942l-7.029 7.029v9.941l7.029 7.03h9.941l7.03-7.029v-9.942l-7.029-7.029zm-8.482 16.992l3.518-3.568 3.554 3.521 1.431-1.43-3.566-3.523 3.535-3.568-1.431-1.432-3.539 3.583-3.581-3.457-1.418 1.418 3.585 3.473-3.507 3.566 1.419 1.417z"; - public static final String ICON_DANGER_ALT = "m16.971 0h-9.942l-7.029 7.029v9.941l7.029 7.03h9.941l7.03-7.029v-9.942l-7.029-7.029zm-1.402 16.945l-3.554-3.521-3.518 3.568-1.418-1.418 3.507-3.566-3.586-3.472 1.418-1.417 3.581 3.458 3.539-3.583 1.431 1.431-3.535 3.568 3.566 3.522-1.431 1.43z"; - public static final String ICON_UPDATE = "m12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm1 15.889v-2.223s-3.78-.114-7 3.333c1.513-6.587 7-7.778 7-7.778v-2.221l5 4.425-5 4.464z"; + public static final String ICON_DANGER = "m16.142 2l5.858 5.858v8.284l-5.858 5.858h-8.284l-5.858-5.858v-8.284l5.858-5.858h8.284zm.829-2h-9.942l-7.029 7.029v9.941l7.029 7.03h9.941l7.03-7.029v-9.942l-7.029-7.029zm-8.482 16.992l3.518-3.568 3.554 3.521 1.431-1.43-3.566-3.523 3.535-3.568-1.431-1.432-3.539 3.583-3.581-3.457-1.418 1.418 3.585 3.473-3.507 3.566 1.419 1.417z"; + public static final String ICON_DANGER_ALT = "m16.971 0h-9.942l-7.029 7.029v9.941l7.029 7.03h9.941l7.03-7.029v-9.942l-7.029-7.029zm-1.402 16.945l-3.554-3.521-3.518 3.568-1.418-1.418 3.507-3.566-3.586-3.472 1.418-1.417 3.581 3.458 3.539-3.583 1.431 1.431-3.535 3.568 3.566 3.522-1.431 1.43z"; + public static final String ICON_UPDATE = "m12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm1 15.889v-2.223s-3.78-.114-7 3.333c1.513-6.587 7-7.778 7-7.778v-2.221l5 4.425-5 4.464z"; /** Gets an SVGPath Node using the given path string and color. * @param svg The SVG path string. @@ -158,8 +162,8 @@ public static JFXDialog createCenteredDialog(StackPane root, boolean overlayClos public static JFXDialog createCommonDialog(StackPane root, Node graphic, String title, String header, String content, String detail) { JFXDialog dialog = DialogUtil.createCenteredDialog(root, false); VBox body = new VBox(); - Label h2 = (Label)DialogUtil.getPrefabsH2(header); - Label h3 = (Label)DialogUtil.getPrefabsH3(content); + Label h2 = (Label) DialogUtil.getPrefabsH2(header); + Label h3 = (Label) DialogUtil.getPrefabsH3(content); body.setSpacing(5); body.getChildren().add(h2); body.getChildren().add(new Separator()); @@ -185,8 +189,8 @@ public static JFXDialog createCommonDialog(StackPane root, Node graphic, String public static JFXDialog createConfirmDialog(StackPane root, Node graphic, String title, String header, String content, Runnable onConfirmed) { JFXDialog dialog = DialogUtil.createCenteredDialog(root, true); VBox body = new VBox(); - Label h2 = (Label)DialogUtil.getPrefabsH2(header); - Label h3 = (Label)DialogUtil.getPrefabsH3(content); + Label h2 = (Label) DialogUtil.getPrefabsH2(header); + Label h3 = (Label) DialogUtil.getPrefabsH3(content); body.setSpacing(5); body.getChildren().add(h2); body.getChildren().add(new Separator()); @@ -210,8 +214,8 @@ public static JFXDialog createErrorDialog(StackPane root, Throwable e) { JFXDialog dialog = DialogUtil.createCenteredDialog(root, false); VBox content = new VBox(); - Label h2 = (Label)DialogUtil.getPrefabsH2("啊哦~ ArkPets启动器抛出了一个异常。"); - Label h3 = (Label)DialogUtil.getPrefabsH3("请重试操作,或查看帮助文档与日志。如需联系开发者,请提供下述信息:"); + Label h2 = (Label) DialogUtil.getPrefabsH2("啊哦~ ArkPets启动器抛出了一个异常。"); + Label h3 = (Label) DialogUtil.getPrefabsH3("请重试操作,或查看帮助文档与日志。如需联系开发者,请提供下述信息:"); content.setSpacing(5); content.getChildren().add(h2); content.getChildren().add(new Separator()); @@ -231,7 +235,32 @@ public static JFXDialog createErrorDialog(StackPane root, Throwable e) { JFXDialogLayout layout = new JFXDialogLayout(); layout.setHeading(DialogUtil.getHeading(Icons.getIcon(Icons.ICON_DANGER, Colors.COLOR_DANGER), "发生异常", Colors.COLOR_DANGER)); layout.setBody(content); - layout.setActions(DialogUtil.getOkayButton(dialog, root)); + + JFXButton button = new JFXButton(); + button.setText("导出错误报告"); + button.setTextFill(Paint.valueOf(Colors.COLOR_WHITE)); + button.setStyle("-fx-font-size:13px;-fx-text-fill:" + Colors.COLOR_WHITE + ";-fx-background-color:" + Colors.COLOR_DARK_GRAY); + button.setOnAction(event -> { + List logList = new ArrayList<>(); + logList.add(Logger.getLogFilePath()); + if (e instanceof ProcessPool.UnexpectedExitCodeException exception) { + logList.add(String.format("%s.%d.log", Const.LogConfig.logCorePath, exception.getProcessId())); + } + FileChooser fileChooser = new FileChooser(); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Archives", "*.zip")); + fileChooser.setInitialDirectory(new File(".")); + fileChooser.setInitialFileName(String.format("error_logs_%s_%d.zip", + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), + System.currentTimeMillis())); + File zipFile = fileChooser.showSaveDialog(root.getScene().getWindow()); + if (zipFile == null) { + return; + } + new ZipTask(root, GuiTask.GuiTaskStyle.STRICT, zipFile.toString(), logList).start(); + disposeDialog(dialog, root); + }); + + layout.setActions(button, DialogUtil.getOkayButton(dialog, root)); dialog.setContent(layout); if (e instanceof ProcessPool.UnexpectedExitCodeException) { @@ -286,7 +315,7 @@ public static JFXDialog createErrorDialog(StackPane root, Throwable e) { } public static void attachAction(JFXDialog dialog, Node action, int index) { - ObservableList actionList = ((JFXDialogLayout)dialog.getContent()).getActions(); + ObservableList actionList = ((JFXDialogLayout) dialog.getContent()).getActions(); if (index < 0) actionList.add(action); else @@ -400,7 +429,8 @@ public int getActivatedId() { return activatedId; } - protected record PeerNodeData(Node[] nodes, EventHandler onActivating, EventHandler onSuppressing) { + protected record PeerNodeData(Node[] nodes, EventHandler onActivating, + EventHandler onSuppressing) { public void handleActivating() { for (Node node : nodes) activateNode(node);