diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index be5776bea15..403fc27d796 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -60,6 +60,8 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.Serializable; import java.util.*; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; import java.util.prefs.Preferences; import mage.client.components.MageComponents; @@ -79,6 +81,7 @@ public class GamePanel extends javax.swing.JPanel { private UUID gameId; private UUID playerId; private Session session; + private ReplayTask replayTask; private CombatDialog combat; private PickNumberDialog pickNumber; private JLayeredPane jLayeredPane; @@ -572,8 +575,10 @@ public class GamePanel extends javax.swing.JPanel { stack = new mage.client.cards.Cards(); pnlReplay = new javax.swing.JPanel(); btnStopReplay = new javax.swing.JButton(); - btnPreviousPlay = new javax.swing.JButton(); btnNextPlay = new javax.swing.JButton(); + btnPlay = new javax.swing.JButton(); + btnSkipForward = new javax.swing.JButton(); + btnPreviousPlay = new javax.swing.JButton(); pnlBattlefield = new javax.swing.JPanel(); hand = new mage.client.cards.Cards(true); gameChatPanel = new mage.client.chat.ChatPanel(); @@ -664,44 +669,63 @@ public class GamePanel extends javax.swing.JPanel { stack.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight + 25)); - btnStopReplay.setText("Stop"); + btnStopReplay.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/control_stop.png"))); // NOI18N btnStopReplay.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnStopReplayActionPerformed(evt); } }); - btnPreviousPlay.setText("Previous"); - btnPreviousPlay.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - btnPreviousPlayActionPerformed(evt); - } - }); - - btnNextPlay.setText("Next"); + btnNextPlay.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/control_stop_right.png"))); // NOI18N btnNextPlay.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnNextPlayActionPerformed(evt); } }); + btnPlay.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/control_right.png"))); // NOI18N + btnPlay.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnPlayActionPerformed(evt); + } + }); + + btnSkipForward.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/control_double_stop_right.png"))); // NOI18N + btnSkipForward.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnSkipForwardActionPerformed(evt); + } + }); + + btnPreviousPlay.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/control_stop_left.png"))); // NOI18N + btnPreviousPlay.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnPreviousPlayActionPerformed(evt); + } + }); + javax.swing.GroupLayout pnlReplayLayout = new javax.swing.GroupLayout(pnlReplay); pnlReplay.setLayout(pnlReplayLayout); pnlReplayLayout.setHorizontalGroup( pnlReplayLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(pnlReplayLayout.createSequentialGroup() - .addComponent(btnStopReplay) + .addComponent(btnPreviousPlay, javax.swing.GroupLayout.PREFERRED_SIZE, 41, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnPreviousPlay) + .addComponent(btnPlay, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(btnStopReplay, javax.swing.GroupLayout.PREFERRED_SIZE, 38, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnNextPlay)) + .addComponent(btnNextPlay, javax.swing.GroupLayout.PREFERRED_SIZE, 36, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnSkipForward, javax.swing.GroupLayout.PREFERRED_SIZE, 39, javax.swing.GroupLayout.PREFERRED_SIZE)) ); pnlReplayLayout.setVerticalGroup( pnlReplayLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(pnlReplayLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(btnStopReplay) - .addComponent(btnPreviousPlay) - .addComponent(btnNextPlay)) + .addComponent(btnSkipForward, 0, 0, Short.MAX_VALUE) + .addComponent(btnNextPlay, 0, 0, Short.MAX_VALUE) + .addComponent(btnStopReplay, 0, 0, Short.MAX_VALUE) + .addComponent(btnPlay, 0, 0, Short.MAX_VALUE) + .addComponent(btnPreviousPlay, javax.swing.GroupLayout.PREFERRED_SIZE, 31, Short.MAX_VALUE) ); javax.swing.GroupLayout pnlGameInfoLayout = new javax.swing.GroupLayout(pnlGameInfo); @@ -919,7 +943,10 @@ public class GamePanel extends javax.swing.JPanel { }//GEN-LAST:event_btnStopWatchingActionPerformed private void btnStopReplayActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnStopReplayActionPerformed - if (modalQuestion("Are you sure you want to stop replay?", "Stop replay") == JOptionPane.YES_OPTION) { + if (replayTask != null && !replayTask.isDone()) { + replayTask.cancel(true); + } + else if (modalQuestion("Are you sure you want to stop replay?", "Stop replay") == JOptionPane.YES_OPTION) { session.stopReplay(gameId); } }//GEN-LAST:event_btnStopReplayActionPerformed @@ -931,7 +958,18 @@ public class GamePanel extends javax.swing.JPanel { private void btnPreviousPlayActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnPreviousPlayActionPerformed session.previousPlay(gameId); }//GEN-LAST:event_btnPreviousPlayActionPerformed + + private void btnPlayActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnPlayActionPerformed + if (replayTask == null || replayTask.isDone()) { + replayTask = new ReplayTask(session, gameId); + replayTask.execute(); + } + }//GEN-LAST:event_btnPlayActionPerformed + private void btnSkipForwardActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSkipForwardActionPerformed + session.skipForward(gameId, 10); + }//GEN-LAST:event_btnSkipForwardActionPerformed + private Dimension getHandCardDimension() { Preferences pref = MageFrame.getPreferences(); String useBigCards = pref.get(PreferencesDialog.KEY_HAND_USE_BIG_CARDS, "false"); @@ -966,6 +1004,7 @@ public class GamePanel extends javax.swing.JPanel { setOpaque(false); jPanel.setOpaque(false); jScrollPane1.setOpaque(false); + pnlReplay.setOpaque(false); jPanel.setBorder(emptyBorder); jScrollPane1.setBorder(emptyBorder); @@ -1000,7 +1039,9 @@ public class GamePanel extends javax.swing.JPanel { private javax.swing.JButton btnConcede; private javax.swing.JButton btnSwitchHands; private javax.swing.JButton btnNextPlay; + private javax.swing.JButton btnPlay; private javax.swing.JButton btnPreviousPlay; + private javax.swing.JButton btnSkipForward; private javax.swing.JButton btnStopReplay; private javax.swing.JButton btnStopWatching; private mage.client.chat.ChatPanel gameChatPanel; @@ -1045,3 +1086,37 @@ public class GamePanel extends javax.swing.JPanel { private JButton prevStep; private JLabel endButtonTip; } + +class ReplayTask extends SwingWorker> { + + private Session session; + private UUID gameId; + + private final static Logger logger = Logger.getLogger(ReplayTask.class); + + ReplayTask(Session session, UUID gameId) { + this.session = session; + this.gameId = gameId; + } + + @Override + protected Void doInBackground() throws Exception { + while (!isCancelled()) { + session.nextPlay(gameId); + Thread.sleep(1000); + } + return null; + } + + @Override + protected void done() { + try { + get(); + } catch (InterruptedException ex) { + logger.fatal("Update Matches Task error", ex); + } catch (ExecutionException ex) { + logger.fatal("Update Matches Task error", ex); + } catch (CancellationException ex) {} + } + +} diff --git a/Mage.Client/src/main/resources/buttons/control_double_stop_left.png b/Mage.Client/src/main/resources/buttons/control_double_stop_left.png new file mode 100644 index 00000000000..181bd6f7c39 Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/control_double_stop_left.png differ diff --git a/Mage.Client/src/main/resources/buttons/control_double_stop_right.png b/Mage.Client/src/main/resources/buttons/control_double_stop_right.png new file mode 100644 index 00000000000..98c2edc028f Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/control_double_stop_right.png differ diff --git a/Mage.Client/src/main/resources/buttons/control_right.png b/Mage.Client/src/main/resources/buttons/control_right.png new file mode 100644 index 00000000000..fd06590db6b Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/control_right.png differ diff --git a/Mage.Client/src/main/resources/buttons/control_stop.png b/Mage.Client/src/main/resources/buttons/control_stop.png new file mode 100644 index 00000000000..34cc507f15b Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/control_stop.png differ diff --git a/Mage.Client/src/main/resources/buttons/control_stop_left.png b/Mage.Client/src/main/resources/buttons/control_stop_left.png new file mode 100644 index 00000000000..454488b62de Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/control_stop_left.png differ diff --git a/Mage.Client/src/main/resources/buttons/control_stop_right.png b/Mage.Client/src/main/resources/buttons/control_stop_right.png new file mode 100644 index 00000000000..7dced0bb6db Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/control_stop_right.png differ diff --git a/Mage.Common/src/mage/interfaces/MageServer.java b/Mage.Common/src/mage/interfaces/MageServer.java index 9d61d2077df..8df091e6943 100644 --- a/Mage.Common/src/mage/interfaces/MageServer.java +++ b/Mage.Common/src/mage/interfaces/MageServer.java @@ -113,6 +113,7 @@ public interface MageServer { public void stopReplay(UUID gameId, String sessionId) throws MageException; public void nextPlay(UUID gameId, String sessionId) throws MageException; public void previousPlay(UUID gameId, String sessionId) throws MageException; + public void skipForward(UUID gameId, String sessionId, int moves) throws MageException; //test methods public void cheat(UUID gameId, String sessionId, UUID playerId, DeckCardLists deckList) throws MageException; diff --git a/Mage.Common/src/mage/remote/Session.java b/Mage.Common/src/mage/remote/Session.java index 5c3169dea64..30a43b3186e 100644 --- a/Mage.Common/src/mage/remote/Session.java +++ b/Mage.Common/src/mage/remote/Session.java @@ -843,7 +843,21 @@ public class Session { return false; } - public boolean cheat(UUID gameId, UUID playerId, DeckCardLists deckList) { + public boolean skipForward(UUID gameId, int moves) { + try { + if (isConnected()) { + server.skipForward(gameId, sessionId, moves); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + public boolean cheat(UUID gameId, UUID playerId, DeckCardLists deckList) { try { if (isConnected()) { server.cheat(gameId, sessionId, playerId, deckList); diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java index 121794f44a9..f2f141dfc18 100644 --- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java @@ -858,6 +858,26 @@ public class MageServerImpl implements MageServer { } @Override + public void skipForward(final UUID gameId, final String sessionId, final int moves) throws MageException { + if (SessionManager.getInstance().isValidSession(sessionId)) { + try { + callExecutor.execute( + new Runnable() { + @Override + public void run() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + ReplayManager.getInstance().skipForward(gameId, userId, moves); + } + } + ); + } + catch (Exception ex) { + handleException(ex); + } + } + } + + @Override public ServerState getServerState() throws MageException { try { return new ServerState( diff --git a/Mage.Server/src/main/java/mage/server/game/ReplayManager.java b/Mage.Server/src/main/java/mage/server/game/ReplayManager.java index a181936ecf6..52de54ea7bc 100644 --- a/Mage.Server/src/main/java/mage/server/game/ReplayManager.java +++ b/Mage.Server/src/main/java/mage/server/game/ReplayManager.java @@ -68,5 +68,10 @@ public class ReplayManager { public void previousPlay(UUID gameId, UUID userId) { replaySessions.get(gameId.toString() + userId.toString()).previous(); } + + public void skipForward(UUID gameId, UUID userId, int moves) { + replaySessions.get(gameId.toString() + userId.toString()).next(moves); + } + } diff --git a/Mage.Server/src/main/java/mage/server/game/ReplaySession.java b/Mage.Server/src/main/java/mage/server/game/ReplaySession.java index ed26b7c1c05..30c3e7fb0e3 100644 --- a/Mage.Server/src/main/java/mage/server/game/ReplaySession.java +++ b/Mage.Server/src/main/java/mage/server/game/ReplaySession.java @@ -68,7 +68,14 @@ public class ReplaySession implements GameCallback { updateGame(replay.next(), replay.getGame()); } - public synchronized void previous() { + public synchronized void next(int moves) { + for (int i = 0; i < moves; i++) { + replay.next(); + } + updateGame(replay.next(), replay.getGame()); + } + + public synchronized void previous() { updateGame(replay.previous(), replay.getGame()); }