diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/jrummikub/control/network/ConnectionControl.java | 233 | ||||
-rw-r--r-- | src/jrummikub/control/network/NetworkControl.java | 37 | ||||
-rw-r--r-- | src/jrummikub/view/impl/GameListPanel.java | 15 |
3 files changed, 254 insertions, 31 deletions
diff --git a/src/jrummikub/control/network/ConnectionControl.java b/src/jrummikub/control/network/ConnectionControl.java index 9777853..171c15d 100644 --- a/src/jrummikub/control/network/ConnectionControl.java +++ b/src/jrummikub/control/network/ConnectionControl.java @@ -1,19 +1,36 @@ package jrummikub.control.network; +import java.util.UUID; + import javax.swing.SwingUtilities; import jrummikub.util.Event; +import jrummikub.util.Event1; import jrummikub.util.IEvent; +import jrummikub.util.IEvent1; import jrummikub.util.LoginData; +import jrummikub.view.IGameListPanel.GameData; import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.filter.AndFilter; +import org.jivesoftware.smack.filter.PacketExtensionFilter; +import org.jivesoftware.smack.filter.PacketTypeFilter; +import org.jivesoftware.smack.packet.DefaultPacketExtension; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.PacketExtension; import org.jivesoftware.smack.packet.XMPPError; import org.jivesoftware.smack.packet.XMPPError.Type; +import org.jivesoftware.smackx.muc.DiscussionHistory; import org.jivesoftware.smackx.muc.MultiUserChat; class ConnectionControl { + private final static String ELEMENT_NAME = "rummikub"; + private final static String NAMESPACE = "http://home.universe-factory.net/rummikub/"; + private final LoginData loginData; private volatile Connection connection; private volatile MultiUserChat muc; @@ -21,6 +38,11 @@ class ConnectionControl { private Event connectedEvent = new Event(); private Event connectionFailedEvent = new Event(); + private Event1<GameData> gameOfferEvent = new Event1<GameData>(); + private Event1<UUID> gameWithdrawalEvent = new Event1<UUID>(); + + private volatile GameData offeredGame; + ConnectionControl(LoginData loginData) { this.loginData = loginData; } @@ -44,6 +66,30 @@ class ConnectionControl { return connectionFailedEvent; } + IEvent1<GameData> getGameOfferEvent() { + return gameOfferEvent; + } + + IEvent1<UUID> getGameWithdrawalEvent() { + return gameWithdrawalEvent; + } + + void offerGame(GameData data) { + offeredGame = data; + + sendGameOffer(); + } + + void withdrawGame(UUID uuid) { + offeredGame = null; + + new Thread(new SendGameWithdrawRunner(uuid)).start(); + } + + private void sendGameOffer() { + new Thread(new SendGameOfferRunner(offeredGame)).start(); + } + private static void emitLater(final Event event) { SwingUtilities.invokeLater(new Runnable() { @Override @@ -53,40 +99,97 @@ class ConnectionControl { }); } + private Message createMessage(PacketExtension extension) { + Message message = muc.createMessage(); + message.addExtension(extension); + return message; + } + + private static DefaultPacketExtension createJRummikubExtension() { + return new DefaultPacketExtension(ELEMENT_NAME, NAMESPACE); + } + + private void processPacket(Packet packet) { + DefaultPacketExtension extension = (DefaultPacketExtension) packet + .getExtension(ELEMENT_NAME, NAMESPACE); + + String messageType = extension.getValue("messageType"); + + if (messageType.equals("game_offer")) { + String host = packet.getFrom(); + host = host.substring(host.indexOf('/') + 1); + + UUID uuid = UUID.fromString(extension.getValue("uuid")); + int currentPlayerCount = Integer.parseInt(extension + .getValue("currentPlayerCount")); + int maxPlayerCount = Integer.parseInt(extension + .getValue("maxPlayerCount")); + + GameData gameData = new GameData(uuid, host); + gameData.setCurrentPlayerCount(currentPlayerCount); + gameData.setMaxPlayerCount(maxPlayerCount); + gameOfferEvent.emit(gameData); + } else if (messageType.equals("game_withdrawal")) { + gameWithdrawalEvent.emit(UUID.fromString(extension.getValue("uuid"))); + } else if (messageType.equals("game_request")) { + if (offeredGame != null) { + sendGameOffer(); + } + } + } + + private Message createGameOfferMessage(GameData data) { + DefaultPacketExtension extension = createJRummikubExtension(); + + extension.setValue("messageType", "game_offer"); + extension.setValue("uuid", data.getGameID().toString()); + extension.setValue("currentPlayerCount", + Integer.toString(data.getCurrentPlayerCount())); + extension.setValue("maxPlayerCount", + Integer.toString(data.getMaxPlayerCount())); + + return createMessage(extension); + } + + private Message createGameWithdrawMessage(UUID uuid) { + DefaultPacketExtension extension = createJRummikubExtension(); + + extension.setValue("messageType", "game_withdrawal"); + extension.setValue("uuid", uuid.toString()); + + return createMessage(extension); + } + + private Message createGameRequestMessage() { + DefaultPacketExtension extension = createJRummikubExtension(); + + extension.setValue("messageType", "game_request"); + + return createMessage(extension); + } + private class ConnectRunner implements Runnable { @Override public void run() { synchronized (ConnectionControl.this) { connection = new XMPPConnection(loginData.getServerName()); - try { - connection.connect(); - connection.login(loginData.getUserName(), - loginData.getPassword(), "JRummikub"); - muc = new MultiUserChat(connection, - loginData.getChannelName()); - - String nickname = loginData.getUserName(); - // Loop until a unused nickname is found - while (true) { - try { - muc.join(nickname); - break; // Join was successful, break the loop - } catch (XMPPException e) { - XMPPError error = e.getXMPPError(); - if (error.getType() == Type.CANCEL - && error.getCode() == 409) { - // There was a conflict, try again with another - // nickname - nickname += "_"; - continue; - } else { - // An unknown error has occurred, cancel connect - throw e; + + connection.addPacketListener(new PacketListener() { + @Override + public void processPacket(final Packet packet) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + ConnectionControl.this.processPacket(packet); } - } + }); + } + }, new AndFilter(new PacketTypeFilter(Message.class), + new PacketExtensionFilter(ELEMENT_NAME, NAMESPACE))); - emitLater(connectedEvent); + try { + doConnect(); } catch (XMPPException e) { connection.disconnect(); connection = null; @@ -98,6 +201,84 @@ class ConnectionControl { } } } + + private void doConnect() throws XMPPException { + connection.connect(); + connection.login(loginData.getUserName(), loginData.getPassword(), + "JRummikub"); + muc = new MultiUserChat(connection, loginData.getChannelName()); + DiscussionHistory history = new DiscussionHistory(); + history.setMaxStanzas(0); + + String nickname = loginData.getUserName(); + // Loop until a unused nickname is found + while (true) { + try { + muc.join(nickname, null, history, 10000); + break; // Join was successful, break the loop + } catch (XMPPException e) { + XMPPError error = e.getXMPPError(); + if (error.getType() == Type.CANCEL && error.getCode() == 409) { + // There was a conflict, try again with another + // nickname + nickname += "_"; + continue; + } else { + // An unknown error has occurred, cancel connect + throw e; + } + } + } + + emitLater(connectedEvent); + + new Thread(new SendGameRequestRunner()).start(); + } + } + + private class SendGameOfferRunner implements Runnable { + private GameData data; + + public SendGameOfferRunner(GameData data) { + this.data = data; + } + + @Override + public void run() { + synchronized (ConnectionControl.this) { + if (connection != null) { + connection.sendPacket(createGameOfferMessage(data)); + } + } + } + } + + private class SendGameWithdrawRunner implements Runnable { + private UUID uuid; + + public SendGameWithdrawRunner(UUID uuid) { + this.uuid = uuid; + } + + @Override + public void run() { + synchronized (ConnectionControl.this) { + if (connection != null) { + connection.sendPacket(createGameWithdrawMessage(uuid)); + } + } + } + } + + private class SendGameRequestRunner implements Runnable { + @Override + public void run() { + synchronized (ConnectionControl.this) { + if (connection != null) { + connection.sendPacket(createGameRequestMessage()); + } + } + } } private class DisconnectRunner implements Runnable { diff --git a/src/jrummikub/control/network/NetworkControl.java b/src/jrummikub/control/network/NetworkControl.java index ce7714b..013c866 100644 --- a/src/jrummikub/control/network/NetworkControl.java +++ b/src/jrummikub/control/network/NetworkControl.java @@ -1,7 +1,9 @@ package jrummikub.control.network; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import jrummikub.util.Connection; @@ -20,6 +22,8 @@ public class NetworkControl { private List<Connection> connections = new ArrayList<Connection>(); private Event stopNetworkEvent = new Event(); + private Map<UUID, GameData> gameMap = new HashMap<UUID, GameData>(); + public NetworkControl(final LoginData loginData, final IView view) { this.view = view; connectionControl = new ConnectionControl(loginData); @@ -33,7 +37,8 @@ public class NetworkControl { GameData testData = new GameData(UUID.randomUUID(), "NeoRaider"); testData.setCurrentPlayerCount(2); testData.setMaxPlayerCount(4); - view.getGameListPanel().addGame(testData); + + connectionControl.offerGame(testData); } })); @@ -46,6 +51,36 @@ public class NetworkControl { } })); + connections.add(connectionControl.getGameOfferEvent().add( + new IListener1<IGameListPanel.GameData>() { + @Override + public void handle(GameData value) { + GameData game = gameMap.get(value.getGameID()); + + if (game == null) { + game = value; + gameMap.put(value.getGameID(), value); + } else { + game.setCurrentPlayerCount(value.getCurrentPlayerCount()); + game.setMaxPlayerCount(value.getMaxPlayerCount()); + } + + view.getGameListPanel().addGame(game); + } + })); + connections.add(connectionControl.getGameWithdrawalEvent().add( + new IListener1<UUID>() { + @Override + public void handle(UUID value) { + GameData game = gameMap.get(value); + + if (game != null) { + view.getGameListPanel().removeGame(game); + gameMap.remove(value); + } + } + })); + connections.add(view.getGameListPanel().getJoinEvent() .add(new IListener1<IGameListPanel.GameData>() { @Override diff --git a/src/jrummikub/view/impl/GameListPanel.java b/src/jrummikub/view/impl/GameListPanel.java index eae424a..dae0f27 100644 --- a/src/jrummikub/view/impl/GameListPanel.java +++ b/src/jrummikub/view/impl/GameListPanel.java @@ -4,7 +4,6 @@ import java.awt.Color; import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; -import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; @@ -163,16 +162,24 @@ class GameListPanel extends JPanel implements IGameListPanel { JLabel hostLabel, playerCountLabel; GameDataCellRenderer() { - setLayout(new GridLayout(1, 2)); + setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + c.gridwidth = 1; + c.weightx = 1; + c.weighty = 1; + hostLabel = new JLabel(); hostLabel.setOpaque(true); hostLabel.setHorizontalAlignment(JLabel.LEFT); - add(hostLabel); + add(hostLabel, c); playerCountLabel = new JLabel(); playerCountLabel.setOpaque(true); playerCountLabel.setHorizontalAlignment(JLabel.RIGHT); - add(playerCountLabel); + + c.gridwidth = GridBagConstraints.REMAINDER; + add(playerCountLabel, c); } @Override |