From 481e177d71685ccf9cad9dcd7560562181227742 Mon Sep 17 00:00:00 2001 From: betasteward Date: Mon, 1 Jun 2015 10:51:20 -0400 Subject: [PATCH] added pingInfo to user info --- .../src/main/java/mage/client/MageFrame.java | 42 ++++++++++++++++++- .../main/java/org/mage/network/Client.java | 2 +- .../main/java/org/mage/network/Server.java | 30 ++++++++----- .../handlers/server/HeartbeatHandler.java | 27 +++++++++++- .../mage/network/interfaces/MageServer.java | 2 + .../src/main/java/mage/server/Main.java | 10 +++++ .../src/main/java/mage/server/Session.java | 31 ++++++++++++++ .../main/java/mage/server/SessionManager.java | 7 ++++ .../main/java/mage/server/TableManager.java | 2 +- .../src/main/java/mage/server/User.java | 14 +++---- .../main/java/mage/server/UserManager.java | 4 ++ .../java/mage/server/game/GameController.java | 5 ++- .../java/mage/server/game/GamesRoomImpl.java | 7 +++- 13 files changed, 159 insertions(+), 24 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 542a99aef6f..6cc05373d1f 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -84,6 +84,7 @@ import java.awt.image.BufferedImage; import java.beans.PropertyVetoException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -92,6 +93,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.prefs.Preferences; +import mage.cards.repository.ExpansionInfo; +import mage.cards.repository.ExpansionRepository; import mage.client.util.audio.AudioManager; import mage.interfaces.ServerState; import mage.view.ChatMessage; @@ -140,7 +143,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { private static final Map drafts = new HashMap<>(); private static final MageUI ui = new MageUI(); - private static final ScheduledExecutorService pingTaskExecutor = Executors.newSingleThreadScheduledExecutor(); +// private static final ScheduledExecutorService pingTaskExecutor = Executors.newSingleThreadScheduledExecutor(); private static UpdateMemUsageTask updateMemUsageTask; private static long startTime; @@ -700,9 +703,46 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { public boolean connect(Connection connection) { client = new Client(instance); boolean result = client.connect(connection.getUsername(), connection.getHost(), connection.getPort(), true, version); + if (result) { + updateDatabase(connection.isForceDBComparison(), serverState); + } return result; } + private void updateDatabase(boolean forceDBComparison, ServerState serverState) { + long cardDBVersion = CardRepository.instance.getContentVersionFromDB(); + if (forceDBComparison || serverState.getCardsContentVersion() > cardDBVersion) { + List classNames = CardRepository.instance.getClassNames(); + List cards = CardRepository.instance.getMissingCards(classNames); + CardRepository.instance.addCards(cards); + CardRepository.instance.setContentVersion(serverState.getCardsContentVersion()); + logger.info("Updating client cards DB - existing cards: " + classNames.size() + " new cards: " + cards.size() + + " content versions - server: " + serverState.getCardsContentVersion() + " client: " + cardDBVersion); + } + + long expansionDBVersion = ExpansionRepository.instance.getContentVersionFromDB(); + if (forceDBComparison || serverState.getExpansionsContentVersion() > expansionDBVersion) { + List setCodes = ExpansionRepository.instance.getSetCodes(); + List expansions = getMissingExpansionData(setCodes); + for (ExpansionInfo expansion : expansions) { + ExpansionRepository.instance.add(expansion); + } + ExpansionRepository.instance.setContentVersion(serverState.getExpansionsContentVersion()); + logger.info("Updating client expansions DB - existing sets: " + setCodes.size() + " new sets: " + expansions.size()+ + " content versions - server: " + serverState.getExpansionsContentVersion() + " client: " + expansionDBVersion); + } + } + + private List getMissingExpansionData(List codes) { + List result = new ArrayList<>(); + for (ExpansionInfo expansionInfo : ExpansionRepository.instance.getAll()) { + if (!codes.contains(expansionInfo.getCode())) { + result.add(expansionInfo); + } + } + return result; + } + // public static boolean stopConnecting() { // return session.stopConnecting(); // } diff --git a/Mage.Network/src/main/java/org/mage/network/Client.java b/Mage.Network/src/main/java/org/mage/network/Client.java index 46ca55dab8f..371826ad1cf 100644 --- a/Mage.Network/src/main/java/org/mage/network/Client.java +++ b/Mage.Network/src/main/java/org/mage/network/Client.java @@ -216,7 +216,7 @@ public class Client { } public String getSessionId() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + return channel.id().asLongText(); } public TableView createTournamentTable(UUID roomId, TournamentOptions tOptions) { diff --git a/Mage.Network/src/main/java/org/mage/network/Server.java b/Mage.Network/src/main/java/org/mage/network/Server.java index 9d50da63d26..aff1012fba5 100644 --- a/Mage.Network/src/main/java/org/mage/network/Server.java +++ b/Mage.Network/src/main/java/org/mage/network/Server.java @@ -36,6 +36,7 @@ import org.mage.network.handlers.server.ServerMessageHandler; import org.mage.network.interfaces.MageServer; import org.mage.network.model.InformClientMessage; import org.mage.network.model.MessageType; +import org.mage.network.model.PingMessage; import org.mage.network.model.ReceiveChatMessage; /** @@ -48,12 +49,13 @@ public class Server { private static final int IDLE_PING_TIME = 30; private static final int IDLE_TIMEOUT = 60; + private static final PingMessage ping = new PingMessage(); public static final ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); private SslContext sslCtx; - private final HeartbeatHandler heartbeatHandler; + private final MageServer server; private final PingMessageHandler pingMessageHandler = new PingMessageHandler(); private final EventExecutorGroup handlersExecutor = new DefaultEventExecutorGroup(Runtime.getRuntime().availableProcessors() * 2); private final RegisterClientMessageHandler registerClientMessageHandler; @@ -66,7 +68,7 @@ public class Server { private final RoomMessageHandler roomMessageHandler; public Server(MageServer server) { - heartbeatHandler = new HeartbeatHandler(server); + this.server = server; registerClientMessageHandler = new RegisterClientMessageHandler(server); chatMessageHandler = new ChatMessageHandler(server); joinChatMessageHandler = new JoinChatMessageHandler(server); @@ -117,18 +119,18 @@ public class Server { ch.pipeline().addLast(new ObjectEncoder()); ch.pipeline().addLast("idleStateHandler", new IdleStateHandler(IDLE_TIMEOUT, IDLE_PING_TIME, 0)); - ch.pipeline().addLast(handlersExecutor, heartbeatHandler); + ch.pipeline().addLast(handlersExecutor, "heartbeatHandler", new HeartbeatHandler(server)); ch.pipeline().addLast("pingMessageHandler", pingMessageHandler); ch.pipeline().addLast("connectionHandler", new ConnectionHandler()); - ch.pipeline().addLast(handlersExecutor, registerClientMessageHandler); + ch.pipeline().addLast(handlersExecutor, "registerClientMessageHandler", registerClientMessageHandler); - ch.pipeline().addLast(handlersExecutor, chatRoomIdHandler); - ch.pipeline().addLast(handlersExecutor, chatMessageHandler); - ch.pipeline().addLast(handlersExecutor, joinChatMessageHandler); - ch.pipeline().addLast(handlersExecutor, leaveChatMessageHandler); - ch.pipeline().addLast(handlersExecutor, serverMessageHandler); - ch.pipeline().addLast(handlersExecutor, roomMessageHandler); + ch.pipeline().addLast(handlersExecutor, "chatRoomIdHandler", chatRoomIdHandler); + ch.pipeline().addLast(handlersExecutor, "chatMessageHandler", chatMessageHandler); + ch.pipeline().addLast(handlersExecutor, "joinChatMessageHandler", joinChatMessageHandler); + ch.pipeline().addLast(handlersExecutor, "leaveChatMessageHandler", leaveChatMessageHandler); + ch.pipeline().addLast(handlersExecutor, "serverMessageHandler", serverMessageHandler); + ch.pipeline().addLast(handlersExecutor, "roomMessageHandler", roomMessageHandler); } } @@ -158,4 +160,12 @@ public class Server { clients.writeAndFlush(new InformClientMessage(message, type)); } + public void pingClient(String sessionId) { + Channel ch = findChannel(sessionId); + if (ch != null) { + HeartbeatHandler heartbeatHandler = (HeartbeatHandler)ch.pipeline().get("heartbeatHandler"); + heartbeatHandler.pingClient(); + } + } + } diff --git a/Mage.Network/src/main/java/org/mage/network/handlers/server/HeartbeatHandler.java b/Mage.Network/src/main/java/org/mage/network/handlers/server/HeartbeatHandler.java index bad59ac627d..0011c9630d9 100644 --- a/Mage.Network/src/main/java/org/mage/network/handlers/server/HeartbeatHandler.java +++ b/Mage.Network/src/main/java/org/mage/network/handlers/server/HeartbeatHandler.java @@ -5,28 +5,38 @@ import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleStateEvent; +import java.util.concurrent.TimeUnit; import mage.remote.DisconnectReason; import org.apache.log4j.Logger; import org.mage.network.interfaces.MageServer; import org.mage.network.model.PingMessage; +import org.mage.network.model.PongMessage; /** * * @author BetaSteward */ -@Sharable public class HeartbeatHandler extends ChannelHandlerAdapter { private static final Logger logger = Logger.getLogger(HeartbeatHandler.class); private static PingMessage ping = new PingMessage(); + private ChannelHandlerContext ctx; + private long startTime; + private final MageServer server; public HeartbeatHandler (MageServer server) { this.server = server; } + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + this.ctx = ctx; + super.channelActive(ctx); + } + @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { @@ -36,9 +46,24 @@ public class HeartbeatHandler extends ChannelHandlerAdapter { ctx.disconnect(); logger.info("Disconnected due to extended idle"); } else if (e.state() == IdleState.WRITER_IDLE) { + startTime = System.nanoTime(); ctx.writeAndFlush(ping); logger.info("Sending ping"); } } } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + if (msg instanceof PongMessage) { + long milliSeconds = TimeUnit.MILLISECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS); + server.pingTime(milliSeconds, ctx.channel().id().asLongText()); + } + ctx.fireChannelRead(msg); + } + + public void pingClient() { + startTime = System.nanoTime(); + ctx.writeAndFlush(ping); + } } diff --git a/Mage.Network/src/main/java/org/mage/network/interfaces/MageServer.java b/Mage.Network/src/main/java/org/mage/network/interfaces/MageServer.java index 7c199d25814..86afda53c1c 100644 --- a/Mage.Network/src/main/java/org/mage/network/interfaces/MageServer.java +++ b/Mage.Network/src/main/java/org/mage/network/interfaces/MageServer.java @@ -26,5 +26,7 @@ public interface MageServer { List getServerMessages(); RoomView getRoom(UUID roomId); + + void pingTime(long milliSeconds, String sessionId); } diff --git a/Mage.Server/src/main/java/mage/server/Main.java b/Mage.Server/src/main/java/mage/server/Main.java index a98950bd5d6..a115bbce19f 100644 --- a/Mage.Server/src/main/java/mage/server/Main.java +++ b/Mage.Server/src/main/java/mage/server/Main.java @@ -491,6 +491,16 @@ public class Main implements MageServer { // public boolean ping(String sessionId, String pingInfo) { // return SessionManager.getInstance().extendUserSession(sessionId, pingInfo); // } + + public void pingClient(String sessionId) { + server.pingClient(sessionId); + } + + @Override + public void pingTime(long milliSeconds, String sessionId) { + SessionManager.getInstance().recordPingTime(sessionId, milliSeconds); + } + // // @Override // public void deregisterClient(final String sessionId) throws MageException { diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index 6ef5b543480..cc9bbf6e549 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -33,6 +33,8 @@ import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Matcher; @@ -57,6 +59,7 @@ import org.jboss.remoting.callback.InvokerCallbackHandler; public class Session { private static final Logger logger = Logger.getLogger(Session.class); + private static final ScheduledExecutorService pingTaskExecutor = Executors.newScheduledThreadPool(10); private final String sessionId; private UUID userId; @@ -64,6 +67,10 @@ public class Session { private int messageId = 0; private final Date timeConnected; private boolean isAdmin = false; + private final static int PING_CYCLES = 10; + private final LinkedList pingTime = new LinkedList<>(); + private String pingInfo = ""; + // private final AsynchInvokerCallbackHandler callbackHandler; private final ReentrantLock lock; @@ -82,6 +89,12 @@ public class Session { // sendErrorMessageToClient(returnMessage); // } // return returnMessage; + pingTaskExecutor.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + Main.getInstance().pingClient(sessionId); + } + }, 10, 60, TimeUnit.SECONDS); return registerUserHandling(userName); } @@ -260,6 +273,7 @@ public class Session { logger.error("SESSION LOCK - kill: userId " + userId); } UserManager.getInstance().removeUser(userId, reason); + pingTime.clear(); } catch (InterruptedException ex) { logger.error("SESSION LOCK - kill: userId " + userId, ex); } @@ -324,4 +338,21 @@ public class Session { } return t; } + + public void recordPingTime(long milliSeconds) { + pingTime.add(milliSeconds); + String lastPing = milliSeconds > 0 ? milliSeconds+"ms" : "<1ms"; + if (pingTime.size() > PING_CYCLES) { + pingTime.poll(); + } + long sum = 0; + for (Long time :pingTime) { + sum += time; + } + pingInfo = lastPing + " (Av: " + (milliSeconds > 0 ? milliSeconds + "ms":"<1ms")+")"; + } + + public String getPingInfo() { + return pingInfo; + } } diff --git a/Mage.Server/src/main/java/mage/server/SessionManager.java b/Mage.Server/src/main/java/mage/server/SessionManager.java index 3c59c76dc09..cb71a73afa3 100644 --- a/Mage.Server/src/main/java/mage/server/SessionManager.java +++ b/Mage.Server/src/main/java/mage/server/SessionManager.java @@ -223,4 +223,11 @@ public class SessionManager { // } // return false; // } + + void recordPingTime(String sessionId, long milliSeconds) { + Session session = sessions.get(sessionId); + if (session != null) { + session.recordPingTime(milliSeconds); + } + } } diff --git a/Mage.Server/src/main/java/mage/server/TableManager.java b/Mage.Server/src/main/java/mage/server/TableManager.java index 385634738f9..3c8c3a49e5d 100644 --- a/Mage.Server/src/main/java/mage/server/TableManager.java +++ b/Mage.Server/src/main/java/mage/server/TableManager.java @@ -375,7 +375,7 @@ public class TableManager { logger.debug(user.getId() + " | " + formatter.format(user.getConnectionTime()) + " | " + sessionState - + " | " + user.getName() +" (" +user.getUserState().toString() + " - " + user.getPingInfo() + ")"); + + " | " + user.getName() +" (" +user.getUserState().toString() + " - " + session.getPingInfo() + ")"); } ArrayList chatSessions = ChatManager.getInstance().getChatSessions(); logger.debug("------- ChatSessions: " + chatSessions.size() + " ----------------------------------"); diff --git a/Mage.Server/src/main/java/mage/server/User.java b/Mage.Server/src/main/java/mage/server/User.java index 61a4b5d452a..43e8ac3deac 100644 --- a/Mage.Server/src/main/java/mage/server/User.java +++ b/Mage.Server/src/main/java/mage/server/User.java @@ -512,12 +512,12 @@ public class User { return userState; } - public String getPingInfo() { - if (isConnected()) { - return pingInfo; - } else { - return " (discon. "+ getDisconnectDuration() + ")"; - } - } +// public String getPingInfo() { +// if (isConnected()) { +// return pingInfo; +// } else { +// return " (discon. "+ getDisconnectDuration() + ")"; +// } +// } } diff --git a/Mage.Server/src/main/java/mage/server/UserManager.java b/Mage.Server/src/main/java/mage/server/UserManager.java index 91743e718fb..0a5363272ff 100644 --- a/Mage.Server/src/main/java/mage/server/UserManager.java +++ b/Mage.Server/src/main/java/mage/server/UserManager.java @@ -197,4 +197,8 @@ public class UserManager { logger.fatal("User manager exception - null"); } } + + void recordPingTime(UUID userId, long milliSeconds) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index 0b7beada630..aecc0affbcb 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -73,6 +73,8 @@ import mage.interfaces.Action; import mage.players.Player; import mage.server.ChatManager; import mage.server.Main; +import mage.server.Session; +import mage.server.SessionManager; import mage.server.TableManager; import mage.server.User; import mage.server.UserManager; @@ -363,7 +365,8 @@ public class GameController implements GameCallback { GameManager.getInstance().joinGame(game.getId(), user.getId()); logger.debug("Player " + player.getLogName() + " (disconnected) has joined gameId: " +game.getId()); } - ChatManager.getInstance().broadcast(chatId, user, user.getPingInfo() + " is pending to join the game", MessageColor.BLUE, true, ChatMessage.MessageType.STATUS); + Session session = SessionManager.getInstance().getSession(user.getSessionId()); + ChatManager.getInstance().broadcast(chatId, user, session.getPingInfo() + " is pending to join the game", MessageColor.BLUE, true, ChatMessage.MessageType.STATUS); if (user.getSecondsDisconnected() > 240) { // Cancel player join possibility lately after 4 minutes logger.debug("Player " + player.getLogName() + " - canceled game (after 240 seconds) gameId: " +game.getId()); diff --git a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java index d434343a869..2295d0950ce 100644 --- a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java +++ b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java @@ -46,6 +46,8 @@ import mage.game.Table; import mage.game.match.MatchOptions; import mage.game.tournament.TournamentOptions; import mage.server.RoomImpl; +import mage.server.Session; +import mage.server.SessionManager; import mage.server.TableManager; import mage.server.User; import mage.server.UserManager; @@ -119,11 +121,12 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable { matchView = matchList; List users = new ArrayList<>(); for (User user : UserManager.getInstance().getUsers()) { + Session session = SessionManager.getInstance().getSession(user.getSessionId()); try { - users.add(new UsersView(user.getName(), user.getInfo(), user.getGameInfo(), user.getPingInfo())); + users.add(new UsersView(user.getName(), user.getInfo(), user.getGameInfo(), session.getPingInfo())); } catch (Exception ex) { logger.fatal("User update exception: " + user.getName() + " - " + ex.toString(), ex); - users.add(new UsersView(user.getName(), user.getInfo(), "[exception]", user.getPingInfo())); + users.add(new UsersView(user.getName(), user.getInfo(), "[exception]", session.getPingInfo())); } }