diff --git a/vassal-app/src/main/java/VASSAL/build/module/BasicLogger.java b/vassal-app/src/main/java/VASSAL/build/module/BasicLogger.java index 0bc0b97f2c..6e35282e8f 100644 --- a/vassal-app/src/main/java/VASSAL/build/module/BasicLogger.java +++ b/vassal-app/src/main/java/VASSAL/build/module/BasicLogger.java @@ -62,6 +62,9 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; /** * BasicLogger deals with VLOG Vassal Log files (i.e. NOT the errorLog--see below): @@ -105,6 +108,7 @@ public class BasicLogger implements Logger, Buildable, GameComponent, CommandEnc private NamedHotKeyConfigurer endLogKeyConfig; private boolean undoInProgress = false; + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); public BasicLogger() { super(); @@ -114,6 +118,23 @@ public BasicLogger() { newLogAction.setEnabled(false); logInput = new ArrayList<>(); logOutput = new ArrayList<>(); + startAutosave(); + } + + private void startAutosave() { + scheduler.scheduleAtFixedRate(this::autosave, 1, 5, TimeUnit.MINUTES); + } + + private void autosave() { + if (isLogging() && !isReplaying()) { + final File autosaveFile = new File(outputFile.getParent(), "autosave_" + outputFile.getName()); //$NON-NLS-1$ + try { + write(autosaveFile); + } + catch (IOException e) { + GameModule.getGameModule().warn("Failed to autosave log file: " + e.getMessage()); + } + } } /** Presently no XML attributes or subcomponents to be built */ @@ -459,6 +480,27 @@ public void write() throws IOException { endLogAction.setEnabled(false); } + /** + * Write the logfile to a specified file. This is used by the autosave feature. + */ + private void write(File file) throws IOException { + if (!logOutput.isEmpty()) { + GameModule.getGameModule().warn(Resources.getString("BasicLogger.logging_autosaving")); //$NON-NLS-1$ + final Command log = beginningState; + for (final Command c : logOutput) { + log.append(new LogCommand(c, logInput, stepAction)); + } + final String logString = GameModule.getGameModule().encode(log); + + try (ZipWriter zw = new ZipWriter(file)) { + try (OutputStream out = new ObfuscatingOutputStream(new BufferedOutputStream(zw.write(GameState.SAVEFILE_ZIP_ENTRY)))) { + out.write(logString.getBytes(StandardCharsets.UTF_8)); + } + metadata.save(zw); + } + } + } + private File getSaveFile() { final GameModule g = GameModule.getGameModule(); diff --git a/vassal-app/src/main/resources/VASSAL/i18n/VASSAL.properties b/vassal-app/src/main/resources/VASSAL/i18n/VASSAL.properties index 6f170f2a31..1f2924bc2e 100644 --- a/vassal-app/src/main/resources/VASSAL/i18n/VASSAL.properties +++ b/vassal-app/src/main/resources/VASSAL/i18n/VASSAL.properties @@ -359,6 +359,7 @@ BasicLogger.replay_commencing=New Session Beginning BasicLogger.replay_completed=End of Logfile BasicLogger.dont_prompt_again=Don't prompt again BasicLogger.logging_begun=Logging begun +BasicLogger.logging_autosaving=Autosaving logfile BasicLogger.logfile_written=Logfile written. BasicLogger.undo_icon=Undo button icon BasicLogger.unsaved_log=Unsaved log