/* * $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.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; import java.util.Properties; import org.litesoft.p2pchat.ActivePeer; import org.litesoft.p2pchat.ActivePeerManager; import org.litesoft.p2pchat.MyInfo; import org.litesoft.p2pchat.PeerInfo; import org.litesoft.p2pchat.PendingPeerManager; import org.litesoft.p2pchat.UserDialog; import VASSAL.build.GameModule; import VASSAL.build.module.Chatter; import VASSAL.chat.ChatServerConnection; import VASSAL.chat.Player; import VASSAL.chat.PlayerEncoder; import VASSAL.chat.Room; import VASSAL.chat.ServerStatus; import VASSAL.chat.SimplePlayer; import VASSAL.chat.SimpleRoom; import VASSAL.chat.SimpleStatus; import VASSAL.chat.SoundEncoder; import VASSAL.chat.SynchEncoder; import VASSAL.chat.WelcomeMessageServer; import VASSAL.chat.messageboard.Message; import VASSAL.chat.messageboard.MessageBoard; import VASSAL.chat.ui.ChatControlsInitializer; import VASSAL.chat.ui.ChatServerControls; import VASSAL.chat.ui.RoomInteractionControlsInitializer; import VASSAL.chat.ui.ShowProfileAction; import VASSAL.chat.ui.SimpleStatusControlsInitializer; import VASSAL.chat.ui.SynchAction; import VASSAL.command.Command; import VASSAL.command.CommandEncoder; import VASSAL.i18n.Resources; import VASSAL.tools.PropertiesEncoder; public class P2PClient implements ChatServerConnection, ChatControlsInitializer, UserDialog, PlayerEncoder { private SimplePlayer me; private PendingPeerManager ppm; protected ActivePeerManager peerMgr; private PeerPool pool; private MessageBoard msgSvr; private WelcomeMessageServer welcomeMessageServer; private RoomManager roomMgr; private RoomTracker tracker; private PropertyChangeSupport propSupport = new PropertyChangeSupport(this); private CommandEncoder encoder; private boolean connected = false; private ServerStatus svrStatus; private RoomInteractionControlsInitializer roomControls; private SimpleStatusControlsInitializer playerStatusControls; private SoundEncoder soundEncoder; private SynchEncoder synchEncoder; private PropertyChangeListener nameChangeListener; private Properties params; public P2PClient(CommandEncoder encoder, MessageBoard msgSvr, WelcomeMessageServer welcomeMessageServer, PeerPool pool) { this(encoder, msgSvr, welcomeMessageServer, pool, new Properties()); } public P2PClient(CommandEncoder encoder, MessageBoard msgSvr, WelcomeMessageServer welcomeMessageServer, PeerPool pool, Properties param) { this.encoder = encoder; this.msgSvr = msgSvr; this.welcomeMessageServer = welcomeMessageServer; this.pool = pool; this.params = param; ppm = new PendingPeerManager(this); ppm.setName("Pending Peer Manager"); //$NON-NLS-1$ roomMgr = new RoomManager(); tracker = new RoomTracker(); me = new SimplePlayer("???"); //$NON-NLS-1$ me.updateStatus(); playerStatusControls = new SimpleStatusControlsInitializer(this, false); roomControls = new RoomInteractionControlsInitializer(this); roomControls.addPlayerActionFactory(ShowProfileAction.factory()); roomControls.addPlayerActionFactory(SynchAction.factory(this)); synchEncoder = new SynchEncoder(this, this); soundEncoder = new SoundEncoder(this); nameChangeListener = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { SimplePlayer p = (SimplePlayer) getUserInfo(); p.setName((String) evt.getNewValue()); setUserInfo(p); } }; } public RoomManager getRoomMgr() { return roomMgr; } public void sendToOthers(Command c) { sendToOthers(encoder.encode(c)); } public void sendToAll(String msg) { if (isConnected()) { sendToOthers(msg); showCHAT(((P2PPlayer) me).getInfo(), msg); } } public void sendToOthers(String msg) { if (isConnected()) { final Room myRoom = getRoom(); final Player[] pl = myRoom.getPlayerList().toArray( new Player[myRoom.getPlayerList().size()]); for (Player p : pl) { if (!p.equals(me)) { final ActivePeer peer = peerMgr.getPeerListenerByID(((P2PPlayer) p).getInfo().getID()); if (peer != null) { peer.sendCHAT(msg); } } } } } public void sendTo(Player recipient, Command c) { if (peerMgr != null) { peerMgr.getPeerListenerByInfo(((P2PPlayer) recipient).getInfo()).sendCHAT(encoder.encode(c)); } } public Room getRoom() { return roomMgr.getRoomContaining(me); } public void setRoom(Room r) { if (me instanceof P2PPlayer) { ((P2PPlayer) me).setRoom(r.getName()); if (isConnected()) { peerMgr.sendToAllNAME(); propSupport.firePropertyChange(AVAILABLE_ROOMS, null, roomMgr.update(((P2PPlayer) me).getInfo())); Room newRoom = getRoom(); propSupport.firePropertyChange(ROOM, null, newRoom); } } } public Room[] getAvailableRooms() { return roomMgr.getRooms(); } public Player getUserInfo() { return me; } public void setUserInfo(Player p) { if (me instanceof P2PPlayer) { ((P2PPlayer) me).setStats(p); if (isConnected()) { propSupport.firePropertyChange(AVAILABLE_ROOMS, null, roomMgr.update(((P2PPlayer) me).getInfo())); propSupport.firePropertyChange(ROOM, null, getRoom()); peerMgr.sendToAllNAME(); } } else { me = (SimplePlayer) p; me.updateStatus(); } propSupport.firePropertyChange(PLAYER_INFO, null, me); } public void setConnected(boolean connect) { if (connect) { try { Integer port; try { port = Integer.valueOf(params.getProperty(P2PClientFactory.P2P_LISTEN_PORT)); } catch (NumberFormatException ex) { port = Integer.valueOf(5050); } MyInfo info = new MyInfo(null, port); info.setNetworkPw(params.getProperty(P2PClientFactory.P2P_SERVER_PW)); P2PPlayer p = new P2PPlayer(info); p.updateStatus(); p.setName(me.getName()); p.setRoom(roomMgr.getDefaultRoom().getName()); p.setId(GameModule.getUserId() + "." + System.currentTimeMillis()); //$NON-NLS-1$ setUserInfo(p); pool.initialize(p, ppm); if (peerMgr == null) { peerMgr = new ActivePeerManager(info, this, ppm); } roomMgr.update(((P2PPlayer) me).getInfo()); fireStatus(Resources.getString("Peer2Peer.server_connection_established", params.getProperty(P2PClientFactory.P2P_LISTEN_PORT))); //$NON-NLS-1$ propSupport.firePropertyChange(AVAILABLE_ROOMS, null, roomMgr.getRooms()); propSupport.firePropertyChange(ROOM, null, getRoom()); welcomeMessageServer.getWelcomeMessage().execute(); connected = true; propSupport.firePropertyChange(CONNECTED, null, Boolean.TRUE); } // FIXME: review error message catch (IOException e) { fireStatus(Resources.getString("Peer2Peer.connection_error", e.getMessage())); //$NON-NLS-1$ fireStatus(Resources.getString("Peer2Peer.disconnected")); //$NON-NLS-1$ //$NON-NLS-2$ connected = false; propSupport.firePropertyChange(CONNECTED, null, Boolean.FALSE); } } else if (isConnected()) { if (peerMgr != null) { peerMgr.clear(); } roomMgr.clear(); pool.disconnect(); propSupport.firePropertyChange(AVAILABLE_ROOMS, null, new Room[0]); propSupport.firePropertyChange(ROOM, new SimpleRoom(), null); connected = false; propSupport.firePropertyChange(CONNECTED, Boolean.TRUE, Boolean.FALSE); fireStatus(Resources.getString("Peer2Peer.disconnected")); //$NON-NLS-1$ //$NON-NLS-2$ ppm.finish(); } } protected void fireStatus(String msg) { propSupport.firePropertyChange(STATUS, null, msg); } public boolean isConnected() { return connected; } public Message[] getMessages() { return msgSvr.getMessages(); } public void postMessage(String msg) { msgSvr.postMessage(msg); } public MessageBoard getMessageServer() { return msgSvr; } public ServerStatus getStatusServer() { return svrStatus; } public Player stringToPlayer(String s) { return roomMgr.getPlayerById(s); } public String playerToString(Player p) { return p.getId(); } public void addPropertyChangeListener(String propertyName, java.beans.PropertyChangeListener l) { propSupport.addPropertyChangeListener(propertyName, l); } public void addPropertyChangeListener(PropertyChangeListener l) { propSupport.addPropertyChangeListener(l); } public void setActivePeerManager(ActivePeerManager pActivePeerManager) { peerMgr = pActivePeerManager; } public void setPendingPeerManager(PendingPeerManager pPendingPeerManager) { ppm = pPendingPeerManager; } public synchronized void showUnrecognized(PeerInfo pPeerInfo, String pBadMessage) { } public synchronized void showStreamsFailed(PeerInfo pPeerInfo) { P2PPlayer p = new P2PPlayer(pPeerInfo); propSupport.firePropertyChange(STATUS, null, Resources.getString("Peer2Peer.connection_lost", p.getName())); //$NON-NLS-1$ } public synchronized void showConnectFailed(PeerInfo pPeerInfo) { pool.connectFailed(pPeerInfo); } public synchronized void showConnect(PeerInfo pPeerInfo) { } public synchronized void showDisconnect(PeerInfo pPeerInfo) { propSupport.firePropertyChange(AVAILABLE_ROOMS, null, roomMgr.remove(pPeerInfo)); propSupport.firePropertyChange(ROOM, null, getRoom()); } public synchronized void showCHAT(PeerInfo pPeerInfo, String msg) { propSupport.firePropertyChange(INCOMING_MSG,null,msg); } public synchronized void showPMSG(PeerInfo pPeerInfo, String msg) { showCHAT(pPeerInfo, msg); } public synchronized void showNAME(PeerInfo pPeerInfo) { tracker.init(getRoom()); propSupport.firePropertyChange(AVAILABLE_ROOMS, null, roomMgr.update(pPeerInfo)); Room myRoom = getRoom(); propSupport.firePropertyChange(ROOM, null, myRoom); tracker.finalize(myRoom); } public synchronized void showHELO(PeerInfo pPeerInfo) { // We have received a connection request final Chatter chatter = GameModule.getGameModule().getChatter(); final ActivePeer peer = peerMgr.getPeerListenerByInfo(pPeerInfo); Properties props; String name, ip, details; try { props = new PropertiesEncoder(pPeerInfo.getChatName()).getProperties(); name = props.getProperty(SimpleStatus.NAME); ip = props.getProperty(SimpleStatus.IP); details = name+" ("+ip+":"+pPeerInfo.getPort()+")"; } catch (IOException ex) { details = ""; } // Does the password of the new Peer match ours? if (! pPeerInfo.getNetworkPw().equals(params.getProperty(P2PClientFactory.P2P_SERVER_PW))) { new Chatter.DisplayText(chatter, Resources.getString("Peer2Peer.bad_password", details)).execute(); peer.finish(); return; } fireStatus(Resources.getString("Peer2Peer.connected", details)); propSupport.firePropertyChange(AVAILABLE_ROOMS, null, roomMgr.update(pPeerInfo)); propSupport.firePropertyChange(ROOM, null, getRoom()); } public void initializeControls(ChatServerControls controls) { playerStatusControls.initializeControls(controls); roomControls.initializeControls(controls); controls.setRoomControlsVisible(false); final GameModule g = GameModule.getGameModule(); me.setName((String) g.getPrefs().getValue(GameModule.REAL_NAME)); g.getPrefs().getOption(GameModule.REAL_NAME).addPropertyChangeListener(nameChangeListener); g.addCommandEncoder(synchEncoder); g.addCommandEncoder(soundEncoder); if (pool instanceof ChatControlsInitializer) { ((ChatControlsInitializer)pool).initializeControls(controls); } } public void uninitializeControls(ChatServerControls controls) { playerStatusControls.uninitializeControls(controls); roomControls.uninitializeControls(controls); final GameModule g = GameModule.getGameModule(); g.getPrefs().getOption(GameModule.REAL_NAME).removePropertyChangeListener(nameChangeListener); g.removeCommandEncoder(synchEncoder); g.removeCommandEncoder(soundEncoder); if (pool instanceof ChatControlsInitializer) { ((ChatControlsInitializer)pool).uninitializeControls(controls); } } }