Added connection speed information per user (milliseconds the ping needs). Some minor changes to server console.

This commit is contained in:
LevelX2 2014-08-31 17:46:14 +02:00
parent 1285df5da3
commit b98c16f061
20 changed files with 166 additions and 54 deletions

View file

@ -1233,17 +1233,17 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setStatusText("Not connected");
disableButtons();
hideGames();
hideTables();
if (JOptionPane.showConfirmDialog(MageFrame.this, "The connection to server was lost. Reconnect?", "Warning", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
session.disconnect(false);
tablesPane.clearChat();
// session.disconnect(false);
// tablesPane.clearChat();
if (performConnect()) {
enableButtons();
}
} else {
setStatusText("Not connected");
hideGames();
hideTables();
// } else {
}
}
});

View file

@ -46,6 +46,7 @@ import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableRowSorter;
import mage.client.MageFrame;
import mage.remote.MageRemoteException;
import mage.remote.Session;
@ -152,6 +153,7 @@ public class ChatPanel extends javax.swing.JPanel {
initComponents();
jTablePlayers.setBackground(new Color(0, 0, 0, 0));
jTablePlayers.setForeground(Color.white);
jTablePlayers.setRowSorter(new TableRowSorter(tableModel));
setBackground(new Color(0, 0, 0, 100));
if (jScrollPaneTxt != null) {
jScrollPaneTxt.setBackground(new Color(0, 0, 0, 100));
@ -301,7 +303,7 @@ public class ChatPanel extends javax.swing.JPanel {
class TableModel extends AbstractTableModel {
private final String[] columnNames = new String[]{"Players", "Info", "Games"};
private final String[] columnNames = new String[]{"Players", "Info", "Games", "Connection"};
private UsersView[] players = new UsersView[0];
public void loadData(Collection<RoomUsersView> roomUserInfoList) throws MageRemoteException {
@ -334,6 +336,8 @@ public class ChatPanel extends javax.swing.JPanel {
return players[arg0].getInfoState();
case 2:
return players[arg0].getInfoGames();
case 3:
return players[arg0].getInfoPing();
}
return "";
}

View file

@ -74,7 +74,7 @@ public final class Constants {
public static final int PRIORITY_TIME_SEC = 1200;
public enum SessionState {
DISCONNECTED, CONNECTED, CONNECTING, DISCONNECTING, SERVER_UNAVAILABLE, SERVER_STARTING;
DISCONNECTED, CONNECTED, CONNECTING, DISCONNECTING, SERVER_STARTING;
}
public enum Option {

View file

@ -47,7 +47,6 @@ import mage.view.TournamentView;
import mage.view.UserDataView;
import mage.view.RoomUsersView;
import mage.view.UserView;
import mage.view.UsersView;
/**
*
@ -76,7 +75,7 @@ public interface MageServer {
Object getServerMessagesCompressed(String sessionId) throws MageException; // messages of the day
// ping - extends session
boolean ping(String sessionId) throws MageException;
boolean ping(String sessionId, String pingInfo) throws MageException;
//table methods
TableView createTable(String sessionId, UUID roomId, MatchOptions matchOptions) throws MageException;

View file

@ -57,6 +57,8 @@ import org.jboss.remoting.transporter.TransporterClient;
import java.net.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
*
@ -75,7 +77,9 @@ public class SessionImpl implements Session {
private ServerState serverState;
private SessionState sessionState = SessionState.DISCONNECTED;
private Connection connection;
private final static int PING_CYCLES = 10;
private final LinkedList<Long> pingTime = new LinkedList<>();
private String pingInfo = "";
private static boolean debugMode = false;
private boolean canceled = false;
@ -303,11 +307,11 @@ public class SessionImpl implements Session {
}
@Override
public synchronized void disconnect(boolean showMessage) {
public synchronized void disconnect(boolean errorCall) {
if (isConnected()) {
sessionState = SessionState.DISCONNECTING;
}
if (connection == null) {
if (connection == null || sessionState == SessionState.DISCONNECTED) {
return;
}
try {
@ -320,11 +324,12 @@ public class SessionImpl implements Session {
if (sessionState == SessionState.DISCONNECTING || sessionState == SessionState.CONNECTING) {
sessionState = SessionState.DISCONNECTED;
logger.info("Disconnected ... ");
}
client.disconnected();
if (showMessage) {
if (errorCall) {
client.showError("Network error. You have been disconnected");
}
pingTime.clear();
}
}
@Override
@ -1305,7 +1310,6 @@ public class SessionImpl implements Session {
private void handleThrowable(Throwable t) {
logger.fatal("Communication error", t);
sessionState = SessionState.SERVER_UNAVAILABLE;
disconnect(true);
}
@ -1350,9 +1354,23 @@ public class SessionImpl implements Session {
public boolean ping() {
try {
if (isConnected()) {
if (!server.ping(sessionId)) {
long startTime = System.nanoTime();
if (!server.ping(sessionId, pingInfo)) {
logger.error(new StringBuilder("Ping failed: ").append(this.getUserName()).append(" Session: ").append(sessionId).append(" to MAGE server at ").append(connection.getHost()).append(":").append(connection.getPort()).toString());
throw new MageException("Ping failed");
}
pingTime.add(System.nanoTime() - startTime);
long milliSeconds = TimeUnit.MILLISECONDS.convert(pingTime.getLast(), TimeUnit.NANOSECONDS);
String lastPing = milliSeconds > 0 ? milliSeconds+"ms" : "<1ms";
if (pingTime.size() > PING_CYCLES) {
pingTime.poll();
}
long sum = 0;
for (Long time :pingTime) {
sum += time;
}
milliSeconds = TimeUnit.MILLISECONDS.convert(sum / pingTime.size(), TimeUnit.NANOSECONDS);
pingInfo = lastPing + " (Av: " + (milliSeconds > 0 ? milliSeconds + "ms":"<1ms")+")";
}
return true;
} catch (MageException ex) {

View file

@ -31,6 +31,7 @@ import java.io.Serializable;
import java.util.Date;
/**
* Admin Console View
*
* @author BetaSteward_at_googlemail.com
*/
@ -40,12 +41,14 @@ public class UserView implements Serializable {
private final String host;
private final String sessionId;
private final Date timeConnected;
private final String gameInfo;
public UserView(String userName, String host, String sessionId, Date timeConnected) {
public UserView(String userName, String host, String sessionId, Date timeConnected, String gameInfo) {
this.userName = userName;
this.host = host;
this.sessionId = sessionId;
this.timeConnected = timeConnected;
this.gameInfo = gameInfo;
}
public String getUserName() {
@ -64,4 +67,8 @@ public class UserView implements Serializable {
return timeConnected;
}
public String getGameInfo() {
return gameInfo;
}
}

View file

@ -40,11 +40,13 @@ public class UsersView implements Serializable {
private final String userName;
private final String infoState;
private final String infoGames;
private final String infoPing;
public UsersView(String userName, String infoState, String infoGames) {
public UsersView(String userName, String infoState, String infoGames, String infoPing) {
this.userName = userName;
this.infoState = infoState;
this.infoGames = infoGames;
this.infoPing = infoPing;
}
public String getUserName() {
@ -59,4 +61,8 @@ public class UsersView implements Serializable {
return infoGames;
}
public String getInfoPing() {
return infoPing;
}
}

View file

@ -487,7 +487,7 @@ public class ConnectDialog extends JDialog {
private void findPublicServerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
BufferedReader in = null;
try {
URL serverListURL = new URL("http://download.magefree.com/files/server-list.txt");
URL serverListURL = new URL("http://XMage.info/files/server-list.txt");
in = new BufferedReader(new InputStreamReader(serverListURL.openStream()));
List<String> servers = new ArrayList<>();

View file

@ -39,6 +39,11 @@ import java.awt.event.WindowEvent;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.prefs.Preferences;
import javax.swing.Box;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import mage.interfaces.MageClient;
import mage.interfaces.callback.ClientCallback;
import mage.remote.Connection;
@ -47,9 +52,6 @@ import mage.remote.SessionImpl;
import mage.utils.MageVersion;
import org.apache.log4j.Logger;
import javax.swing.*;
import java.util.prefs.Preferences;
/**
*
* @author BetaSteward_at_googlemail.com
@ -121,7 +123,7 @@ public class ConsoleFrame extends javax.swing.JFrame implements MageClient {
public void enableButtons() {
btnConnect.setEnabled(true);
btnConnect.setText("Disconnect");
btnConnect.setText("Disconnect & Close");
btnSendMessage.setEnabled(true);
}

View file

@ -40,8 +40,11 @@ import java.util.List;
import java.util.UUID;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import static javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN;
import static javax.swing.JTable.AUTO_RESIZE_OFF;
import javax.swing.SwingWorker;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;
import mage.remote.Session;
import mage.view.TableView;
import mage.view.UserView;
@ -66,7 +69,12 @@ public class ConsolePanel extends javax.swing.JPanel {
this.tableTableModel = new TableTableModel();
initComponents();
this.tblUsers.createDefaultColumnsFromModel();
this.tblUsers.setRowSorter(new TableRowSorter(tableUserModel));
this.tblUsers.setAutoResizeMode(AUTO_RESIZE_OFF);
this.tblTables.createDefaultColumnsFromModel();
this.tblTables.setRowSorter(new TableRowSorter(tableTableModel));
this.tblUsers.setAutoResizeMode(AUTO_RESIZE_NEXT_COLUMN);
}
public void update(List<UserView> users) {
@ -259,17 +267,17 @@ public class ConsolePanel extends javax.swing.JPanel {
}// </editor-fold>//GEN-END:initComponents
private void btnDisconnectActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDisconnectActionPerformed
int row = this.tblUsers.getSelectedRow();
int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow());
ConsoleFrame.getSession().disconnectUser((String)tableUserModel.getValueAt(row, 3));
}//GEN-LAST:event_btnDisconnectActionPerformed
private void btnRemoveTableActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRemoveTableActionPerformed
int row = this.tblTables.getSelectedRow();
int row = this.tblTables.convertRowIndexToModel(tblTables.getSelectedRow());
ConsoleFrame.getSession().removeTable((UUID)tableTableModel.getValueAt(row, 7));
}//GEN-LAST:event_btnRemoveTableActionPerformed
private void btnEndSessionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnEndSessionActionPerformed
int row = this.tblUsers.getSelectedRow();
int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow());
ConsoleFrame.getSession().endUserSession((String) tableUserModel.getValueAt(row, 3));
}//GEN-LAST:event_btnEndSessionActionPerformed
@ -292,7 +300,7 @@ public class ConsolePanel extends javax.swing.JPanel {
}
class TableUserModel extends AbstractTableModel {
private final String[] columnNames = new String[]{"User Name", "Host", "Time Connected"};
private final String[] columnNames = new String[]{"User Name", "Host", "Time Connected", "SessionId", "Gameinfo"};
private UserView[] users = new UserView[0];
private static final DateFormat formatter = new SimpleDateFormat("HH:mm:ss");
@ -322,6 +330,8 @@ class TableUserModel extends AbstractTableModel {
return formatter.format(users[arg0].getConnectionTime());
case 3:
return users[arg0].getSessionId();
case 4:
return users[arg0].getGameInfo();
}
return "";
}
@ -438,12 +448,13 @@ class UpdateUsersTask extends SwingWorker<Void, List<UserView>> {
protected Void doInBackground() throws Exception {
while (!isCancelled()) {
List<UserView> users = session.getUsers();
if (previousUsers == null || checkUserListChanged(users)) {
logger.debug("Need to update the user list");
this.publish(users);
previousUsers = users;
}
Thread.sleep(1000);
Thread.sleep(2000);
}
return null;
}
@ -508,7 +519,7 @@ class UpdateTablesTask extends SwingWorker<Void, Collection<TableView>> {
protected Void doInBackground() throws Exception {
while (!isCancelled()) {
this.publish(session.getTables(roomId));
Thread.sleep(1000);
Thread.sleep(3000);
}
return null;
}

View file

@ -93,6 +93,12 @@ public class ChatSession {
case SessionExpired:
message = " session expired";
break;
case AdminDisconnect:
message = " was disconnected by the Admin";
break;
case ConnectingOtherInstance:
message = " reconnected and replaced still active old session";
break;
case CleaningUp:
message = null;
break;

View file

@ -330,8 +330,8 @@ public class MageServerImpl implements MageServer {
}
@Override
public boolean ping(String sessionId) {
return SessionManager.getInstance().extendUserSession(sessionId);
public boolean ping(String sessionId, String pingInfo) {
return SessionManager.getInstance().extendUserSession(sessionId, pingInfo);
}
// @Override
@ -959,6 +959,13 @@ public class MageServerImpl implements MageServer {
});
}
/**
* Get user data for admin console
*
* @param sessionId
* @return
* @throws MageException
*/
@Override
public List<UserView> getUsers(String sessionId) throws MageException {
return executeWithResult("getUsers", sessionId, new ActionWithNullNegativeResult<List<UserView>>() {
@ -966,7 +973,8 @@ public class MageServerImpl implements MageServer {
public List<UserView> execute() throws MageException {
List<UserView> users = new ArrayList<>();
for (User user : UserManager.getInstance().getUsers()) {
users.add(new UserView(user.getName(), user.getHost(), user.getSessionId(), user.getConnectionTime()));
users.add(new UserView(user.getName(), user.getHost(), user.getSessionId(), user.getConnectionTime(), user.getGameInfo()));
}
return users;
}

View file

@ -99,7 +99,7 @@ public class Session {
if (user == null) { // user already exists
user = UserManager.getInstance().findUser(userName);
if (user.getHost().equals(host)) {
user.updateLastActivity(); // minimizes possible expiration
user.updateLastActivity(null); // minimizes possible expiration
this.userId = user.getId();
if (user.getSessionId().isEmpty()) {
logger.info("Reconnecting session for " + userName);

View file

@ -152,12 +152,34 @@ public class SessionManager {
return map;
}
/**
* Admin requested the disconnect of a user
* @param sessionId
* @param userSessionId
*/
public void disconnectUser(String sessionId, String userSessionId) {
if (isAdmin(sessionId)) {
User userAdmin, user;
if ((userAdmin = getUserFromSession(sessionId)) != null) {
if ((user = getUserFromSession(userSessionId)) != null) {
user.showUserMessage("Admin operation","Your session was disconnected by Admin.");
userAdmin.showUserMessage("Admin action", "User" + user.getName() + " was disconnected.");
disconnect(userSessionId, DisconnectReason.AdminDisconnect);
LogServiceImpl.instance.log(LogKeys.KEY_SESSION_DISCONNECTED_BY_ADMIN, sessionId, userSessionId);
} else {
userAdmin.showUserMessage("Admin operation","User with sessionId " + userSessionId + " could not be found!");
}
}
}
}
private User getUserFromSession(String sessionId) {
Session session = getSession(sessionId);
if (session == null) {
return null;
}
return UserManager.getInstance().getUser(session.getUserId());
}
public void endUserSession(String sessionId, String userSessionId) {
if (isAdmin(sessionId)) {
@ -186,10 +208,10 @@ public class SessionManager {
return null;
}
public boolean extendUserSession(String sessionId) {
public boolean extendUserSession(String sessionId, String pingInfo) {
Session session = sessions.get(sessionId);
if (session != null) {
return UserManager.getInstance().extendUserSession(session.getUserId());
return UserManager.getInstance().extendUserSession(session.getUserId(), pingInfo);
}
return false;
}

View file

@ -34,6 +34,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
@ -51,6 +52,7 @@ import mage.game.match.MatchOptions;
import mage.game.tournament.Tournament;
import mage.game.tournament.TournamentOptions;
import mage.players.Player;
import mage.server.game.GameController;
import mage.server.game.GameManager;
import mage.server.game.GamesRoomManager;
import org.apache.log4j.Logger;
@ -379,11 +381,17 @@ public class TableManager {
for (Table table: tables.values()) {
logger.debug(table.getId() + " [" + table.getName()+ "] " + formatter.format(table.getStartTime()) +" (" + table.getState().toString() + ")");
}
logger.debug("------- Games: " + GameManager.getInstance().getNumberActiveGames() + " --------------------------------------------");
for (Entry<UUID, GameController> entry: GameManager.getInstance().getGameController().entrySet()) {
logger.debug(entry.getKey() + entry.getValue().getPlayerNameList());
}
logger.debug("--- Server state END ------------------------------------------");
}
private void checkExpired() {
if (logger.isDebugEnabled()) {
debugServerState();
}
Date now = new Date();
List<UUID> toRemove = new ArrayList<>();
for (Table table : tables.values()) {

View file

@ -75,7 +75,8 @@ public class User {
private final Map<UUID, Deck> sideboarding;
private final List<UUID> watchedGames;
private String sessionId;
private String info;
private String info = "";
private String pingInfo = "";
private Date lastActivity;
private UserState userState;
private UserData userData;
@ -238,7 +239,10 @@ public class User {
GameManager.getInstance().sendPlayerInteger(gameId, userId, data);
}
public void updateLastActivity() {
public void updateLastActivity(String pingInfo) {
if (pingInfo != null) {
this.pingInfo = pingInfo;
}
lastActivity = new Date();
if (userState == UserState.Disconnected) { // this can happen if user reconnects very fast after disconnect
userState = UserState.Reconnected;
@ -458,4 +462,8 @@ public class User {
return userState;
}
public String getPingInfo() {
return pingInfo;
}
}

View file

@ -145,11 +145,11 @@ public class UserManager {
}
}
public boolean extendUserSession(UUID userId) {
public boolean extendUserSession(UUID userId, String pingInfo) {
if (userId != null) {
User user = users.get(userId);
if (user != null) {
user.updateLastActivity();
user.updateLastActivity(pingInfo);
return true;
}
}

View file

@ -826,4 +826,17 @@ public class GameController implements GameCallback {
}
return gameSessions.get(playerId);
}
public String getPlayerNameList() {
StringBuilder sb = new StringBuilder(" [");
for (UUID playerId: userPlayerMap.values()) {
Player player = game.getPlayer(playerId);
if (player != null) {
sb.append(player.getName()).append("(Left=").append(player.hasLeft() ? "Y":"N").append(") ");
} else {
sb.append("player missing: ").append(playerId).append(" ");
}
}
return sb.append("]").toString();
}
}

View file

@ -62,10 +62,6 @@ public class GameManager {
}
}
// public void destroyChatSession(UUID gameId) {
// gameControllers.remove(gameId);
// }
public UUID getChatId(UUID gameId) {
if (gameControllers.containsKey(gameId)) {
return gameControllers.get(gameId).getChatId();
@ -213,4 +209,8 @@ public class GameManager {
public int getNumberActiveGames() {
return gameControllers.size();
}
public ConcurrentHashMap<UUID, GameController> getGameController() {
return gameControllers;
}
}

View file

@ -118,10 +118,10 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
List<UsersView> users = new ArrayList<>();
for (User user : UserManager.getInstance().getUsers()) {
try {
users.add(new UsersView(user.getName(), user.getInfo(), user.getGameInfo()));
users.add(new UsersView(user.getName(), user.getInfo(), user.getGameInfo(), user.getPingInfo()));
} catch (Exception ex) {
logger.fatal("User update exception: " + user.getName() + " - " + ex.toString(), ex);
users.add(new UsersView(user.getName(), user.getInfo(), "[exception]"));
users.add(new UsersView(user.getName(), user.getInfo(), "[exception]", user.getPingInfo()));
}
}