Network upgrade and new reconnection mode (#11527)

Network upgrade and new reconnection mode:
* users can disconnect or close app without game progress loose now;
* disconnect dialog will show active tables stats and additional options;
* all active tables will be restored on reconnect (tables, tourneys, games, drafts, sideboarding, constructing);
* user must use same server and username on next connection;
* there are few minutes for reconnect until server kick off a disconnected player from all player's tables (concede/loose);
* now you can safety reconnect after IP change (after proxy/vpn/wifi/router restart);

Other improvements and fixes:
* gui: main menu - improved switch panel button, added stats about current tables/panels;
* gui: improved data sync and updates (fixes many use cases with empty battlefield, not started games/drafts/tourneys, not updatable drafts, etc);
* gui: improved stability on game updates (fixes some random errors related to wrong threads);
* server: fixed miss messages about player's disconnection problems for other players in the chat;
* refactor: simplified and improved connection and network related code, deleted outdated code, added docs;
* tests: improved load test to support lands only set for more stable performance/network testing (set TEST_AI_RANDOM_DECK_SETS = PELP and run test_TwoAIPlayGame_Multiple);
This commit is contained in:
Oleg Agafonov 2023-12-07 19:56:52 +03:00 committed by GitHub
parent 7f0558ff3c
commit 960e896903
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
71 changed files with 1274 additions and 802 deletions

View file

@ -14,8 +14,8 @@ import mage.game.tournament.TournamentOptions;
import mage.game.tournament.TournamentPlayer;
import mage.players.PlayerType;
import mage.server.game.GameController;
import mage.server.managers.TableManager;
import mage.server.managers.ManagerFactory;
import mage.server.managers.TableManager;
import org.apache.log4j.Logger;
import java.text.DateFormat;
@ -23,9 +23,6 @@ import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@ -34,7 +31,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
* @author BetaSteward_at_googlemail.com
*/
public class TableManagerImpl implements TableManager {
protected final ScheduledExecutorService expireExecutor = Executors.newSingleThreadScheduledExecutor();
private final ManagerFactory managerFactory;
private final Logger logger = Logger.getLogger(TableManagerImpl.class);
@ -46,22 +42,11 @@ public class TableManagerImpl implements TableManager {
private final ConcurrentHashMap<UUID, Table> tables = new ConcurrentHashMap<>();
private final ReadWriteLock tablesLock = new ReentrantReadWriteLock();
// defines how often checking process should be run on server (in minutes)
private static final int TABLE_HEALTH_CHECK_TIMEOUT_MINS = 10;
public TableManagerImpl(ManagerFactory managerFactory) {
this.managerFactory = managerFactory;
}
public void init() {
expireExecutor.scheduleAtFixedRate(() -> {
try {
managerFactory.chatManager().clearUserMessageStorage();
checkTableHealthState();
} catch (Exception ex) {
logger.fatal("Check table health state job error:", ex);
}
}, TABLE_HEALTH_CHECK_TIMEOUT_MINS, TABLE_HEALTH_CHECK_TIMEOUT_MINS, TimeUnit.MINUTES);
}
@Override
@ -426,7 +411,7 @@ public class TableManagerImpl implements TableManager {
List<ChatSession> chatSessions = managerFactory.chatManager().getChatSessions();
logger.debug("------- ChatSessions: " + chatSessions.size() + " ----------------------------------");
for (ChatSession chatSession : chatSessions) {
logger.debug(chatSession.getChatId() + " " + formatter.format(chatSession.getCreateTime()) + ' ' + chatSession.getInfo() + ' ' + chatSession.getClients().values().toString());
logger.debug(chatSession.getChatId() + " " + formatter.format(chatSession.getCreateTime()) + ' ' + chatSession.getInfo() + ' ' + chatSession.getUsers().values().toString());
}
logger.debug("------- Games: " + managerFactory.gameManager().getNumberActiveGames() + " --------------------------------------------");
logger.debug(" Active Game Worker: " + managerFactory.threadExecutor().getActiveThreads(managerFactory.threadExecutor().getGameExecutor()));
@ -436,7 +421,8 @@ public class TableManagerImpl implements TableManager {
logger.debug("--- Server state END ------------------------------------------");
}
private void checkTableHealthState() {
private void removeOutdatedTables() {
// TODO: need research and check - is it actual code or not, 2023-12-06
if (logger.isDebugEnabled()) {
debugServerState();
}
@ -465,6 +451,13 @@ public class TableManagerImpl implements TableManager {
}
}
logger.debug("TABLE HEALTH CHECK - END");
}
@Override
public void checkHealth() {
//logger.info("Checking tables...");
// TODO: add memory reports
// TODO: add broken tables check and report (without seats, without gamecontroller, without active players, too long playing, etc)
removeOutdatedTables();
}
}