diff --git a/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java index 7192b53b3e9..be176d263d7 100644 --- a/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java +++ b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java @@ -3,9 +3,12 @@ package forge.view; import java.io.File; import java.io.FilenameFilter; import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import forge.LobbyPlayer; import forge.deck.DeckGroup; +import forge.game.*; import forge.properties.ForgeConstants; import forge.tournament.system.*; import forge.util.TextUtil; @@ -15,12 +18,6 @@ import org.apache.commons.lang3.time.StopWatch; import forge.deck.Deck; import forge.deck.io.DeckSerializer; -import forge.game.Game; -import forge.game.GameLogEntry; -import forge.game.GameRules; -import forge.game.GameType; -import forge.game.GameLogEntryType; -import forge.game.Match; import forge.game.player.RegisteredPlayer; import forge.model.FModel; import forge.player.GamePlayerUtil; @@ -161,14 +158,38 @@ public class SimulateMatch { System.out.println("\tq - Quiet flag. Output just the game result, not the entire game log."); } + + private static void simulateSingleMatch(Match mc, int iGame, boolean outputGamelog) { StopWatch sw = new StopWatch(); sw.start(); Game g1 = mc.createGame(); // will run match in the same thread - mc.startGame(g1); - sw.stop(); + + long startTime = System.currentTimeMillis(); + try { + TimeLimitedCodeBlock.runWithTimeout(new Runnable() { + @Override + public void run() { + mc.startGame(g1); + sw.stop(); + } + }, 120, TimeUnit.SECONDS); + } + catch (TimeoutException e) { + System.out.println("Stopping slow match as draw"); + g1.setGameOver(GameEndReason.Draw); + sw.stop(); + }catch (Exception e){ + e.printStackTrace(); + g1.setGameOver(GameEndReason.Draw); + sw.stop(); + }catch(StackOverflowError e){ + g1.setGameOver(GameEndReason.Draw); + sw.stop(); + } + List log; if (outputGamelog) { diff --git a/forge-gui-desktop/src/main/java/forge/view/TimeLimitedCodeBlock.java b/forge-gui-desktop/src/main/java/forge/view/TimeLimitedCodeBlock.java new file mode 100644 index 00000000000..15a3f5a188a --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/view/TimeLimitedCodeBlock.java @@ -0,0 +1,46 @@ +package forge.view; + +import java.util.concurrent.*; + +/** + * Created by maustin on 08/02/2018. + */ +public class TimeLimitedCodeBlock { + + public static void runWithTimeout(final Runnable runnable, long timeout, TimeUnit timeUnit) throws Exception { + runWithTimeout(new Callable() { + @Override + public Object call() throws Exception { + runnable.run(); + return null; + } + }, timeout, timeUnit); + } + + public static T runWithTimeout(Callable callable, long timeout, TimeUnit timeUnit) throws Exception { + final ExecutorService executor = Executors.newSingleThreadExecutor(); + final Future future = executor.submit(callable); + executor.shutdown(); // This does not cancel the already-scheduled task. + try { + return future.get(timeout, timeUnit); + } + catch (TimeoutException e) { + //remove this if you do not want to cancel the job in progress + //or set the argument to 'false' if you do not want to interrupt the thread + future.cancel(true); + throw e; + } + catch (ExecutionException e) { + //unwrap the root cause + Throwable t = e.getCause(); + if (t instanceof Error) { + throw (Error) t; + } else if (t instanceof Exception) { + throw (Exception) t; + } else { + throw new IllegalStateException(t); + } + } + } + +}