package games.strategy.engine.lobby.server; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import games.strategy.engine.message.IRemoteMessenger; import games.strategy.engine.message.MessageContext; import games.strategy.net.GUID; import games.strategy.net.IConnectionChangeListener; import games.strategy.net.IMessenger; import games.strategy.net.INode; import games.strategy.net.IServerMessenger; public class LobbyGameController implements ILobbyGameController { private static final Logger s_logger = Logger.getLogger(LobbyGameController.class.getName()); private final Object m_mutex = new Object(); private final Map<GUID, GameDescription> m_allGames = new HashMap<>(); private final ILobbyGameBroadcaster m_broadcaster; public LobbyGameController(final ILobbyGameBroadcaster broadcaster, final IMessenger messenger) { m_broadcaster = broadcaster; final IMessenger m_messenger = messenger; ((IServerMessenger) m_messenger).addConnectionChangeListener(new IConnectionChangeListener() { @Override public void connectionRemoved(final INode to) { connectionLost(to); } @Override public void connectionAdded(final INode to) {} }); } private void connectionLost(final INode to) { final List<GUID> removed = new ArrayList<>(); synchronized (m_mutex) { final Iterator<GUID> keys = m_allGames.keySet().iterator(); while (keys.hasNext()) { final GUID key = keys.next(); final GameDescription game = m_allGames.get(key); if (game.getHostedBy().equals(to)) { keys.remove(); removed.add(key); } } } for (final GUID guid : removed) { m_broadcaster.gameRemoved(guid); } } @Override public void postGame(final GUID gameID, final GameDescription description) { final INode from = MessageContext.getSender(); assertCorrectHost(description, from); s_logger.info("Game added:" + description); synchronized (m_mutex) { m_allGames.put(gameID, description); } m_broadcaster.gameUpdated(gameID, description); } private static void assertCorrectHost(final GameDescription description, final INode from) { if (!from.getAddress().getHostAddress().equals(description.getHostedBy().getAddress().getHostAddress())) { s_logger.severe("Game modified from wrong host, from:" + from + " game host:" + description.getHostedBy()); throw new IllegalStateException("Game from the wrong host"); } } @Override public void updateGame(final GUID gameID, final GameDescription description) { final INode from = MessageContext.getSender(); assertCorrectHost(description, from); if (s_logger.isLoggable(Level.FINE)) { s_logger.fine("Game updated:" + description); } synchronized (m_mutex) { final GameDescription oldDescription = m_allGames.get(gameID); // out of order updates // ignore, we already have the latest if (oldDescription.getVersion() > description.getVersion()) { return; } if (!oldDescription.getHostedBy().equals(description.getHostedBy())) { throw new IllegalStateException("Game modified by wrong host"); } m_allGames.put(gameID, description); } m_broadcaster.gameUpdated(gameID, description); } @Override public Map<GUID, GameDescription> listGames() { synchronized (m_mutex) { final Map<GUID, GameDescription> rVal = new HashMap<>(m_allGames); return rVal; } } public void register(final IRemoteMessenger remote) { remote.registerRemote(this, GAME_CONTROLLER_REMOTE); } @Override public String testGame(final GUID gameID) { GameDescription description; synchronized (m_mutex) { description = m_allGames.get(gameID); } if (description == null) { return "No such game found"; } // make sure we are being tested from the right node final INode from = MessageContext.getSender(); assertCorrectHost(description, from); final int port = description.getPort(); final String host = description.getHostedBy().getAddress().getHostAddress(); s_logger.fine("Testing game connection on host:" + host + " port:" + port); final Socket s = new Socket(); try { s.connect(new InetSocketAddress(host, port), 10 * 1000); s.close(); s_logger.fine("Connection test passed for host:" + host + " port:" + port); return null; } catch (final IOException e) { s_logger.fine("Connection test failed for host:" + host + " port:" + port + " reason:" + e.getMessage()); return "host:" + host + " " + " port:" + port; } } }