From c4a3a496aaa07cb69b5ab2032e01418f8d92a2ea Mon Sep 17 00:00:00 2001 From: BetaSteward Date: Wed, 15 Jun 2011 23:24:03 -0400 Subject: [PATCH] moved Session to Mage.Common --- .../src/main/java/mage/client/MageFrame.java | 67 ++++++- .../src/main/java/mage/client/cards/Card.java | 6 +- .../main/java/mage/client/chat/ChatPanel.java | 6 +- .../mage/client/dialog/ConnectDialog.java | 4 +- .../mage/client/dialog/JoinTableDialog.java | 2 +- .../mage/client/dialog/NewTableDialog.java | 2 +- .../client/dialog/TableWaitingDialog.java | 2 +- .../java/mage/client/draft/DraftPanel.java | 4 +- .../java/mage/client/game/AbilityPicker.java | 2 +- .../java/mage/client/game/FeedbackPanel.java | 2 +- .../main/java/mage/client/game/GamePanel.java | 8 +- .../java/mage/client/game/PlayerPanel.java | 2 +- .../java/mage/client/game/PlayerPanelExt.java | 2 +- .../plugins/adapters/MageActionCallback.java | 8 +- .../{Client.java => CallbackClientImpl.java} | 64 +++---- .../mage/client/table/TablePlayerPanel.java | 2 +- .../java/mage/client/table/TablesPanel.java | 4 +- .../client/table/TournamentPlayerPanel.java | 2 +- .../client/tournament/TournamentPanel.java | 9 +- .../client/util/DefaultActionCallback.java | 2 +- .../src/mage/interfaces/MageClient.java | 48 +++++ .../mage/{utils => remote}/Connection.java | 39 +++- .../src/mage}/remote/MageRemoteException.java | 2 +- .../src/mage}/remote/Session.java | 181 ++++++------------ .../plugins/mage-tournament-booster-draft.jar | Bin 5721 -> 5725 bytes .../plugins/mage-tournament-sealed.jar | Bin 5049 -> 5053 bytes 26 files changed, 268 insertions(+), 202 deletions(-) rename Mage.Client/src/main/java/mage/client/remote/{Client.java => CallbackClientImpl.java} (85%) create mode 100644 Mage.Common/src/mage/interfaces/MageClient.java rename Mage.Common/src/mage/{utils => remote}/Connection.java (79%) rename {Mage.Client/src/main/java/mage/client => Mage.Common/src/mage}/remote/MageRemoteException.java (98%) rename {Mage.Client/src/main/java/mage/client => Mage.Common/src/mage}/remote/Session.java (83%) diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 4f39cf3f92c..2d9bbd5fdb8 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -71,37 +71,50 @@ import java.util.List; import java.util.prefs.Preferences; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; +import mage.client.chat.ChatPanel; import mage.client.components.MageUI; import mage.client.deckeditor.DeckEditorPane; import mage.client.draft.DraftPane; +import mage.client.draft.DraftPanel; import mage.client.game.GamePane; -import mage.client.remote.Session; +import mage.client.game.GamePanel; +import mage.client.remote.CallbackClientImpl; import mage.client.table.TablesPane; import mage.client.tournament.TournamentPane; +import mage.client.tournament.TournamentPanel; import mage.game.match.MatchOptions; +import mage.interfaces.MageClient; +import mage.interfaces.callback.CallbackClient; +import mage.interfaces.callback.ClientCallback; +import mage.remote.Connection; +import mage.remote.Connection.ProxyType; +import mage.remote.Session; import mage.utils.MageVersion; import mage.sets.Sets; -import mage.utils.Connection; -import mage.utils.Connection.ProxyType; import mage.view.TableView; import org.apache.log4j.Logger; /** * @author BetaSteward_at_googlemail.com */ -public class MageFrame extends javax.swing.JFrame { +public class MageFrame extends javax.swing.JFrame implements MageClient { private final static Logger logger = Logger.getLogger(MageFrame.class); private static Session session; private ConnectDialog connectDialog; + private static CallbackClient callbackClient; private static Preferences prefs = Preferences.userNodeForPackage(MageFrame.class); private JLabel title; private Rectangle titleRectangle; private final static MageVersion version = new MageVersion(0, 7, 4, "beta"); private UUID clientId; private static MagePane activeFrame; - + + private static Map chats = new HashMap(); + private static Map games = new HashMap(); + private static Map drafts = new HashMap(); + private static Map tournaments = new HashMap(); private static MageUI ui = new MageUI(); /** @@ -119,6 +132,7 @@ public class MageFrame extends javax.swing.JFrame { return prefs; } + @Override public MageVersion getVersion() { return version; } @@ -156,6 +170,7 @@ public class MageFrame extends javax.swing.JFrame { this.setExtendedState(JFrame.MAXIMIZED_BOTH); session = new Session(this); + callbackClient = new CallbackClientImpl(this); connectDialog = new ConnectDialog(); desktopPane.add(connectDialog, JLayeredPane.POPUP_LAYER); ui.addComponent(MageComponents.DESKTOP_PANE, desktopPane); @@ -739,7 +754,7 @@ public class MageFrame extends javax.swing.JFrame { private void btnConnectActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnConnectActionPerformed if (session.isConnected()) { if (JOptionPane.showConfirmDialog(this, "Are you sure you want to disconnect?", "Confirm disconnect", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - session.disconnect(); + session.disconnect(false); showMessage("You have disconnected"); } } else { @@ -754,7 +769,7 @@ public class MageFrame extends javax.swing.JFrame { }//GEN-LAST:event_btnAboutActionPerformed public void exitApp() { - session.disconnect(); + session.disconnect(false); Plugins.getInstance().shutdown(); dispose(); System.exit(0); @@ -873,10 +888,40 @@ public class MageFrame extends javax.swing.JFrame { return ui; } + public static ChatPanel getChat(UUID chatId) { + return chats.get(chatId); + } + + public static void addChat(UUID chatId, ChatPanel chatPanel) { + chats.put(chatId, chatPanel); + } + + public static GamePanel getGame(UUID gameId) { + return games.get(gameId); + } + + public static void addGame(UUID gameId, GamePanel gamePanel) { + games.put(gameId, gamePanel); + } + + public static DraftPanel getDraft(UUID draftId) { + return drafts.get(draftId); + } + + public static void addDraft(UUID draftId, DraftPanel draftPanel) { + drafts.put(draftId, draftPanel); + } + + public static void addTournament(UUID tournamentId, TournamentPanel tournament) { + tournaments.put(tournamentId, tournament); + } + + @Override public UUID getId() { return clientId; } + @Override public void connected(final String message) { if (SwingUtilities.isEventDispatchThread()) { setStatusText(message); @@ -893,6 +938,7 @@ public class MageFrame extends javax.swing.JFrame { } } + @Override public void disconnected() { if (SwingUtilities.isEventDispatchThread()) { setStatusText("Not connected"); @@ -913,6 +959,7 @@ public class MageFrame extends javax.swing.JFrame { } } + @Override public void showMessage(final String message) { if (SwingUtilities.isEventDispatchThread()) { JOptionPane.showMessageDialog(desktopPane, message); @@ -927,6 +974,7 @@ public class MageFrame extends javax.swing.JFrame { } } + @Override public void showError(final String message) { if (SwingUtilities.isEventDispatchThread()) { JOptionPane.showMessageDialog(desktopPane, message, "Error", JOptionPane.ERROR_MESSAGE); @@ -940,6 +988,11 @@ public class MageFrame extends javax.swing.JFrame { }); } } + + @Override + public void processCallback(ClientCallback callback) { + callbackClient.processCallback(callback); + } } diff --git a/Mage.Client/src/main/java/mage/client/cards/Card.java b/Mage.Client/src/main/java/mage/client/cards/Card.java index c54e1058959..96b2e2fdb0d 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Card.java +++ b/Mage.Client/src/main/java/mage/client/cards/Card.java @@ -80,7 +80,7 @@ import mage.cards.MagePermanent; import mage.cards.TextPopup; import mage.client.MageFrame; import mage.client.game.PlayAreaPanel; -import mage.client.remote.Session; +import mage.remote.Session; import mage.client.util.Config; import mage.client.util.DefaultActionCallback; import mage.client.util.ImageHelper; @@ -382,13 +382,13 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis List targets = card.getTargets(); if (targets != null) { for (UUID uuid : targets) { - PlayAreaPanel p = MageFrame.getSession().getGame(gameId).getPlayers().get(uuid); + PlayAreaPanel p = MageFrame.getGame(gameId).getPlayers().get(uuid); if (p != null) { Point target = p.getLocationOnScreen(); Point me = this.getLocationOnScreen(); ArrowBuilder.addArrow((int)me.getX() + 35, (int)me.getY(), (int)target.getX() + 40, (int)target.getY() - 40, Color.red); } else { - for (PlayAreaPanel pa : MageFrame.getSession().getGame(gameId).getPlayers().values()) { + for (PlayAreaPanel pa : MageFrame.getGame(gameId).getPlayers().values()) { MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); if (permanent != null) { Point target = permanent.getLocationOnScreen(); diff --git a/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java b/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java index bb4fec70866..a2beed58220 100644 --- a/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java +++ b/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java @@ -40,7 +40,7 @@ import java.util.*; import java.util.List; import mage.client.MageFrame; import mage.client.components.ColorPane; -import mage.client.remote.Session; +import mage.remote.Session; import mage.view.ChatMessage.MessageColor; import javax.swing.border.EmptyBorder; @@ -130,7 +130,9 @@ public class ChatPanel extends javax.swing.JPanel { public void connect(UUID chatId) { session = MageFrame.getSession(); this.chatId = chatId; - session.joinChat(chatId, this); + if (session.joinChat(chatId)) { + MageFrame.addChat(chatId, this); + } } public void disconnect() { diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java index a56c37f4131..98287f80b40 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java @@ -50,8 +50,8 @@ import javax.swing.SwingWorker; import mage.client.MageFrame; import mage.client.util.Config; -import mage.utils.Connection; -import mage.utils.Connection.ProxyType; +import mage.remote.Connection; +import mage.remote.Connection.ProxyType; import org.apache.log4j.Logger; diff --git a/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java index fb227155009..9837fbd4d26 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java @@ -38,7 +38,7 @@ package mage.client.dialog; import mage.client.*; import java.util.UUID; import javax.swing.JOptionPane; -import mage.client.remote.Session; +import mage.remote.Session; import mage.sets.Sets; import org.apache.log4j.Logger; diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index 020c308b2f9..4ecc346a460 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -46,7 +46,7 @@ import javax.swing.SpinnerNumberModel; import mage.Constants.MultiplayerAttackOption; import mage.Constants.RangeOfInfluence; import mage.client.components.MageComponents; -import mage.client.remote.Session; +import mage.remote.Session; import mage.client.table.TablePlayerPanel; import mage.client.util.Event; import mage.client.util.Listener; diff --git a/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java index 7a473d4e577..2e9a084e31f 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java @@ -42,7 +42,7 @@ import java.util.concurrent.ExecutionException; import javax.swing.SwingWorker; import javax.swing.table.AbstractTableModel; import mage.client.components.MageComponents; -import mage.client.remote.Session; +import mage.remote.Session; import mage.view.SeatView; import mage.view.TableView; import org.apache.log4j.Logger; diff --git a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java index 2525de73f8e..ee4351dc6a6 100644 --- a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java +++ b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java @@ -41,7 +41,7 @@ import java.util.UUID; import javax.swing.Timer; import mage.client.MageFrame; import mage.client.constants.Constants.SortBy; -import mage.client.remote.Session; +import mage.remote.Session; import mage.client.util.Event; import mage.client.util.Listener; import mage.view.DraftPickView; @@ -81,7 +81,7 @@ public class DraftPanel extends javax.swing.JPanel { public synchronized void showDraft(UUID draftId) { this.draftId = draftId; session = MageFrame.getSession(); - session.addDraft(draftId, this); + MageFrame.addDraft(draftId, this); if (!session.joinDraft(draftId)) hideDraft(); } diff --git a/Mage.Client/src/main/java/mage/client/game/AbilityPicker.java b/Mage.Client/src/main/java/mage/client/game/AbilityPicker.java index b9d027a6eec..b521cb0c92d 100644 --- a/Mage.Client/src/main/java/mage/client/game/AbilityPicker.java +++ b/Mage.Client/src/main/java/mage/client/game/AbilityPicker.java @@ -38,7 +38,7 @@ import javax.swing.JPopupMenu; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import mage.client.MageFrame; -import mage.client.remote.Session; +import mage.remote.Session; import mage.client.util.gui.GuiDisplayUtil; import mage.view.AbilityPickerView; diff --git a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java index 7b3f92b8736..e65afe4dd22 100644 --- a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java @@ -46,7 +46,7 @@ import javax.swing.*; import mage.client.MageFrame; import mage.client.components.MageTextArea; import mage.client.components.arcane.GlowText; -import mage.client.remote.Session; +import mage.remote.Session; import org.apache.log4j.Logger; 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 7dddf7cf652..c7864044c26 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -58,7 +58,7 @@ import mage.client.dialog.PickNumberDialog; import mage.client.dialog.ShowCardsDialog; import mage.client.game.FeedbackPanel.FeedbackMode; import mage.client.plugins.impl.Plugins; -import mage.client.remote.Session; +import mage.remote.Session; import mage.client.util.Config; import mage.client.util.GameManager; import mage.client.util.PhaseManager; @@ -161,7 +161,7 @@ public class GamePanel extends javax.swing.JPanel { this.gameId = gameId; this.playerId = playerId; session = MageFrame.getSession(); - session.addGame(gameId, this); + MageFrame.addGame(gameId, this); this.feedbackPanel.init(gameId); this.feedbackPanel.clear(); this.abilityPicker.init(session, gameId); @@ -178,7 +178,7 @@ public class GamePanel extends javax.swing.JPanel { this.gameId = gameId; this.playerId = null; session = MageFrame.getSession(); - session.addGame(gameId, this); + MageFrame.addGame(gameId, this); this.feedbackPanel.init(gameId); this.feedbackPanel.clear(); this.btnConcede.setVisible(false); @@ -194,7 +194,7 @@ public class GamePanel extends javax.swing.JPanel { this.gameId = gameId; this.playerId = null; session = MageFrame.getSession(); - session.addGame(gameId, this); + MageFrame.addGame(gameId, this); this.feedbackPanel.clear(); this.btnConcede.setVisible(false); this.btnStopWatching.setVisible(false); diff --git a/Mage.Client/src/main/java/mage/client/game/PlayerPanel.java b/Mage.Client/src/main/java/mage/client/game/PlayerPanel.java index eef67591d1d..5b0be1560a9 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayerPanel.java @@ -39,7 +39,7 @@ import java.util.UUID; import mage.client.MageFrame; import mage.client.cards.BigCard; import mage.client.dialog.ShowCardsDialog; -import mage.client.remote.Session; +import mage.remote.Session; import mage.client.util.Config; import mage.view.PlayerView; diff --git a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java index 3db87b15540..1f04aa03104 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java @@ -40,7 +40,7 @@ import mage.client.components.HoverButton; import mage.client.components.MageRoundPane; import mage.client.components.arcane.ManaSymbols; import mage.client.dialog.ShowCardsDialog; -import mage.client.remote.Session; +import mage.remote.Session; import mage.client.util.Command; import mage.client.util.Config; import mage.client.util.ImageHelper; diff --git a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java index 100bd8c1fb9..9a7798856ef 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java +++ b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java @@ -18,7 +18,7 @@ import mage.client.cards.BigCard; import mage.client.components.MageComponents; import mage.client.game.PlayAreaPanel; import mage.client.plugins.impl.Plugins; -import mage.client.remote.Session; +import mage.remote.Session; import mage.client.util.DefaultActionCallback; import mage.client.util.ImageHelper; import mage.client.util.gui.ArrowBuilder; @@ -82,13 +82,13 @@ public class MageActionCallback implements ActionCallback { for (UUID uuid : targets) { //System.out.println("Getting play area panel for uuid: " + uuid); - PlayAreaPanel p = session.getGame(data.gameId).getPlayers().get(uuid); + PlayAreaPanel p = MageFrame.getGame(data.gameId).getPlayers().get(uuid); if (p != null) { Point target = p.getLocationOnScreen(); target.translate(-parentPoint.x, -parentPoint.y); ArrowBuilder.addArrow((int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 40, (int) target.getY() - 40, Color.red); } else { - for (PlayAreaPanel pa : session.getGame(data.gameId).getPlayers().values()) { + for (PlayAreaPanel pa : MageFrame.getGame(data.gameId).getPlayers().values()) { MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); if (permanent != null) { Point target = permanent.getLocationOnScreen(); @@ -105,7 +105,7 @@ public class MageActionCallback implements ActionCallback { Point me = new Point(data.locationOnScreen); me.translate(-parentPoint.x, -parentPoint.y); UUID uuid = data.card.getParentId(); - for (PlayAreaPanel pa : session.getGame(data.gameId).getPlayers().values()) { + for (PlayAreaPanel pa : MageFrame.getGame(data.gameId).getPlayers().values()) { MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); if (permanent != null) { Point source = permanent.getLocationOnScreen(); diff --git a/Mage.Client/src/main/java/mage/client/remote/Client.java b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java similarity index 85% rename from Mage.Client/src/main/java/mage/client/remote/Client.java rename to Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java index 9831f67a3c6..5c2ec948877 100644 --- a/Mage.Client/src/main/java/mage/client/remote/Client.java +++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java @@ -28,9 +28,7 @@ package mage.client.remote; -import java.rmi.RemoteException; import java.util.UUID; -import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import mage.cards.decks.Deck; import mage.client.MageFrame; @@ -40,8 +38,10 @@ import mage.client.draft.DraftPanel; import mage.client.game.GamePanel; import mage.client.plugins.impl.Plugins; import mage.client.util.GameManager; +import mage.client.util.object.SaveObjectUtil; import mage.interfaces.callback.CallbackClient; import mage.interfaces.callback.ClientCallback; +import mage.utils.CompressUtil; import mage.view.AbilityPickerView; import mage.view.ChatMessage; import mage.view.DraftClientMessage; @@ -55,26 +55,26 @@ import org.apache.log4j.Logger; * * @author BetaSteward_at_googlemail.com */ -public class Client implements CallbackClient { +public class CallbackClientImpl implements CallbackClient { - private final static Logger logger = Logger.getLogger(Client.class); + private final static Logger logger = Logger.getLogger(CallbackClientImpl.class); private UUID clientId; private MageFrame frame; - private Session session; private int messageId = 0; - public Client(Session session, MageFrame frame) { + public CallbackClientImpl(MageFrame frame) { this.clientId = UUID.randomUUID(); this.frame = frame; - this.session = session; } @Override public synchronized void processCallback(final ClientCallback callback) { logger.info(callback.getMessageId() + " - " + callback.getMethod()); + SaveObjectUtil.saveObject(callback.getData(), callback.getMethod()); + callback.setData(CompressUtil.decompress(callback.getData())); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { @@ -101,7 +101,7 @@ public class Client implements CallbackClient { } else if (callback.getMethod().equals("chatMessage")) { ChatMessage message = (ChatMessage) callback.getData(); - ChatPanel panel = session.getChat(callback.getObjectId()); + ChatPanel panel = frame.getChat(callback.getObjectId()); if (panel != null) { if (message.isUserMessage() && panel.getConnectedChat() != null) { panel.getConnectedChat().receiveMessage(message.getUsername(), message.getMessage(), message.getTime(), ChatMessage.MessageColor.BLACK); @@ -111,31 +111,31 @@ public class Client implements CallbackClient { } } else if (callback.getMethod().equals("replayInit")) { - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) panel.init((GameView) callback.getData()); } else if (callback.getMethod().equals("replayDone")) { - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) { panel.modalMessage((String) callback.getData()); panel.hideGame(); } } else if (callback.getMethod().equals("replayUpdate")) { - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) panel.updateGame((GameView) callback.getData()); } else if (callback.getMethod().equals("gameInit")) { - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) { panel.init((GameView) callback.getData()); - session.ack("gameInit"); + } } else if (callback.getMethod().equals("gameOver")) { - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) { panel.modalMessage((String) callback.getData()); panel.hideGame(); @@ -143,53 +143,53 @@ public class Client implements CallbackClient { } else if (callback.getMethod().equals("gameAsk")) { GameClientMessage message = (GameClientMessage) callback.getData(); - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) panel.ask(message.getMessage(), message.getGameView()); } else if (callback.getMethod().equals("gameTarget")) { GameClientMessage message = (GameClientMessage) callback.getData(); - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) panel.pickTarget(message.getMessage(), message.getCardsView(), message.getGameView(), message.getTargets(), message.isFlag(), message.getOptions()); } else if (callback.getMethod().equals("gameSelect")) { GameClientMessage message = (GameClientMessage) callback.getData(); - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) panel.select(message.getMessage(), message.getGameView()); } else if (callback.getMethod().equals("gameChooseAbility")) { - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) panel.pickAbility((AbilityPickerView) callback.getData()); } else if (callback.getMethod().equals("gameChoose")) { GameClientMessage message = (GameClientMessage) callback.getData(); - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) panel.getChoice(message.getMessage(), message.getStrings()); } else if (callback.getMethod().equals("gamePlayMana")) { GameClientMessage message = (GameClientMessage) callback.getData(); - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) panel.playMana(message.getMessage(), message.getGameView()); } else if (callback.getMethod().equals("gamePlayXMana")) { GameClientMessage message = (GameClientMessage) callback.getData(); - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) panel.playXMana(message.getMessage(), message.getGameView()); } else if (callback.getMethod().equals("gameSelectAmount")) { GameClientMessage message = (GameClientMessage) callback.getData(); - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) panel.getAmount(message.getMin(), message.getMax(), message.getMessage()); } else if (callback.getMethod().equals("gameUpdate")) { - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) panel.updateGame((GameView) callback.getData()); } @@ -197,7 +197,7 @@ public class Client implements CallbackClient { if (callback.getMessageId() > messageId) { GameClientMessage message = (GameClientMessage) callback.getData(); - GamePanel panel = session.getGame(callback.getObjectId()); + GamePanel panel = frame.getGame(callback.getObjectId()); if (panel != null) panel.inform(message.getMessage(), message.getGameView()); } @@ -214,18 +214,18 @@ public class Client implements CallbackClient { construct(message.getDeck(), message.getTableId(), message.getTime()); } else if (callback.getMethod().equals("draftOver")) { - DraftPanel panel = session.getDraft(callback.getObjectId()); + DraftPanel panel = frame.getDraft(callback.getObjectId()); if (panel != null) panel.hideDraft(); } else if (callback.getMethod().equals("draftPick")) { DraftClientMessage message = (DraftClientMessage) callback.getData(); - DraftPanel panel = session.getDraft(callback.getObjectId()); + DraftPanel panel = frame.getDraft(callback.getObjectId()); if (panel != null) panel.loadBooster(message.getDraftPickView()); } else if (callback.getMethod().equals("draftUpdate")) { - DraftPanel panel = session.getDraft(callback.getObjectId()); + DraftPanel panel = frame.getDraft(callback.getObjectId()); if (panel != null) panel.updateDraft((DraftView) callback.getData()); } @@ -238,10 +238,10 @@ public class Client implements CallbackClient { } } else if (callback.getMethod().equals("draftInit")) { - session.ack("draftInit"); + } else if (callback.getMethod().equals("tournamentInit")) { - session.ack("tournamentInit"); + } messageId = callback.getMessageId(); } @@ -252,7 +252,7 @@ public class Client implements CallbackClient { }); } - public UUID getId() throws RemoteException { + public UUID getId() { return clientId; } @@ -320,9 +320,7 @@ public class Client implements CallbackClient { private void handleException(Exception ex) { logger.fatal("Client error\n", ex); - JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Unrecoverable client error. Disconnecting", "Error", JOptionPane.ERROR_MESSAGE); - session.disconnect(); - frame.disableButtons(); + frame.showError("Error: " + ex.getMessage()); } } diff --git a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java index 8515206a695..2eb3f7b6cb2 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java @@ -41,7 +41,7 @@ import java.util.UUID; import javax.swing.DefaultComboBoxModel; import mage.client.MageFrame; -import mage.client.remote.Session; +import mage.remote.Session; import mage.client.util.Config; import mage.client.util.Event; import mage.client.util.Listener; diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index 9565332724f..f73fe53feb1 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -43,8 +43,8 @@ import mage.client.dialog.JoinTableDialog; import mage.client.dialog.NewTableDialog; import mage.client.dialog.NewTournamentDialog; import mage.client.dialog.TableWaitingDialog; -import mage.client.remote.MageRemoteException; -import mage.client.remote.Session; +import mage.remote.MageRemoteException; +import mage.remote.Session; import mage.client.util.ButtonColumn; import mage.game.match.MatchOptions; import mage.sets.Sets; diff --git a/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java index 42fdffe8891..e4ace620af8 100644 --- a/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java @@ -38,7 +38,7 @@ import java.util.UUID; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import mage.client.MageFrame; -import mage.client.remote.Session; +import mage.remote.Session; /** * diff --git a/Mage.Client/src/main/java/mage/client/tournament/TournamentPanel.java b/Mage.Client/src/main/java/mage/client/tournament/TournamentPanel.java index b70548a65ab..f20f6212c01 100644 --- a/Mage.Client/src/main/java/mage/client/tournament/TournamentPanel.java +++ b/Mage.Client/src/main/java/mage/client/tournament/TournamentPanel.java @@ -36,20 +36,15 @@ package mage.client.tournament; import java.awt.*; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.Observable; -import java.util.Observer; import java.util.UUID; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import javax.swing.*; import javax.swing.table.AbstractTableModel; import mage.client.MageFrame; -import mage.client.remote.MageRemoteException; -import mage.client.remote.Session; +import mage.remote.Session; import mage.client.util.ButtonColumn; import mage.view.RoundView; import mage.view.TournamentGameView; @@ -104,7 +99,7 @@ public class TournamentPanel extends javax.swing.JPanel { public synchronized void showTournament(UUID tournamentId) { this.tournamentId = tournamentId; session = MageFrame.getSession(); - session.addTournament(tournamentId, this); + MageFrame.addTournament(tournamentId, this); UUID chatRoomId = session.getTournamentChatId(tournamentId); if (session.joinTournament(tournamentId) && chatRoomId != null) { this.chatPanel1.connect(chatRoomId); diff --git a/Mage.Client/src/main/java/mage/client/util/DefaultActionCallback.java b/Mage.Client/src/main/java/mage/client/util/DefaultActionCallback.java index 7a7421af71f..e783ad9f5bb 100644 --- a/Mage.Client/src/main/java/mage/client/util/DefaultActionCallback.java +++ b/Mage.Client/src/main/java/mage/client/util/DefaultActionCallback.java @@ -3,7 +3,7 @@ package mage.client.util; import java.awt.event.MouseEvent; import java.util.UUID; -import mage.client.remote.Session; +import mage.remote.Session; import mage.view.CardView; diff --git a/Mage.Common/src/mage/interfaces/MageClient.java b/Mage.Common/src/mage/interfaces/MageClient.java new file mode 100644 index 00000000000..67384f58be8 --- /dev/null +++ b/Mage.Common/src/mage/interfaces/MageClient.java @@ -0,0 +1,48 @@ +/* +* Copyright 2011 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.interfaces; + +import java.util.UUID; +import mage.interfaces.callback.CallbackClient; +import mage.utils.MageVersion; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public interface MageClient extends CallbackClient { + + public UUID getId(); + public MageVersion getVersion(); + public void connected(String message); + public void disconnected(); + public void showMessage(String message); + public void showError(String message); + +} diff --git a/Mage.Common/src/mage/utils/Connection.java b/Mage.Common/src/mage/remote/Connection.java similarity index 79% rename from Mage.Common/src/mage/utils/Connection.java rename to Mage.Common/src/mage/remote/Connection.java index e00d78740de..691b5923a2c 100644 --- a/Mage.Common/src/mage/utils/Connection.java +++ b/Mage.Common/src/mage/remote/Connection.java @@ -26,7 +26,11 @@ * or implied, of BetaSteward_at_googlemail.com. */ -package mage.utils; +package mage.remote; + +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import mage.interfaces.Server; /** * @@ -44,6 +48,39 @@ public class Connection { private String proxyUsername; private String proxyPassword; +// protected Server getServer() { +// Server server = null; +// try { +// Registry reg = LocateRegistry.getRegistry(host, port); +// server = (Server) reg.lookup("mage-server"); +// } +// catch (Exception ignored) {} +// return server; +// } + + @Override + public int hashCode() { + return (host + Integer.toString(port) + proxyType.toString()).hashCode(); + } + + @Override + public boolean equals(Object object) { + if (! (object instanceof Connection)) { + return false; + } + Connection otherConnection = (Connection) object; + return hashCode() == otherConnection.hashCode(); + } + + @Override + public String toString() { + return host + ":" + Integer.toString(port); + } + + public String getURI() { + return "bisocket://" + host + ":" + port; + } + public ProxyType getProxyType() { return proxyType; } diff --git a/Mage.Client/src/main/java/mage/client/remote/MageRemoteException.java b/Mage.Common/src/mage/remote/MageRemoteException.java similarity index 98% rename from Mage.Client/src/main/java/mage/client/remote/MageRemoteException.java rename to Mage.Common/src/mage/remote/MageRemoteException.java index 006252fd760..4f775473e08 100644 --- a/Mage.Client/src/main/java/mage/client/remote/MageRemoteException.java +++ b/Mage.Common/src/mage/remote/MageRemoteException.java @@ -26,7 +26,7 @@ * or implied, of BetaSteward_at_googlemail.com. */ -package mage.client.remote; +package mage.remote; /** * diff --git a/Mage.Client/src/main/java/mage/client/remote/Session.java b/Mage.Common/src/mage/remote/Session.java similarity index 83% rename from Mage.Client/src/main/java/mage/client/remote/Session.java rename to Mage.Common/src/mage/remote/Session.java index c3e5e22e2b6..454b15278c4 100644 --- a/Mage.Client/src/main/java/mage/client/remote/Session.java +++ b/Mage.Common/src/mage/remote/Session.java @@ -26,7 +26,7 @@ * or implied, of BetaSteward_at_googlemail.com. */ -package mage.client.remote; +package mage.remote; import java.net.Authenticator; import java.net.PasswordAuthentication; @@ -35,32 +35,23 @@ import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; -import javax.swing.JOptionPane; import mage.cards.decks.DeckCardLists; -import mage.client.MageFrame; -import mage.client.chat.ChatPanel; -import mage.client.components.MageUI; -import mage.client.draft.DraftPanel; -import mage.client.game.GamePanel; -import mage.client.tournament.TournamentPanel; -import mage.client.util.Config; import mage.game.GameException; import mage.MageException; +import mage.constants.Constants.SessionState; import mage.game.match.MatchOptions; import mage.game.tournament.TournamentOptions; +import mage.interfaces.MageClient; import mage.interfaces.Server; import mage.interfaces.ServerState; import mage.interfaces.callback.CallbackClientDaemon; -import mage.utils.Connection; import mage.view.DraftPickView; import mage.view.GameTypeView; import mage.view.TableView; @@ -79,17 +70,15 @@ public class Session { private UUID sessionId; private Server server; - private Client client; + private MageClient client; private String userName; - private MageFrame frame; private ServerState serverState; - private Map chats = new HashMap(); - private Map games = new HashMap(); - private Map drafts = new HashMap(); - private Map tournaments = new HashMap(); + private SessionState sessionState = SessionState.DISCONNECTED; private CallbackClientDaemon callbackDaemon; private ScheduledFuture future; - private MageUI ui = new MageUI(); + private Connection connection; + private boolean reconnecting = false; + private boolean connecting = false; /** * For locking session object. @@ -97,17 +86,23 @@ public class Session { * there shouldn't be any penalty for synchronization. * * @author nantuko - */ + */ private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - public Session(MageFrame frame) { - this.frame = frame; + public Session(MageClient client) { + this.client = client; } - public boolean connect(Connection connection) { + public synchronized boolean connect(Connection connection) { + this.connecting = true; if (isConnected()) { - disconnect(); + disconnect(true); } + this.connection = connection; + return connect(); + } + + public boolean connect() { try { System.setSecurityManager(null); System.setProperty("http.nonProxyHosts", "code.google.com"); @@ -131,80 +126,54 @@ public class Session { break; } Registry reg = LocateRegistry.getRegistry(connection.getHost(), connection.getPort()); - this.server = (Server) reg.lookup(Config.remoteServer); + this.server = (Server) reg.lookup("mage-server"); this.userName = connection.getUsername(); - if (client == null) - client = new Client(this, frame); - sessionId = server.registerClient(userName, client.getId(), frame.getVersion()); + sessionId = server.registerClient(userName, client.getId(), client.getVersion()); callbackDaemon = new CallbackClientDaemon(sessionId, client, server); serverState = server.getServerState(); future = sessionExecutor.scheduleWithFixedDelay(new ServerPinger(), 5, 5, TimeUnit.SECONDS); logger.info("Connected to RMI server at " + connection.getHost() + ":" + connection.getPort()); - frame.setStatusText("Connected to " + connection.getHost() + ":" + connection.getPort() + " "); - frame.enableButtons(); + client.connected("Connected to " + connection.getHost() + ":" + connection.getPort() + " "); + reconnecting = false; + connecting = false; return true; } catch (MageException ex) { logger.fatal("", ex); - disconnect(); - JOptionPane.showMessageDialog(frame, "Unable to connect to server. " + ex.getMessage()); + if (!reconnecting) { + disconnect(false); + client.showMessage("Unable to connect to server. " + ex.getMessage()); + } } catch (RemoteException ex) { logger.fatal("Unable to connect to server - ", ex); - disconnect(); - JOptionPane.showMessageDialog(frame, "Unable to connect to server. " + ex.getMessage()); + if (!reconnecting) { + disconnect(false); + client.showMessage("Unable to connect to server. " + ex.getMessage()); + } } catch (NotBoundException ex) { logger.fatal("Unable to connect to server - ", ex); } return false; } - - public void disconnect() { - - if (isConnected()) { - try { - for (UUID chatId: chats.keySet()) { - server.leaveChat(chatId, sessionId); - } - } - catch (Exception ex) { - //swallow all exceptions at this point - } - try { - //TODO: stop daemon - if (server != null) - server.deregisterClient(sessionId); - } catch (RemoteException ex) { - logger.fatal("Error disconnecting ...", ex); - } catch (MageException ex) { - logger.fatal("Error disconnecting ...", ex); - } - removeServer(); - } - } - - private void removeServer() { - if (future != null && !future.isDone()) - future.cancel(true); - lock.writeLock().lock(); - try { - server = null; - } finally { - lock.writeLock().unlock(); - } - frame.hideGames(); - frame.hideTables(); - frame.setStatusText("Not connected"); - frame.disableButtons(); - logger.info("Disconnected ... "); - JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Disconnected.", "Disconnected", JOptionPane.INFORMATION_MESSAGE); - } - public void ack(String message) { - try { - server.ack(message, sessionId); - } catch (RemoteException ex) { - handleRemoteException(ex); - } catch (MageException ex) { - handleMageException(ex); + public synchronized void disconnect(boolean showMessage) { + if (sessionState == SessionState.CONNECTED) + sessionState = SessionState.DISCONNECTING; + if (connection == null) + return; + if (sessionState == SessionState.CONNECTED) { + try { + server.deregisterClient(sessionId); + } catch (Exception ex) { + logger.fatal("Error disconnecting ...", ex); + } + } + client.disconnected(); + logger.info("Disconnected ... "); + if (sessionState == SessionState.SERVER_UNAVAILABLE && showMessage) { + client.showError("Server error. You have been disconnected"); + } + else { + sessionState = SessionState.DISCONNECTED; } } @@ -220,7 +189,7 @@ public class Session { } public boolean isConnected() { - return server != null; + return sessionState == SessionState.CONNECTED; } public String[] getPlayerTypes() { @@ -245,30 +214,6 @@ public class Session { return false; } - public ChatPanel getChat(UUID chatId) { - return chats.get(chatId); - } - - public GamePanel getGame(UUID gameId) { - return games.get(gameId); - } - - public void addGame(UUID gameId, GamePanel gamePanel) { - games.put(gameId, gamePanel); - } - - public DraftPanel getDraft(UUID draftId) { - return drafts.get(draftId); - } - - public void addDraft(UUID draftId, DraftPanel draftPanel) { - drafts.put(draftId, draftPanel); - } - - public void addTournament(UUID tournamentId, TournamentPanel tournament) { - tournaments.put(tournamentId, tournament); - } - public UUID getMainRoomId() { try { return server.getMainRoomId(); @@ -475,10 +420,9 @@ public class Session { return null; } - public boolean joinChat(UUID chatId, ChatPanel chat) { + public boolean joinChat(UUID chatId) { try { server.joinChat(chatId, sessionId, userName); - chats.put(chatId, chat); return true; } catch (RemoteException ex) { handleRemoteException(ex); @@ -493,7 +437,6 @@ public class Session { try { if (server == null) return false; server.leaveChat(chatId, sessionId); - chats.remove(chatId); return true; } catch (RemoteException ex) { handleRemoteException(ex); @@ -785,19 +728,17 @@ public class Session { private void handleRemoteException(RemoteException ex) { logger.fatal("Communication error", ex); - JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Critical server error. Disconnecting", "Error", JOptionPane.ERROR_MESSAGE); - disconnect(); + disconnect(false); } private void handleMageException(MageException ex) { logger.fatal("Server error", ex); - JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Critical server error. Disconnecting", "Error", JOptionPane.ERROR_MESSAGE); - disconnect(); + disconnect(false); } private void handleGameException(GameException ex) { logger.warn(ex.getMessage()); - JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); + client.showError(ex.getMessage()); } @@ -805,14 +746,6 @@ public class Session { return userName; } - public MageUI getUI() { - return ui; - } - - public Server getServerRef() { - return server; - } - class ServerPinger implements Runnable { private int missed = 0; @@ -823,7 +756,7 @@ public class Session { missed++; if (missed > 10) { logger.info("Connection to server timed out"); - removeServer(); + disconnect(true); } } else { diff --git a/Mage.Server/plugins/mage-tournament-booster-draft.jar b/Mage.Server/plugins/mage-tournament-booster-draft.jar index 9b81b4c1bf9af8f4f0de78dbe903fe985cee57a4..2fd3c301dba5ba29f044bf70035411625518dad1 100644 GIT binary patch delta 977 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 delta 990 zcmdn1zEhnyz?+$civa}q*LqImRpH=U>uINc{TI_j%X(qwIK4PM22>@d6_`OP-)V!W z%`%MTOkl<|=FbpD51R)wSmZkABSsKo@*Zw2F#VF-7);CacnOKRdfJsL|D5!Tk%8eS zD+7ZW!{kIEsmXG}a`kgVeDmiRh}cYzZw%Ypbkc0qD$%DRp{u6kDyy!o>W+D=x+Ecb zTcY`@PxpUs;C+_8I#h+vKyLp>>1UQ6txwKRdGR46+^6=QO{Q)c$JHH6b&fGjR+lP{ zT(J9nb&0Hc;iipe?s`sT{#{{n>^OhhcHc?bU$X0~`K7aErut-_;ke6EpU7cdcIv?P zISH{vWsNZveTfY#_SX5xP5!aulP{alhnB>s&vU};SB5A|oT;TbD}gyU!E@%hGwqCA zC#64{o_A1OSLhggMR{KHQa%0|84TYYI^V6#S=sY$)v|`NV3Vltv!m4K9+3~e@~ilO z&?|qzHPh-J^rq}!W7Bxp^~{^~MRWb1gA3wP&PSB7{`TK}C)fO_Q@qZb?wLpX#P)1{ zGl%O`(J2qlt1Frr>png>viW;`?6jozJ?nOCzxHTXs_^Za-1a@^Vz&65J8$9SW4!M0 zec#C5`aY&`j=h?y90floX?g~ku-nGou(!2d<+}WVQn20uv$Ko*&#dw;UY_%SbG>V% zWBr{s&pu!CeRKPeKu)-fbVti(=SY)FJx_~jp9Ga%RlTuyy|C#t)~nNA+?(F@!hX_( z2cpL3t@jjo230$h&h(vL!Y}x!(mcpsZlBBiAd{N?Tel|g?R2R86JQim`})}C-Cas= z^_iZgT{n)o)1I=s+Of!SVezT}@w$Lb!BbfO1JjxmBt3F~(xctURl80U0@G$05Zl6% zi1Z|WzWQbZ0fq~<1^){ktWjAgA|iA9(Ss!ye&(!mTrs_g@U4w9WPXuI{%r-P<$W^5-%aZ}@jjWpD1fghk8#TDRC`=9qQyw3)2WuX;Ku zxwFb|H&?FC&UrKJWDfjc4De=T5@CjC6FUZm$^HC>@T@ZV1iuwHWwQzxfN2W>YcO3V z5C^8O3uJ?7SHT!Cy+kkpOtT9?^tlK*gZW)TQM@QQWAaC#a0SeO0EPw>ENNWJ2hBOa QVBwm)Tv&juk{=`q0QbC;n*aa+