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 592d7878436..c052f639d80 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -917,14 +917,21 @@ public class GameController implements GameCallback { private void error(String message, Exception ex) { StringBuilder sb = new StringBuilder(); - sb.append(message).append(ex.toString()); + sb.append(message); + sb.append("\n"); + sb.append("\n"); + sb.append(ex); sb.append("\nServer version: ").append(Main.getVersion().toString()); - sb.append('\n'); + sb.append("\nStack trace:"); + sb.append("\n"); for (StackTraceElement e : ex.getStackTrace()) { - sb.append(e.toString()).append('\n'); + sb.append(e.toString()).append("\n"); } + String mes = sb.toString(); + + // send error for each player for (final Entry entry : getGameSessionsMap().entrySet()) { - entry.getValue().gameError(sb.toString()); + entry.getValue().gameError(mes); } } diff --git a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java index f5006e22352..24003ba0902 100644 --- a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java +++ b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java @@ -268,6 +268,7 @@ public final class SystemUtil { Ability fakeSourceAbilityTemplate = new SimpleStaticAbility(Zone.OUTSIDE, new InfoEffect("adding testing cards")); fakeSourceAbilityTemplate.setControllerId(feedbackPlayer.getId()); + List errorsList = new ArrayList<>(); try { String fileName = fileSource; if (fileName == null) { @@ -276,7 +277,10 @@ public final class SystemUtil { File f = new File(fileName); if (!f.exists()) { - logger.warn("Couldn't find init file: " + fileName); + String mes = String.format("Couldn't find init file: %s", f.getAbsolutePath()); + logger.warn(mes); + errorsList.add(mes); + sendCheatCommandsFeedback(game, feedbackPlayer, errorsList); return; } @@ -287,6 +291,7 @@ public final class SystemUtil { // 2. ask user if many groups // 3. process system commands // 4. run commands from selected group + // 1. parse List groups = new ArrayList<>(); @@ -312,7 +317,9 @@ public final class SystemUtil { currentGroup = new CommandGroup(groupName, true); groups.add(currentGroup); } else { - logger.warn("Special group [" + groupName + "] is not supported."); + String mes = String.format("Special group is not supported: %s", groupName); + errorsList.add(mes); + logger.warn(mes); } continue; } else { @@ -368,7 +375,10 @@ public final class SystemUtil { return; } - logger.info("Selected group [" + runGroup.name + "] with " + runGroup.commands.size() + " commands"); + logger.info(String.format("Selected group [%s] with %d commands", + runGroup.name, + runGroup.commands.size() + )); // 3. system commands if (runGroup.isSpecialCommand) { @@ -447,7 +457,14 @@ public final class SystemUtil { game.firePriorityEvent(savedPriorityPlayer); } break; + + default: + String mes = String.format("Unknown system command: %s", runGroup.name); + errorsList.add(mes); + logger.error(mes); + break; } + sendCheatCommandsFeedback(game, feedbackPlayer, errorsList); return; } @@ -464,11 +481,16 @@ public final class SystemUtil { if (line.startsWith(COMMAND_REF_PREFIX)) { CommandGroup other = otherGroupRefs.getOrDefault(line, null); if (other != null && !other.isSpecialCommand) { - logger.info("Replace ref group " + line + " by " + other.commands.size() + " commands"); + logger.info(String.format("Replace ref group [%s] by %d child commands", + line, + other.commands.size() + )); runGroup.commands.remove(i); runGroup.commands.addAll(i, other.commands); } else { - logger.error("Can't find ref group: " + line); + String mes = String.format("Can't find ref group: %s", line); + errorsList.add(mes); + logger.error(mes); } } } @@ -478,13 +500,17 @@ public final class SystemUtil { CardCommandData command = parseCardCommand(line); if (!command.OK) { - logger.warn(command.Error + ": " + line); + String mes = String.format("%s: %s", command.Error, line); + errorsList.add(mes); + logger.warn(mes); continue; } Optional playerOptional = findPlayer(game, command.player); if (!playerOptional.isPresent()) { - logger.warn("Unknown player: " + line); + String mes = String.format("Unknown player: %s", line); + errorsList.add(mes); + logger.warn(mes); continue; } Player player = playerOptional.get(); @@ -531,7 +557,11 @@ public final class SystemUtil { // find card info CardInfo cardInfo = CardLookup.instance.lookupCardInfo(command.cardName, command.cardSet).orElse(null); if (cardInfo == null) { - logger.warn("Unknown card for stack command [" + command.cardName + "]: " + line); + String mes = String.format("Unknown card for stack command [%s]: %s", + command.cardName, + line); + errorsList.add(mes); + logger.warn(mes); continue; } @@ -576,7 +606,11 @@ public final class SystemUtil { } else if ("sideboard".equalsIgnoreCase(command.zone)) { gameZone = Zone.OUTSIDE; } else { - logger.warn("Unknown zone [" + command.zone + "]: " + line); + String mes = String.format("Unknown zone [%s]: %s", + command.zone, + line); + errorsList.add(mes); + logger.warn(mes); continue; } @@ -590,11 +624,13 @@ public final class SystemUtil { } if (cards.isEmpty()) { - logger.warn(String.format("Unknown card [%s%s]: %s", + String mes = String.format("Unknown card [%s%s]: %s", command.cardSet.isEmpty() ? "" : command.cardSet + "-", command.cardName, line - )); + ); + errorsList.add(mes); + logger.warn(mes); continue; } @@ -615,7 +651,9 @@ public final class SystemUtil { cardsToLoad.forEach(card -> gameCommander.addCommander(card, player)); cardsToLoad.forEach(card -> gameCommander.initCommander(card, player)); } else { - logger.fatal("Commander card can be used in commander game only: " + command.cardName); + String mes = String.format("Commander card can be used in commander game only: %s", command.cardName); + errorsList.add(mes); + logger.error(mes); } } else if ("sideboard".equalsIgnoreCase(command.zone) && cardsToLoad.size() > 0) { // put to sideboard @@ -632,8 +670,24 @@ public final class SystemUtil { } } } catch (Exception e) { - logger.fatal("", e); + String mes = String.format("Catch critical error: %s", e.getMessage()); + errorsList.add(mes); + logger.error(mes, e); } + sendCheatCommandsFeedback(game, feedbackPlayer, errorsList); + } + + private static void sendCheatCommandsFeedback(Game game, Player feedbackPlayer, List errorsList) { + // inform all players about wrong commands or other errors + // TODO: it's a workaround to show a dialog with error message (must be replaced by message dialog for feedback player) + if (errorsList.size() > 0) { + String mes = String.format("Player %s tried to apply cheat commands and catch %d errors:\n\n", + feedbackPlayer.getName(), errorsList.size()); + mes += String.join("\n", errorsList); + mes += "\n"; + game.fireErrorEvent("Cheat command errors", new IllegalArgumentException(mes)); + } + game.informPlayers(String.format("%s: tried to apply cheat commands", feedbackPlayer.getLogName())); } /** diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 508bac1db85..aef605940d0 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -1636,15 +1636,16 @@ public abstract class GameImpl implements Game { String info = this.getStack().stream().map(MageObject::toString).collect(Collectors.joining("\n")); logger.info(String.format("\nStack before error %d: \n%s\n", this.getStack().size(), info)); - // rollback game to prev state - GameState restoredState = restoreState(rollbackBookmark, "Game exception: " + ex.getMessage()); - rollbackBookmark = 0; - + // too many errors - end game if (errorContinueCounter > 15) { throw new MageException("Iterated player priority after game exception too often, game ends! Last error:\n " + ex.getMessage()); } + // rollback game to prev state + GameState restoredState = restoreState(rollbackBookmark, "Game exception: " + ex.getMessage()); + rollbackBookmark = 0; + if (restoredState != null) { this.informPlayers(String.format("Auto-restored to %s due game error: %s", restoredState, ex.getMessage())); } else {