mirror of
https://github.com/magefree/mage.git
synced 2026-01-09 20:32:06 -08:00
tests: fixed error on load tests end (related to #11572), improved logs, improved session lifecycle on load tests;
tests: added additional test for Mana Maze and deep copy (related to #11572); docs: added more info to network related code;
This commit is contained in:
parent
98a3d8b947
commit
b3c55555a1
12 changed files with 119 additions and 66 deletions
|
|
@ -59,20 +59,20 @@ public class TableController {
|
|||
|
||||
public TableController(ManagerFactory managerFactory, UUID roomId, UUID userId, MatchOptions options) {
|
||||
this.managerFactory = managerFactory;
|
||||
timeoutExecutor = managerFactory.threadExecutor().getTimeoutExecutor();
|
||||
this.timeoutExecutor = managerFactory.threadExecutor().getTimeoutExecutor();
|
||||
this.userId = userId;
|
||||
this.options = options;
|
||||
match = GameFactory.instance.createMatch(options.getGameType(), options);
|
||||
this.match = GameFactory.instance.createMatch(options.getGameType(), options);
|
||||
if (userId != null) {
|
||||
Optional<User> user = managerFactory.userManager().getUser(userId);
|
||||
// TODO: Handle if user == null
|
||||
controllerName = user.map(User::getName).orElse("undefined");
|
||||
this.controllerName = user.map(User::getName).orElse("undefined");
|
||||
} else {
|
||||
controllerName = "System";
|
||||
this.controllerName = "System";
|
||||
}
|
||||
table = new Table(roomId, options.getGameType(), options.getName(), controllerName, DeckValidatorFactory.instance.createDeckValidator(options.getDeckType()),
|
||||
this.table = new Table(roomId, options.getGameType(), options.getName(), controllerName, DeckValidatorFactory.instance.createDeckValidator(options.getDeckType()),
|
||||
options.getPlayerTypes(), new TableRecorderImpl(managerFactory.userManager()), match, options.getBannedUsers(), options.isPlaneChase());
|
||||
chatId = managerFactory.chatManager().createChatSession("Match Table " + table.getId());
|
||||
this.chatId = managerFactory.chatManager().createChatSession("Match Table " + table.getId());
|
||||
init();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
|
||||
|
||||
package mage.server.game;
|
||||
|
||||
import mage.MageException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface GameCallback {
|
||||
|
||||
void gameResult(String result) throws MageException;
|
||||
void endGameWithResult(String result) throws MageException;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ public class GameController implements GameCallback {
|
|||
logger.fatal("Send info about player not joined yet:", ex);
|
||||
}
|
||||
}, GAME_TIMEOUTS_CHECK_JOINING_STATUS_EVERY_SECS, GAME_TIMEOUTS_CHECK_JOINING_STATUS_EVERY_SECS, TimeUnit.SECONDS);
|
||||
checkStart();
|
||||
checkJoinAndStart();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -316,7 +316,7 @@ public class GameController implements GameCallback {
|
|||
user.get().addGame(playerId, gameSession);
|
||||
logger.debug("Player " + player.getName() + ' ' + playerId + " has " + joinType + " gameId: " + game.getId());
|
||||
managerFactory.chatManager().broadcast(chatId, "", game.getPlayer(playerId).getLogName() + " has " + joinType + " the game", MessageColor.ORANGE, true, game, MessageType.GAME, null);
|
||||
checkStart();
|
||||
checkJoinAndStart();
|
||||
}
|
||||
|
||||
private synchronized void startGame() {
|
||||
|
|
@ -326,16 +326,19 @@ public class GameController implements GameCallback {
|
|||
player.updateRange(game);
|
||||
}
|
||||
|
||||
// send first info to users
|
||||
for (GameSessionPlayer gameSessionPlayer : getGameSessions()) {
|
||||
gameSessionPlayer.init();
|
||||
}
|
||||
|
||||
// real game start
|
||||
GameWorker worker = new GameWorker(game, choosingPlayerId, this);
|
||||
gameFuture = gameExecutor.submit(worker);
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ignore) {
|
||||
}
|
||||
|
||||
if (game.getState().getChoosingPlayerId() != null) {
|
||||
// start timer to force player to choose starting player otherwise loosing by being idle
|
||||
setupTimeout(game.getState().getChoosingPlayerId());
|
||||
|
|
@ -395,7 +398,7 @@ public class GameController implements GameCallback {
|
|||
}
|
||||
}
|
||||
}
|
||||
checkStart();
|
||||
checkJoinAndStart();
|
||||
}
|
||||
|
||||
private Optional<User> getUserByPlayerId(UUID playerId) {
|
||||
|
|
@ -407,14 +410,14 @@ public class GameController implements GameCallback {
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
private void checkStart() {
|
||||
if (allJoined()) {
|
||||
private void checkJoinAndStart() {
|
||||
if (isAllJoined()) {
|
||||
joinWaitingExecutor.shutdownNow();
|
||||
managerFactory.threadExecutor().getCallExecutor().execute(this::startGame);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean allJoined() {
|
||||
private boolean isAllJoined() {
|
||||
for (Player player : game.getPlayers().values()) {
|
||||
if (!player.hasLeft()) {
|
||||
Optional<User> user = getUserByPlayerId(player.getId());
|
||||
|
|
@ -480,7 +483,7 @@ public class GameController implements GameCallback {
|
|||
public void quitMatch(UUID userId) {
|
||||
UUID playerId = getPlayerId(userId);
|
||||
if (playerId != null) {
|
||||
if (allJoined()) {
|
||||
if (isAllJoined()) {
|
||||
GameSessionPlayer gameSessionPlayer = gameSessions.get(playerId);
|
||||
if (gameSessionPlayer != null) {
|
||||
gameSessionPlayer.quitGame();
|
||||
|
|
@ -492,7 +495,7 @@ public class GameController implements GameCallback {
|
|||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
player.leave();
|
||||
checkStart(); // => So the game starts and gets an result or multiplayer game starts with active players
|
||||
checkJoinAndStart(); // => So the game starts and gets an result or multiplayer game starts with active players
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -734,6 +737,7 @@ public class GameController implements GameCallback {
|
|||
}
|
||||
|
||||
public void endGame(final String message) throws MageException {
|
||||
// send end game message/dialog
|
||||
for (final GameSessionPlayer gameSession : getGameSessions()) {
|
||||
gameSession.gameOver(message);
|
||||
gameSession.removeGame();
|
||||
|
|
@ -741,6 +745,8 @@ public class GameController implements GameCallback {
|
|||
for (final GameSessionWatcher gameWatcher : getGameSessionWatchers()) {
|
||||
gameWatcher.gameOver(message);
|
||||
}
|
||||
|
||||
// start next game or close finished table
|
||||
managerFactory.tableManager().endGame(tableId);
|
||||
}
|
||||
|
||||
|
|
@ -944,7 +950,7 @@ public class GameController implements GameCallback {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void gameResult(String result) {
|
||||
public void endGameWithResult(String result) {
|
||||
try {
|
||||
endGame(result);
|
||||
} catch (MageException ex) {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
|
||||
package mage.server.game;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import mage.MageException;
|
||||
import mage.game.Game;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* @param <T>
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
* Game: main thread to process full game (one thread per game)
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com, JayDi85
|
||||
*/
|
||||
public class GameWorker<T> implements Callable {
|
||||
public class GameWorker implements Callable<Boolean> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(GameWorker.class);
|
||||
|
||||
|
|
@ -27,25 +27,23 @@ public class GameWorker<T> implements Callable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object call() {
|
||||
public Boolean call() {
|
||||
try {
|
||||
LOGGER.debug("GAME WORKER started gameId " + game.getId());
|
||||
// play game
|
||||
Thread.currentThread().setName("GAME " + game.getId());
|
||||
game.start(choosingPlayerId);
|
||||
game.fireUpdatePlayersEvent();
|
||||
gameController.gameResult(game.getWinner());
|
||||
game.cleanUp();
|
||||
} catch (MageException ex) {
|
||||
LOGGER.fatal("GameWorker mage error [" + game.getId() + "] " + ex, ex);
|
||||
} catch (Exception e) {
|
||||
LOGGER.fatal("GameWorker general exception [" + game.getId() + "] " + e.getMessage(), e);
|
||||
if (e instanceof NullPointerException) {
|
||||
LOGGER.info(e.getStackTrace());
|
||||
}
|
||||
} catch (Error err) {
|
||||
LOGGER.fatal("GameWorker general error [" + game.getId() + "] " + err, err);
|
||||
|
||||
// save result and start next game or close finished table
|
||||
game.fireUpdatePlayersEvent(); // TODO: no needs in update event (gameController.endGameWithResult already send game end dialog)?
|
||||
gameController.endGameWithResult(game.getWinner());
|
||||
|
||||
// clear resources
|
||||
game.cleanUp();// TODO: no needs in cleanup code (cards list are useless for memory optimization, game states are more important)?
|
||||
} catch (MageException e) {
|
||||
LOGGER.fatal("GameWorker mage error [" + game.getId() + " - " + game + "]: " + e, e);
|
||||
} catch (Throwable e) {
|
||||
LOGGER.fatal("GameWorker system error [" + game.getId() + " - " + game + "]: " + e, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public class ReplaySession implements GameCallback {
|
|||
}
|
||||
|
||||
public void stop() {
|
||||
gameResult("stopped replay");
|
||||
endGameWithResult("stopped replay");
|
||||
}
|
||||
|
||||
public synchronized void next() {
|
||||
|
|
@ -51,7 +51,7 @@ public class ReplaySession implements GameCallback {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void gameResult(final String result) {
|
||||
public void endGameWithResult(final String result) {
|
||||
managerFactory.userManager().getUser(userId).ifPresent(user ->
|
||||
user.fireCallback(new ClientCallback(ClientCallbackMethod.REPLAY_DONE, replay.getGame().getId(), result)));
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ public class ReplaySession implements GameCallback {
|
|||
|
||||
private void updateGame(final GameState state, Game game) {
|
||||
if (state == null) {
|
||||
gameResult("game ended");
|
||||
endGameWithResult("game ended");
|
||||
} else {
|
||||
managerFactory.userManager().getUser(userId).ifPresent(user ->
|
||||
user.fireCallback(new ClientCallback(ClientCallbackMethod.REPLAY_UPDATE, replay.getGame().getId(), new GameView(state, game, null, null))));
|
||||
|
|
|
|||
|
|
@ -1,20 +1,34 @@
|
|||
package mage.server.managers;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
/**
|
||||
* Server side threads to execute some code/tasks
|
||||
*/
|
||||
public interface ThreadExecutor {
|
||||
|
||||
int getActiveThreads(ExecutorService executerService);
|
||||
|
||||
ExecutorService getCallExecutor();
|
||||
|
||||
/**
|
||||
* Main game thread (one per game)
|
||||
*/
|
||||
ExecutorService getGameExecutor();
|
||||
|
||||
/**
|
||||
* Helper threads to execute async commands for game and server related tasks (example: process income command from a client)
|
||||
*/
|
||||
ExecutorService getCallExecutor();
|
||||
|
||||
/**
|
||||
* Helper threads to execute async timers and time related tasks
|
||||
*/
|
||||
ScheduledExecutorService getTimeoutExecutor();
|
||||
|
||||
ScheduledExecutorService getTimeoutIdleExecutor();
|
||||
|
||||
/**
|
||||
* Helper thread to execute inner server tasks
|
||||
*/
|
||||
ScheduledExecutorService getServerHealthExecutor();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue