mirror of
https://github.com/correl/mage.git
synced 2024-11-25 11:09:53 +00:00
* Reworked rollback handling - possible fix for #2072 #5383, #4309 and fixes #5883, fixes #1983, fixes #5917.
This commit is contained in:
parent
fe9deec071
commit
621d8c188d
2 changed files with 90 additions and 66 deletions
|
@ -1,5 +1,8 @@
|
||||||
package mage.game;
|
package mage.game;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import mage.MageItem;
|
import mage.MageItem;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
@ -42,10 +45,6 @@ import mage.players.Players;
|
||||||
import mage.util.MessageToClient;
|
import mage.util.MessageToClient;
|
||||||
import mage.util.functions.ApplyToPermanent;
|
import mage.util.functions.ApplyToPermanent;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public interface Game extends MageItem, Serializable {
|
public interface Game extends MageItem, Serializable {
|
||||||
|
|
||||||
MatchType getGameType();
|
MatchType getGameType();
|
||||||
|
@ -301,8 +300,8 @@ public interface Game extends MageItem, Serializable {
|
||||||
/**
|
/**
|
||||||
* Creates and fires an damage prevention event
|
* Creates and fires an damage prevention event
|
||||||
*
|
*
|
||||||
* @param damageEvent damage event that will be replaced (instanceof check
|
* @param damageEvent damage event that will be replaced (instanceof
|
||||||
* will be done)
|
* check will be done)
|
||||||
* @param source ability that's the source of the prevention effect
|
* @param source ability that's the source of the prevention effect
|
||||||
* @param game
|
* @param game
|
||||||
* @param amountToPrevent max preventable amount
|
* @param amountToPrevent max preventable amount
|
||||||
|
@ -313,9 +312,10 @@ public interface Game extends MageItem, Serializable {
|
||||||
/**
|
/**
|
||||||
* Creates and fires an damage prevention event
|
* Creates and fires an damage prevention event
|
||||||
*
|
*
|
||||||
* @param event damage event that will be replaced (instanceof check will be
|
* @param event damage event that will be replaced (instanceof
|
||||||
* done)
|
* check will be done)
|
||||||
* @param source ability that's the source of the prevention effect
|
* @param source ability that's the source of the prevention
|
||||||
|
* effect
|
||||||
* @param game
|
* @param game
|
||||||
* @param preventAllDamage true if there is no limit to the damage that can
|
* @param preventAllDamage true if there is no limit to the damage that can
|
||||||
* be prevented
|
* be prevented
|
||||||
|
@ -489,4 +489,7 @@ public interface Game extends MageItem, Serializable {
|
||||||
return getCommandersIds(player, CommanderCardType.ANY);
|
return getCommandersIds(player, CommanderCardType.ANY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setGameStopped(boolean gameStopped);
|
||||||
|
|
||||||
|
boolean isGameStopped();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package mage.game;
|
package mage.game;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import mage.MageException;
|
import mage.MageException;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.*;
|
import mage.abilities.*;
|
||||||
|
@ -67,11 +71,6 @@ import mage.util.functions.ApplyToPermanent;
|
||||||
import mage.watchers.common.*;
|
import mage.watchers.common.*;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
public abstract class GameImpl implements Game, Serializable {
|
public abstract class GameImpl implements Game, Serializable {
|
||||||
|
|
||||||
private static final int ROLLBACK_TURNS_MAX = 4;
|
private static final int ROLLBACK_TURNS_MAX = 4;
|
||||||
|
@ -107,11 +106,13 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
// game states to allow player rollback
|
// game states to allow player rollback
|
||||||
protected transient Map<Integer, GameState> gameStatesRollBack = new HashMap<>();
|
protected transient Map<Integer, GameState> gameStatesRollBack = new HashMap<>();
|
||||||
protected boolean executingRollback;
|
protected boolean executingRollback;
|
||||||
|
protected int turnToGoToForRollback;
|
||||||
|
|
||||||
protected Date startTime;
|
protected Date startTime;
|
||||||
protected Date endTime;
|
protected Date endTime;
|
||||||
protected UUID startingPlayerId;
|
protected UUID startingPlayerId;
|
||||||
protected UUID winnerId;
|
protected UUID winnerId;
|
||||||
|
protected boolean gameStopped = false;
|
||||||
|
|
||||||
protected RangeOfInfluence range;
|
protected RangeOfInfluence range;
|
||||||
protected Mulligan mulligan;
|
protected Mulligan mulligan;
|
||||||
|
@ -767,27 +768,6 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resume() {
|
|
||||||
playerList = state.getPlayerList(state.getActivePlayerId());
|
|
||||||
Player player = getPlayer(playerList.get());
|
|
||||||
boolean wasPaused = state.isPaused();
|
|
||||||
state.resume();
|
|
||||||
if (!checkIfGameIsOver()) {
|
|
||||||
fireInformEvent("Turn " + state.getTurnNum());
|
|
||||||
if (checkStopOnTurnOption()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state.getTurn().resumePlay(this, wasPaused);
|
|
||||||
if (!isPaused() && !checkIfGameIsOver()) {
|
|
||||||
endOfTurn();
|
|
||||||
player = playerList.getNext(this, true);
|
|
||||||
state.setTurnNum(state.getTurnNum() + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
play(player.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void play(UUID nextPlayerId) {
|
protected void play(UUID nextPlayerId) {
|
||||||
if (!isPaused() && !checkIfGameIsOver()) {
|
if (!isPaused() && !checkIfGameIsOver()) {
|
||||||
playerList = state.getPlayerList(nextPlayerId);
|
playerList = state.getPlayerList(nextPlayerId);
|
||||||
|
@ -876,13 +856,8 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
boolean skipTurn = false;
|
boolean skipTurn = false;
|
||||||
do {
|
do {
|
||||||
if (executingRollback) {
|
if (executingRollback) {
|
||||||
executingRollback = false;
|
rollbackTurnsExecution(turnToGoToForRollback);
|
||||||
player = getPlayer(state.getActivePlayerId());
|
player = getPlayer(state.getActivePlayerId());
|
||||||
for (Player playerObject : getPlayers().values()) {
|
|
||||||
if (playerObject.isInGame()) {
|
|
||||||
playerObject.abortReset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
state.setActivePlayerId(player.getId());
|
state.setActivePlayerId(player.getId());
|
||||||
saveRollBackGameState();
|
saveRollBackGameState();
|
||||||
|
@ -904,6 +879,27 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resume() {
|
||||||
|
playerList = state.getPlayerList(state.getActivePlayerId());
|
||||||
|
Player player = getPlayer(playerList.get());
|
||||||
|
boolean wasPaused = state.isPaused();
|
||||||
|
state.resume();
|
||||||
|
if (!checkIfGameIsOver()) {
|
||||||
|
fireInformEvent("Turn " + state.getTurnNum());
|
||||||
|
if (checkStopOnTurnOption()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.getTurn().resumePlay(this, wasPaused);
|
||||||
|
if (!isPaused() && !checkIfGameIsOver()) {
|
||||||
|
endOfTurn();
|
||||||
|
player = playerList.getNext(this, true);
|
||||||
|
state.setTurnNum(state.getTurnNum() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
play(player.getId());
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkStopOnTurnOption() {
|
private boolean checkStopOnTurnOption() {
|
||||||
if (gameOptions.stopOnTurn != null && gameOptions.stopAtStep == PhaseStep.UNTAP) {
|
if (gameOptions.stopOnTurn != null && gameOptions.stopAtStep == PhaseStep.UNTAP) {
|
||||||
if (gameOptions.stopOnTurn.equals(state.getTurnNum())) {
|
if (gameOptions.stopOnTurn.equals(state.getTurnNum())) {
|
||||||
|
@ -3260,14 +3256,8 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
return turnToGoTo > 0 && gameStatesRollBack.containsKey(turnToGoTo);
|
return turnToGoTo > 0 && gameStatesRollBack.containsKey(turnToGoTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void rollbackTurnsExecution(int turnToGoToForRollback) {
|
||||||
public synchronized void rollbackTurns(int turnsToRollback) {
|
GameState restore = gameStatesRollBack.get(turnToGoToForRollback);
|
||||||
if (gameOptions.rollbackTurnsAllowed) {
|
|
||||||
int turnToGoTo = getTurnNum() - turnsToRollback;
|
|
||||||
if (turnToGoTo < 1 || !gameStatesRollBack.containsKey(turnToGoTo)) {
|
|
||||||
informPlayers(GameLog.getPlayerRequestColoredText("Player request: It's not possible to rollback " + turnsToRollback + " turn(s)"));
|
|
||||||
} else {
|
|
||||||
GameState restore = gameStatesRollBack.get(turnToGoTo);
|
|
||||||
if (restore != null) {
|
if (restore != null) {
|
||||||
informPlayers(GameLog.getPlayerRequestColoredText("Player request: Rolling back to start of turn " + restore.getTurnNum()));
|
informPlayers(GameLog.getPlayerRequestColoredText("Player request: Rolling back to start of turn " + restore.getTurnNum()));
|
||||||
state.restoreForRollBack(restore);
|
state.restoreForRollBack(restore);
|
||||||
|
@ -3277,7 +3267,25 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
gameStates.clear();
|
gameStates.clear();
|
||||||
// because restore uses the objects without copy each copy the state again
|
// because restore uses the objects without copy each copy the state again
|
||||||
gameStatesRollBack.put(getTurnNum(), state.copy());
|
gameStatesRollBack.put(getTurnNum(), state.copy());
|
||||||
|
|
||||||
|
for (Player playerObject : getPlayers().values()) {
|
||||||
|
if (playerObject.isInGame()) {
|
||||||
|
playerObject.abortReset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
executingRollback = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void rollbackTurns(int turnsToRollback) {
|
||||||
|
if (gameOptions.rollbackTurnsAllowed && !executingRollback) {
|
||||||
|
int turnToGoTo = getTurnNum() - turnsToRollback;
|
||||||
|
if (turnToGoTo < 1 || !gameStatesRollBack.containsKey(turnToGoTo)) {
|
||||||
|
informPlayers(GameLog.getPlayerRequestColoredText("Player request: It's not possible to rollback " + turnsToRollback + " turn(s)"));
|
||||||
|
} else {
|
||||||
executingRollback = true;
|
executingRollback = true;
|
||||||
|
turnToGoToForRollback = turnToGoTo;
|
||||||
for (Player playerObject : getPlayers().values()) {
|
for (Player playerObject : getPlayers().values()) {
|
||||||
if (playerObject.isHuman() && playerObject.canRespond()) {
|
if (playerObject.isHuman() && playerObject.canRespond()) {
|
||||||
playerObject.resetStoredBookmark(this);
|
playerObject.resetStoredBookmark(this);
|
||||||
|
@ -3286,6 +3294,8 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fireUpdatePlayersEvent();
|
fireUpdatePlayersEvent();
|
||||||
|
if (gameOptions.testMode && gameStopped) { // in test mode execute rollback directly
|
||||||
|
rollbackTurnsExecution(turnToGoToForRollback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3374,4 +3384,15 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
public Set<UUID> getCommandersIds(Player player, CommanderCardType commanderCardType) {
|
public Set<UUID> getCommandersIds(Player player, CommanderCardType commanderCardType) {
|
||||||
return player.getCommandersIds();
|
return player.getCommandersIds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setGameStopped(boolean gameStopped) {
|
||||||
|
this.gameStopped = gameStopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGameStopped() {
|
||||||
|
return gameStopped;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue