/* * $Id$ * * Copyright (c) 2000-2013 by Rodney Kinney, Brent Easton * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License (LGPL) as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, copies are available * at http://www.opensource.org. */ package VASSAL.chat.peer2peer; import java.awt.Color; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.util.Enumeration; import java.util.Properties; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.UIManager; import javax.swing.WindowConstants; import net.miginfocom.swing.MigLayout; import org.litesoft.p2pchat.PeerInfo; import org.litesoft.p2pchat.PendingPeerManager; import VASSAL.build.GameModule; import VASSAL.chat.ui.ChatControlsInitializer; import VASSAL.chat.ui.ChatServerControls; import VASSAL.configure.StringArrayConfigurer; import VASSAL.i18n.Resources; import VASSAL.preferences.Prefs; import VASSAL.tools.SequenceEncoder; import VASSAL.tools.swing.Dialogs; /** * Date: Mar 12, 2003 */ public class DirectPeerPool implements PeerPool, ChatControlsInitializer { protected static final String ADDRESS_PREF = "PeerAddressBook"; //$NON-NLS-1$ private AcceptPeerThread acceptThread; private JButton inviteButton; private JDialog frame; private int listenPort; private static StringArrayConfigurer addressConfig; private JButton invitePeerButton; private JButton addButton; private JButton editButton; private JButton removeButton; private JList addressList; private DefaultListModel addressBook; private Properties params; private boolean serverMode; public DirectPeerPool() { this (new Properties()); } public DirectPeerPool(Properties param) { params = new Properties(); params.putAll(param); serverMode = P2PClientFactory.P2P_SERVER_MODE.equals(params.getProperty(P2PClientFactory.P2P_MODE_KEY)); inviteButton = new JButton(Resources.getString("Peer2Peer.connect")); //$NON-NLS-1$ inviteButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { frame.setVisible(true); } }); inviteButton.setEnabled(false); inviteButton.setVisible(P2PClientFactory.P2P_SERVER_MODE.equals(params.getProperty(P2PClientFactory.P2P_MODE_KEY))); } public void initialize(P2PPlayer myInfo, PendingPeerManager ppm) throws IOException { listenPort = 5050; String port = params.getProperty(P2PClientFactory.P2P_LISTEN_PORT); if (port != null && port.length() > 0) { try { listenPort = Integer.parseInt(port); } catch (NumberFormatException e) { // No error; } } acceptThread = new AcceptPeerThread(listenPort, ppm); acceptThread.setName("Accept Peer Thread"); //$NON-NLS-1$ myInfo.getInfo().setPort(acceptThread.getPort()); acceptThread.start(); if (frame == null) { initComponents(myInfo, ppm); inviteButton.setEnabled(true); } } protected boolean isServerMode() { return serverMode; } public void disconnect() { if (frame != null) { frame.dispose(); frame = null; inviteButton.setEnabled(false); } if (acceptThread != null) { acceptThread.halt(); acceptThread = null; } } public void connectFailed(PeerInfo peerInfo) { JOptionPane.showMessageDialog(frame, Resources.getString("Peer2Peer.could_not_reach", peerInfo.getAddresses(), String.valueOf(peerInfo.getPort())), //$NON-NLS-1$ Resources.getString("Peer2Peer.invite_failed"), JOptionPane.INFORMATION_MESSAGE); //$NON-NLS-1$ } public void initComponents(final P2PPlayer me, final PendingPeerManager ppm) { // Retrieve Address Book from preference if (addressConfig == null) { addressConfig = new StringArrayConfigurer(ADDRESS_PREF, null); Prefs.getGlobalPrefs().addOption(null, addressConfig); } String[] encodedEntries = addressConfig.getStringArray(); addressBook = new DefaultListModel(); addressList = new JList(addressBook); for (String e : encodedEntries) { addToList(new Entry(e)); } Frame owner = null; if (GameModule.getGameModule() != null) { owner = GameModule.getGameModule().getFrame(); } frame = new JDialog(owner,Resources.getString("Peer2Peer.direct_connection")); //$NON-NLS-1$ frame.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); frame.setLayout(new MigLayout()); frame.add(new WTextArea(Resources.getString("Peer2Peer.other_players_address")), "growx, span 4,wrap"); //$NON-NLS-1$ //$NON-NLS-2$ final JScrollPane scroll = new JScrollPane(addressList); frame.add(scroll, "span 4, grow, push, w 300, h 400, wrap"); //$NON-NLS-1$ invitePeerButton = new JButton(Resources.getString("Peer2Peer.connect")); //$NON-NLS-1$ invitePeerButton.setToolTipText(Resources.getString("Peer2Peer.invite_button_tooltip")); //$NON-NLS-1$)) invitePeerButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { invite(ppm); }}); frame.add(invitePeerButton, "growx, push"); //$NON-NLS-1$ addButton = new JButton(Resources.getString("General.add")); //$NON-NLS-1$ addButton.setToolTipText(Resources.getString("Peer2Peer.add_button_tooltip")); //$NON-NLS-1$)) addButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { addEntry(); }}); frame.add(addButton, "growx, push"); //$NON-NLS-1$ editButton = new JButton(Resources.getString("General.edit")); //$NON-NLS-1$ editButton.setToolTipText(Resources.getString("Peer2Peer.edit_button_tooltip")); //$NON-NLS-1$)) editButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { editEntry(); }}); frame.add(editButton, "growx, push"); //$NON-NLS-1$ removeButton = new JButton(Resources.getString("General.remove")); //$NON-NLS-1$ removeButton.setToolTipText(Resources.getString("Peer2Peer.remove_button_tooltip")); //$NON-NLS-1$)) removeButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { removeEntries(); }}); frame.add(removeButton, "growx, push, wrap"); //$NON-NLS-1$ frame.pack(); frame.setLocationRelativeTo(owner); return; } protected void invite(final PendingPeerManager ppm) { final int selected[] = addressList.getSelectedIndices(); for (int i = 0; i < selected.length; i++) { final Entry entry = (Entry) addressBook.getElementAt(selected[i]); final PeerInfo info = PeerInfo.deFormat(entry.getAddress()+":"+entry.getPort()+" "+entry.getDescription()); //$NON-NLS-1$ //$NON-NLS-2$ if (info != null) { ppm.addNewPeer(info); GameModule.getGameModule().warn(Resources.getString("Chat.invite_sent", entry.toString())); //$NON-NLS-1$ } else { JOptionPane.showMessageDialog(frame, Resources.getString("Peer2Peer.invalid_format")); //$NON-NLS-1$ } } } protected void addEntry() { final Entry e = new Entry(); if (e.edit()) { if (! addressBook.contains(e)) { addToList(e); saveAddressBook(); } } } protected void addToList(Entry e) { boolean added = false; for (int i = 0; i < addressBook.getSize() && !added; i++) { if (e.compareTo((Entry) (addressBook.getElementAt(i))) < 0) { addressBook.insertElementAt(e, i); added = true; } } if (! added) { addressBook.addElement(e); } } protected void editEntry() { final int index = addressList.getSelectedIndex(); if (index >= 0) { final Entry e = (Entry) addressBook.getElementAt(index); if (e.edit()) { addressBook.removeElementAt(index); addToList(e); saveAddressBook(); } } } protected void removeEntries() { final int selected[] = addressList.getSelectedIndices(); if (selected.length == 0) { return; } final Entry entries[] = new Entry[selected.length]; for (int i = 0; i < selected.length; i++) { entries[i] = (Entry) addressBook.getElementAt(selected[i]); } final JPanel queryPanel = new JPanel(new MigLayout("", "10[][]10")); final String mess = (entries.length == 1 ? Resources.getString("Peer2Peer.remove_entry") : Resources.getString("Peer2Peer.remove_entries", entries.length)); //$NON-NLS-1$ //$NON-NLS-2$ queryPanel.add(new JLabel(mess), "align center, wrap"); queryPanel.add(new JLabel(), "wrap"); for (int i = 0; i < entries.length; i++) { queryPanel.add(new JLabel(entries[i].toString()), "wrap"); } final Integer result = (Integer) Dialogs.showDialog(null, Resources.getString("Peer2Peer.remove_entry"), //$NON-NLS-1$ queryPanel, JOptionPane.QUESTION_MESSAGE, null, JOptionPane.OK_CANCEL_OPTION, null, null, null, null); if (result != null && result.intValue() == 0) { for (int i = 0; i < entries.length; i++) { addressBook.removeElement(entries[i]); } saveAddressBook(); } } protected void saveAddressBook() { final String[] entries = new String[addressBook.size()]; int i = 0; for (Enumeration<?> e = addressBook.elements(); e.hasMoreElements(); ) { entries[i++] = ((Entry) e.nextElement()).encode(); } addressConfig.setValue(entries); } public void initializeControls(ChatServerControls controls) { controls.getToolbar().add(inviteButton); } public void uninitializeControls(ChatServerControls controls) { controls.getToolbar().remove(inviteButton); controls.getToolbar().repaint(); } /** * A class representing the address of another player's computer. * */ private class Entry implements Comparable<Entry>{ String description; String address; String port; JTextField descriptionField; JTextField addressField; JTextField portField; public Entry () { this("", "", "5050", ""); } public Entry (String description, String address, String port, String passwd) { this.description =description; this.address = address; this.port = port; } public Entry (String s) { decode(s); } public String getDescription() { return description; } public String getAddress() { return address; } public String getPort() { return port; } // public String getPasswd() { // return passwd; // } public String toString() { return description + " [" + address + ":" + port; // + (getPasswd().length() == 0 ? "" : "/") + getPasswd() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } private void decode(String s) { SequenceEncoder.Decoder sd = new SequenceEncoder.Decoder(s, '|'); description = sd.nextToken(""); //$NON-NLS-1$ address = sd.nextToken(""); //$NON-NLS-1$ port = sd.nextToken("5050"); //$NON-NLS-1$ } public String encode() { SequenceEncoder se = new SequenceEncoder('|'); se.append(description); se.append(address); se.append(port); return se.getValue(); } public int compareTo(Entry e) { return toString().compareTo(e.toString()); } public boolean edit() { descriptionField = new JTextField(description); addressField = new JTextField(address); portField = new JTextField(port); final JPanel editPanel = new JPanel(new MigLayout("", "[align right]rel[]", "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ editPanel.add(new JLabel(Resources.getString("Editor.description_label"))); //$NON-NLS-1$ editPanel.add(descriptionField, "wrap, grow, push"); //$NON-NLS-1$ editPanel.add(new JLabel(Resources.getString("Chat.ip_address"))); //$NON-NLS-1$ editPanel.add(addressField, "wrap, grow, push"); //$NON-NLS-1$ editPanel.add(new JLabel(Resources.getString("ServerAddressBook.port"))); //$NON-NLS-1$ editPanel.add(portField, "wrap, grow, push"); //$NON-NLS-1$ final Integer result = (Integer) Dialogs.showDialog(null, Resources.getString("Peer2Peer.add_peer_connection"), //$NON-NLS-1$ editPanel, JOptionPane.PLAIN_MESSAGE, null, JOptionPane.OK_CANCEL_OPTION, null, null, null, null); if (result != null && result == 0) { description = descriptionField.getText(); address = addressField.getText(); port = portField.getText(); return true; } return false; } } private class WTextArea extends JTextArea { private static final long serialVersionUID = 1L; public WTextArea(String s) { super(s); setEditable(false); setLineWrap(true); setWrapStyleWord(true); setBackground(UIManager.getColor("OptionPane.background")); } } }