diff --git a/game-app/game-core/src/main/java/games/strategy/engine/framework/GameDataManager.java b/game-app/game-core/src/main/java/games/strategy/engine/framework/GameDataManager.java index cdf35a41f12..31387b6e3c9 100644 --- a/game-app/game-core/src/main/java/games/strategy/engine/framework/GameDataManager.java +++ b/game-app/game-core/src/main/java/games/strategy/engine/framework/GameDataManager.java @@ -20,6 +20,7 @@ import java.util.Optional; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; +import lombok.Builder; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.triplea.game.server.HeadlessGameServer; @@ -191,7 +192,7 @@ public static void saveGame( try (OutputStream os = Files.newOutputStream(tempFile); OutputStream bufferedOutStream = new BufferedOutputStream(os); OutputStream zippedOutStream = new GZIPOutputStream(bufferedOutStream)) { - saveGameUncompressed(zippedOutStream, gameData, true, engineVersion); + saveGameUncompressed(zippedOutStream, gameData, Options.withEverything(), engineVersion); } // now write to sink (ensure sink is closed per method contract) @@ -204,10 +205,25 @@ public static void saveGame( } } + @Builder + public static class Options { + @Builder.Default boolean withDelegates = false; + @Builder.Default boolean withHistory = false; + @Builder.Default boolean withAttachmentXmlData = false; + + public static Options withEverything() { + return builder().withDelegates(true).withHistory(true).withAttachmentXmlData(true).build(); + } + + public static Options forBattleCalculator() { + return builder().build(); + } + } + public static void saveGameUncompressed( final OutputStream sink, final GameData data, - final boolean saveDelegateInfoHistoryAndAttachmentsOrder, + final Options options, final Version engineVersion) throws IOException { // write to temporary file first in case of error @@ -215,19 +231,26 @@ public static void saveGameUncompressed( outStream.writeObject(engineVersion); data.acquireReadLock(); try { - if (saveDelegateInfoHistoryAndAttachmentsOrder) { - outStream.writeObject(data); - writeDelegates(data, outStream); - } else { - // TODO: Attachment order data is only used for XML export and takes up lots of memory. - // Could we remove it and just get the info again from the XML when exporting? - final var attachments = data.getAttachmentOrderAndValues(); - final var history = data.getHistory(); - data.setAttachmentOrderAndValues(null); + final var history = data.getHistory(); + if (!options.withHistory) { data.resetHistory(); - outStream.writeObject(data); + } + // TODO: Attachment order data is only used for XML export and takes up lots of memory. + // Could we remove it and just get the info again from the XML when exporting? + final var attachments = data.getAttachmentOrderAndValues(); + if (!options.withAttachmentXmlData) { + data.setAttachmentOrderAndValues(null); + } + outStream.writeObject(data); + if (!options.withAttachmentXmlData) { data.setAttachmentOrderAndValues(attachments); + } + if (!options.withHistory) { data.setHistory(history); + } + if (options.withDelegates) { + writeDelegates(data, outStream); + } else { outStream.writeObject(DELEGATE_LIST_END); } } finally { diff --git a/game-app/game-core/src/main/java/games/strategy/engine/framework/GameDataUtils.java b/game-app/game-core/src/main/java/games/strategy/engine/framework/GameDataUtils.java index dc1bc8195bb..e171c20dc6d 100644 --- a/game-app/game-core/src/main/java/games/strategy/engine/framework/GameDataUtils.java +++ b/game-app/game-core/src/main/java/games/strategy/engine/framework/GameDataUtils.java @@ -2,11 +2,11 @@ import games.strategy.engine.data.GameData; import games.strategy.engine.data.GameObjectOutputStream; -import games.strategy.engine.history.History; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Optional; +import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; import org.triplea.injection.Injections; import org.triplea.io.IoUtils; @@ -14,24 +14,10 @@ /** A collection of useful methods for working with instances of {@link GameData}. */ @Slf4j +@UtilityClass public final class GameDataUtils { - private GameDataUtils() {} - - /** - * Create a deep copy of GameData without history as it can get large. You should have the - * game data's write lock before calling this method - */ - public static Optional cloneGameDataWithoutHistory( - final GameData data, final boolean copyDelegates, final Version engineVersion) { - final History temp = data.getHistory(); - data.resetHistory(); - final Optional dataCopy = cloneGameData(data, copyDelegates, engineVersion); - data.setHistory(temp); - return dataCopy; - } - - public static Optional cloneGameData(final GameData data) { - return cloneGameData(data, false, Injections.getInstance().getEngineVersion()); + public static Optional cloneGameData(GameData data, GameDataManager.Options options) { + return cloneGameData(data, options, Injections.getInstance().getEngineVersion()); } /** @@ -39,8 +25,8 @@ public static Optional cloneGameData(final GameData data) { * before calling this method */ public static Optional cloneGameData( - final GameData data, final boolean copyDelegates, final Version engineVersion) { - final byte[] bytes = gameDataToBytes(data, copyDelegates, engineVersion).orElse(null); + GameData data, GameDataManager.Options options, Version engineVersion) { + final byte[] bytes = gameDataToBytes(data, options, engineVersion).orElse(null); if (bytes != null) { return createGameDataFromBytes(bytes, engineVersion); } @@ -48,11 +34,11 @@ public static Optional cloneGameData( } public static Optional gameDataToBytes( - GameData data, boolean copyDelegates, Version engineVersion) { + GameData data, GameDataManager.Options options, Version engineVersion) { try { return Optional.of( IoUtils.writeToMemory( - os -> GameDataManager.saveGameUncompressed(os, data, copyDelegates, engineVersion))); + os -> GameDataManager.saveGameUncompressed(os, data, options, engineVersion))); } catch (final IOException e) { log.error("Failed to clone game data", e); return Optional.empty(); diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/AbstractProAi.java b/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/AbstractProAi.java index ec32ddc4d7c..4b34bef4dd2 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/AbstractProAi.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/AbstractProAi.java @@ -7,6 +7,7 @@ import games.strategy.engine.data.Territory; import games.strategy.engine.data.Unit; import games.strategy.engine.delegate.IDelegateBridge; +import games.strategy.engine.framework.GameDataManager; import games.strategy.engine.framework.GameDataUtils; import games.strategy.triplea.Properties; import games.strategy.triplea.ai.AbstractBuiltInAi; @@ -180,8 +181,10 @@ protected void purchase( try { data.acquireWriteLock(); dataCopy = - GameDataUtils.cloneGameDataWithoutHistory( - data, true, Injections.getInstance().getEngineVersion()) + GameDataUtils.cloneGameData( + data, + GameDataManager.Options.builder().withDelegates(true).build(), + Injections.getInstance().getEngineVersion()) .orElse(null); if (dataCopy == null) { return; diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/BattleCalculator.java b/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/BattleCalculator.java index 57910d5e2b5..b2ff52820e6 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/BattleCalculator.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/BattleCalculator.java @@ -9,6 +9,7 @@ import games.strategy.engine.data.TerritoryEffect; import games.strategy.engine.data.Unit; import games.strategy.engine.data.changefactory.ChangeFactory; +import games.strategy.engine.framework.GameDataManager; import games.strategy.engine.framework.GameDataUtils; import games.strategy.triplea.delegate.GameDelegateBridge; import games.strategy.triplea.delegate.battle.BattleResults; @@ -38,7 +39,10 @@ private BattleCalculator(@Nullable GameData data) { } BattleCalculator(GameData data, Version engineVersion) { - this(GameDataUtils.cloneGameData(data, false, engineVersion).orElse(null)); + this( + GameDataUtils.cloneGameData( + data, GameDataManager.Options.forBattleCalculator(), engineVersion) + .orElse(null)); } BattleCalculator(byte[] data, Version engineVersion) { diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/ConcurrentBattleCalculator.java b/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/ConcurrentBattleCalculator.java index 6a6b5639b58..c42c830353c 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/ConcurrentBattleCalculator.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/ConcurrentBattleCalculator.java @@ -6,6 +6,7 @@ import games.strategy.engine.data.Territory; import games.strategy.engine.data.TerritoryEffect; import games.strategy.engine.data.Unit; +import games.strategy.engine.framework.GameDataManager; import games.strategy.engine.framework.GameDataUtils; import java.util.Collection; import java.util.List; @@ -127,7 +128,10 @@ private void createWorkers(final GameData data) { // Serialize the data, then release lock on it so game can continue (ie: we don't want to // lock on it while we copy it 16 times). data.acquireWriteLock(); - serializedData = GameDataUtils.gameDataToBytes(data, false, engineVersion).orElse(null); + serializedData = + GameDataUtils.gameDataToBytes( + data, GameDataManager.Options.forBattleCalculator(), engineVersion) + .orElse(null); if (serializedData == null) { return; } diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ui/TripleAFrame.java b/game-app/game-core/src/main/java/games/strategy/triplea/ui/TripleAFrame.java index c53998c551a..0d8d2f2b692 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ui/TripleAFrame.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ui/TripleAFrame.java @@ -1763,7 +1763,8 @@ private void showHistory() { try { // we want to use a clone of the data, so we can make changes to it as we walk up and down the // history - clonedGameData = GameDataUtils.cloneGameData(data).orElse(null); + final var cloneOptions = GameDataManager.Options.builder().withHistory(true).build(); + clonedGameData = GameDataUtils.cloneGameData(data, cloneOptions).orElse(null); if (clonedGameData == null) { return; } @@ -1878,7 +1879,9 @@ public void actionPerformed(final ActionEvent ae) { try (OutputStream fileOutputStream = Files.newOutputStream(f.get())) { final GameData datacopy = GameDataUtils.cloneGameData( - data, true, Injections.getInstance().getEngineVersion()) + data, + GameDataManager.Options.withEverything(), + Injections.getInstance().getEngineVersion()) .orElse(null); if (datacopy != null) { datacopy.getHistory().gotoNode(historyPanel.getCurrentPopupNode()); diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ui/menubar/ExportMenu.java b/game-app/game-core/src/main/java/games/strategy/triplea/ui/menubar/ExportMenu.java index 9cb2e0a6bdb..ac37f94589d 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ui/menubar/ExportMenu.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ui/menubar/ExportMenu.java @@ -7,6 +7,7 @@ import games.strategy.engine.data.Resource; import games.strategy.engine.data.UnitType; import games.strategy.engine.data.export.GameDataExporter; +import games.strategy.engine.framework.GameDataManager; import games.strategy.engine.framework.GameDataUtils; import games.strategy.engine.framework.system.SystemProperties; import games.strategy.engine.history.HistoryNode; @@ -167,7 +168,8 @@ private void createAndSaveStats(final boolean showPhaseStats) { try (PrintWriter writer = new PrintWriter(chooser.getSelectedFile(), StandardCharsets.UTF_8.toString())) { gameData.acquireReadLock(); - final GameData clone = GameDataUtils.cloneGameData(gameData).orElse(null); + final var cloneOptions = GameDataManager.Options.builder().withHistory(true).build(); + final GameData clone = GameDataUtils.cloneGameData(gameData, cloneOptions).orElse(null); if (clone == null) { return; } @@ -394,7 +396,8 @@ private void exportSetupCharts() { final GameData clonedGameData; gameData.acquireReadLock(); try { - clonedGameData = GameDataUtils.cloneGameData(gameData).orElse(null); + final var cloneOptions = GameDataManager.Options.builder().withHistory(true).build(); + clonedGameData = GameDataUtils.cloneGameData(gameData, cloneOptions).orElse(null); if (clonedGameData == null) { return; }