Fixed (probably) the player1 concedes but other player2 wins. Fixed that if player runs out of timer time or player lose of beeing idle, the match will end correctly.

This commit is contained in:
LevelX2 2014-05-01 19:34:52 +02:00
parent 6d039e4262
commit 473c9e207b
32 changed files with 286 additions and 132 deletions

View file

@ -71,10 +71,12 @@ public class NewTournamentDialog extends MageDialog {
private TableView table;
private UUID playerId;
private UUID roomId;
private Session session;
private final Session session;
private String lastSessionId;
private final List<TournamentPlayerPanel> players = new ArrayList<>();
private final List<JComboBox> packs = new ArrayList<>();
private final int CONSTRUCTION_TIME_MIN = 6;
private final int CONSTRUCTION_TIME_MAX = 30;
/** Creates new form NewTournamentDialog */
public NewTournamentDialog() {
@ -84,7 +86,7 @@ public class NewTournamentDialog extends MageDialog {
txtName.setText("Tournament");
this.spnNumWins.setModel(new SpinnerNumberModel(2, 1, 5, 1));
this.spnFreeMulligans.setModel(new SpinnerNumberModel(0, 0, 5, 1));
this.spnConstructTime.setModel(new SpinnerNumberModel(10, 10, 30, 5));
this.spnConstructTime.setModel(new SpinnerNumberModel(10, CONSTRUCTION_TIME_MIN, CONSTRUCTION_TIME_MAX, 2));
this.spnNumRounds.setModel(new SpinnerNumberModel(2, 2, 10, 1));
}
@ -428,8 +430,7 @@ public class NewTournamentDialog extends MageDialog {
if (tOptions.getLimitedOptions() == null) {
tOptions.setLimitedOptions(new LimitedOptions());
}
tOptions.getLimitedOptions().setConstructionTime(60);
// tOptions.getLimitedOptions().setConstructionTime((Integer)this.spnConstructTime.getValue() * 60);
tOptions.getLimitedOptions().setConstructionTime((Integer)this.spnConstructTime.getValue() * 60);
if (tournamentType.isCubeBooster()) {
tOptions.getLimitedOptions().setDraftCubeName(this.cbDraftCube.getSelectedItem().toString());
} else {
@ -646,7 +647,11 @@ public class NewTournamentDialog extends MageDialog {
break;
}
}
this.spnConstructTime.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_CONSTR_TIME, "600"))/60);
int constructionTime = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_CONSTR_TIME, "600")) / 60;
if (constructionTime < CONSTRUCTION_TIME_MIN || constructionTime > CONSTRUCTION_TIME_MAX) {
constructionTime = CONSTRUCTION_TIME_MIN;
}
this.spnConstructTime.setValue(constructionTime);
String tournamentTypeName = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_TYPE, "Sealed Elimination");
for (TournamentTypeView tournamentTypeView : session.getTournamentTypes()) {
if (tournamentTypeView.getName().equals(tournamentTypeName)) {

View file

@ -12,6 +12,7 @@ public interface Action {
/**
* Executes action.
* @throws mage.MageException
*/
void execute() throws MageException;
}

View file

@ -1,11 +1,10 @@
package mage.utils.timer;
import mage.MageException;
import mage.interfaces.Action;
import org.apache.log4j.Logger;
import java.util.Timer;
import java.util.TimerTask;
import mage.MageException;
import mage.interfaces.Action;
import org.apache.log4j.Logger;
/**
* @author noxx
@ -14,14 +13,11 @@ public class PriorityTimer extends TimerTask {
private static final Logger logger = Logger.getLogger(PriorityTimer.class);
private final long delay;
private final Action taskOnTimeout;
private int count;
private long delay;
private Action taskOnTimeout;
private Action taskOnTick;
private States state = States.NONE;
enum States {

View file

@ -72,14 +72,18 @@ public class MatchView implements Serializable {
}
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
for (MatchPlayer player: match.getPlayers()) {
sb1.append(player.getName());
if(player.hasQuit()) {
for (MatchPlayer matchPlayer: match.getPlayers()) {
sb1.append(matchPlayer.getName());
if(matchPlayer.hasQuit()) {
if (matchPlayer.hasTimerTimeout()) {
sb1.append(" [timer] ");
} else {
sb1.append(" [quit] ");
}
}
sb1.append(", ");
sb2.append(player.getName()).append(" ");
sb2.append(player.getWins()).append("-").append(player.getLoses()).append(", ");
sb2.append(matchPlayer.getName()).append(" ");
sb2.append(matchPlayer.getWins()).append("-").append(matchPlayer.getLoses()).append(", ");
}
players = sb1.substring(0, sb1.length() - 2);
result = sb2.substring(0, sb2.length() - 2);

View file

@ -39,7 +39,6 @@ import mage.game.Seat;
import mage.game.Table;
import mage.game.match.MatchPlayer;
import mage.game.tournament.TournamentPlayer;
import org.jboss.logging.Logger;
/**
*

View file

@ -54,7 +54,7 @@ public class TournamentGameView implements Serializable {
this.matchId = pair.getMatch().getId();
this.gameId = game.getId();
this.players = pair.getPlayer1().getPlayer().getName() + " - " + pair.getPlayer2().getPlayer().getName();
if (game.isGameOver()) {
if (game.hasEnded()) {
this.state = "Finished";
this.result = game.getWinner();
}

View file

@ -467,7 +467,7 @@ public class ComputerPlayer6 extends ComputerPlayer<ComputerPlayer6> implements
logger.trace("interrupted - " + val);
return val;
}
if (depth <= 0 || SimulationNode2.nodeCount > maxNodes || game.isGameOver()) {
if (depth <= 0 || SimulationNode2.nodeCount > maxNodes || game.gameOver(null)) {
logger.trace("Add actions -- reached end state, node count=" + SimulationNode2.nodeCount + ", depth=" + depth);
val = GameStateEvaluator2.evaluate(playerId, game);
UUID currentPlayerId = node.getGame().getPlayerList().get();
@ -488,7 +488,7 @@ public class ComputerPlayer6 extends ComputerPlayer<ComputerPlayer6> implements
}
}
if (game.isGameOver()) {
if (game.gameOver(null)) {
val = GameStateEvaluator2.evaluate(playerId, game);
} else if (node.getChildren().size() > 0) {
//declared attackers or blockers or triggered abilities
@ -534,7 +534,7 @@ public class ComputerPlayer6 extends ComputerPlayer<ComputerPlayer6> implements
logger.debug("Sim Prio [" + depth + "] -- repeated action: " + action.toString());
continue;
}
if (!sim.isGameOver() && action.isUsesStack()) {
if (!sim.gameOver(null) && action.isUsesStack()) {
// only pass if the last action uses the stack
sim.getPlayer(currentPlayer.getId()).pass(game);
sim.getPlayerList().getNext();
@ -797,7 +797,7 @@ public class ComputerPlayer6 extends ComputerPlayer<ComputerPlayer6> implements
break;
case CLEANUP:
game.getPhase().getStep().beginStep(game, activePlayerId);
if (!game.checkStateAndTriggered() && !game.isGameOver()) {
if (!game.checkStateAndTriggered() && !game.gameOver(null)) {
game.getState().setActivePlayerId(game.getState().getPlayerList(game.getActivePlayerId()).getNext());
game.getTurn().setPhase(new BeginningPhase());
game.getPhase().setStep(new UntapStep());

View file

@ -229,7 +229,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
return GameStateEvaluator2.evaluate(playerId, game);
}
// Condition to stop deeper simulation
if (depth <= 0 || SimulationNode2.nodeCount > maxNodes || game.isGameOver()) {
if (depth <= 0 || SimulationNode2.nodeCount > maxNodes || game.gameOver(null)) {
val = GameStateEvaluator2.evaluate(playerId, game);
if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder("Add Actions -- reached end state <").append(val).append(">");
@ -265,7 +265,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
}
}
if (game.isGameOver()) {
if (game.gameOver(null)) {
val = GameStateEvaluator2.evaluate(playerId, game);
}
else if (stepFinished) {
@ -494,7 +494,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_POST, sim.getActivePlayerId(), sim.getActivePlayerId()));
Combat simCombat = sim.getCombat().copy();
finishCombat(sim);
if (sim.isGameOver()) {
if (sim.gameOver(null)) {
val = GameStateEvaluator2.evaluate(playerId, sim);
}
else if (!counter) {
@ -568,7 +568,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
logger.debug("interrupted");
return;
}
if (!game.isGameOver()) {
if (!game.gameOver(null)) {
game.getPhase().setStep(step);
if (!step.skipStep(game, game.getActivePlayerId())) {
step.beginStep(game, game.getActivePlayerId());
@ -617,7 +617,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
logger.debug("interrupted");
return;
}
if (!game.isGameOver()) {
if (!game.gameOver(null)) {
game.getTurn().getPhase().endPhase(game, game.getActivePlayerId());
game.getTurn().setPhase(new EndPhase());
if (game.getTurn().getPhase().beginPhase(game, game.getActivePlayerId())) {

View file

@ -33,7 +33,7 @@ public class GameStateEvaluator2 {
public static int evaluate(UUID playerId, Game game) {
Player player = game.getPlayer(playerId);
Player opponent = game.getPlayer(game.getOpponents(playerId).iterator().next());
if (game.isGameOver()) {
if (game.gameOver(null)) {
if (player.hasLost() || opponent.hasWon()) {
return LOSE_GAME_SCORE;
}

View file

@ -60,12 +60,14 @@ public class ActionSimulator {
public int evaluateState() {
Player opponent = game.getPlayer(game.getOpponents(player.getId()).iterator().next());
if (game.isGameOver()) {
if (player.hasLost() || opponent.hasWon())
if (game.gameOver(null)) {
if (player.hasLost() || opponent.hasWon()) {
return Integer.MIN_VALUE;
if (opponent.hasLost() || player.hasWon())
}
if (opponent.hasLost() || player.hasWon()) {
return Integer.MAX_VALUE;
}
}
int value = player.getLife();
value -= opponent.getLife();
PermanentEvaluator evaluator = new PermanentEvaluator();

View file

@ -58,11 +58,11 @@ public class MCTSNode {
private int visits = 0;
private int wins = 0;
private MCTSNode parent;
private List<MCTSNode> children = new ArrayList<MCTSNode>();
private final List<MCTSNode> children = new ArrayList<MCTSNode>();
private Ability action;
private Game game;
private Combat combat;
private String stateValue;
private final String stateValue;
private UUID playerId;
private boolean terminal = false;
@ -71,7 +71,7 @@ public class MCTSNode {
public MCTSNode(Game game) {
this.game = game;
this.stateValue = game.getState().getValue(false, game);
this.terminal = game.isGameOver();
this.terminal = game.gameOver(null);
setPlayer();
nodeCount = 1;
}
@ -79,7 +79,7 @@ public class MCTSNode {
protected MCTSNode(MCTSNode parent, Game game, Ability action) {
this.game = game;
this.stateValue = game.getState().getValue(false, game);
this.terminal = game.isGameOver();
this.terminal = game.gameOver(null);
this.parent = parent;
this.action = action;
setPlayer();
@ -90,22 +90,23 @@ public class MCTSNode {
this.game = game;
this.combat = combat;
this.stateValue = game.getState().getValue(false, game);
this.terminal = game.isGameOver();
this.terminal = game.gameOver(null);
this.parent = parent;
setPlayer();
nodeCount++;
}
private void setPlayer() {
if (game.getStep().getStepPart() == StepPart.PRIORITY)
if (game.getStep().getStepPart() == StepPart.PRIORITY) {
playerId = game.getPriorityPlayerId();
else {
if (game.getStep().getType() == PhaseStep.DECLARE_BLOCKERS)
} else {
if (game.getStep().getType() == PhaseStep.DECLARE_BLOCKERS) {
playerId = game.getCombat().getDefenders().iterator().next();
else
} else {
playerId = game.getActivePlayerId();
}
}
}
public MCTSNode select(UUID targetPlayerId) {
double bestValue = Double.NEGATIVE_INFINITY;

View file

@ -330,7 +330,7 @@ public class ComputerPlayer2 extends ComputerPlayer<ComputerPlayer2> implements
return GameStateEvaluator.evaluate(playerId, game);
}
int val;
if (node.depth > maxDepth || game.isGameOver()) {
if (node.depth > maxDepth || game.gameOver(null)) {
logger.debug(indent(node.depth) + "simulating -- reached end state");
val = GameStateEvaluator.evaluate(playerId, game);
}
@ -357,7 +357,7 @@ public class ComputerPlayer2 extends ComputerPlayer<ComputerPlayer2> implements
}
}
if (game.isGameOver()) {
if (game.gameOver(null)) {
val = GameStateEvaluator.evaluate(playerId, game);
}
else if (node.getChildren().size() > 0) {
@ -403,7 +403,7 @@ public class ComputerPlayer2 extends ComputerPlayer<ComputerPlayer2> implements
logger.debug(indent(node.depth) + "found useless action: " + action);
continue;
}
if (!sim.isGameOver() && action.isUsesStack()) {
if (!sim.gameOver(null) && action.isUsesStack()) {
// only pass if the last action uses the stack
sim.getPlayer(currentPlayer.getId()).pass(game);
sim.getPlayerList().getNext();
@ -587,7 +587,7 @@ public class ComputerPlayer2 extends ComputerPlayer<ComputerPlayer2> implements
break;
case CLEANUP:
game.getPhase().getStep().beginStep(game, activePlayerId);
if (!game.checkStateAndTriggered() && !game.isGameOver()) {
if (!game.checkStateAndTriggered() && !game.gameOver(null)) {
game.getState().setActivePlayerId(game.getState().getPlayerList(game.getActivePlayerId()).getNext());
game.getTurn().setPhase(new BeginningPhase());
game.getPhase().setStep(new UntapStep());

View file

@ -185,7 +185,7 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player {
logger.debug(indent(node.depth) + "interrupted");
return GameStateEvaluator.evaluate(playerId, game);
}
if (node.depth > maxDepth || game.isGameOver()) {
if (node.depth > maxDepth || game.gameOver(null)) {
logger.debug(indent(node.depth) + "simulating -- reached end state");
val = GameStateEvaluator.evaluate(playerId, game);
}
@ -205,7 +205,7 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player {
}
}
if (game.isGameOver()) {
if (game.gameOver(null)) {
val = GameStateEvaluator.evaluate(playerId, game);
}
else if (stepFinished) {
@ -406,7 +406,7 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player {
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_POST, sim.getActivePlayerId(), sim.getActivePlayerId()));
Combat simCombat = sim.getCombat().copy();
finishCombat(sim);
if (sim.isGameOver()) {
if (sim.gameOver(null)) {
val = GameStateEvaluator.evaluate(playerId, sim);
}
else if (!counter) {
@ -448,7 +448,7 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player {
return GameStateEvaluator.evaluate(playerId, game);
}
Integer val = null;
if (!game.isGameOver()) {
if (!game.gameOver(null)) {
logger.debug(indent(node.depth) + "simulating -- ending turn");
simulateToEnd(game);
game.getState().setActivePlayerId(game.getState().getPlayerList(game.getActivePlayerId()).getNext());
@ -476,7 +476,7 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player {
logger.debug("interrupted");
return;
}
if (!game.isGameOver()) {
if (!game.gameOver(null)) {
game.getPhase().setStep(step);
if (!step.skipStep(game, game.getActivePlayerId())) {
step.beginStep(game, game.getActivePlayerId());
@ -524,7 +524,7 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player {
logger.debug("interrupted");
return;
}
if (!game.isGameOver()) {
if (!game.gameOver(null)) {
game.getTurn().getPhase().endPhase(game, game.getActivePlayerId());
game.getTurn().setPhase(new EndPhase());
if (game.getTurn().getPhase().beginPhase(game, game.getActivePlayerId())) {

View file

@ -70,7 +70,7 @@ public class GameStateEvaluator {
public static int evaluate(UUID playerId, Game game, boolean ignoreTapped) {
Player player = game.getPlayer(playerId);
Player opponent = game.getPlayer(game.getOpponents(playerId).iterator().next());
if (game.isGameOver()) {
if (game.gameOver(null)) {
if (player.hasLost() || opponent.hasWon())
return LOSE_SCORE;
if (opponent.hasLost() || player.hasWon())

View file

@ -593,7 +593,6 @@ public class TableController {
* @return true if table can be closed
*/
public boolean endGameAndStartNextGame() {
boolean matchIsOver = false;
// get player that chooses who goes first
UUID choosingPlayerId = match.getChooser();
match.endGame();
@ -604,27 +603,24 @@ public class TableController {
}
GameManager.getInstance().removeGame(match.getGame().getId());
try {
if (!match.isMatchOver()) {
if (!match.hasEnded()) {
table.sideboard();
setupTimeout(Match.SIDEBOARD_TIME);
match.sideboard();
cancelTimeout();
if (!match.isMatchOver()) {
if (!match.hasEnded()) {
startGame(choosingPlayerId);
} else {
matchIsOver = true;
closeTable();
}
}
else {
// if match has only one game
matchIsOver = true;
closeTable();
}
} catch (GameException ex) {
logger.fatal(null, ex);
}
return matchIsOver;
return match.hasEnded();
}
/**

View file

@ -240,8 +240,8 @@ public class GameController implements GameCallback {
PriorityTimer timer = new PriorityTimer(count, delay, new Action() {
@Override
public void execute() throws MageException {
game.concede(initPlayerId);
logger.debug("Game timeout for player: " + initPlayerId + ". Conceding.");
game.timerTimeout(initPlayerId);
logger.debug(new StringBuilder("Game timeout for player: ").append(initPlayerId).append(". Conceding."));
}
});
timers.put(playerId, timer);
@ -399,7 +399,7 @@ public class GameController implements GameCallback {
.append(ConfigSettings.getInstance().getMaxSecondsIdle())
.append(" seconds ) - Auto concede.");
ChatManager.getInstance().broadcast(chatId, "", sb.toString() , MessageColor.BLACK, true, MessageType.STATUS);
concede(userId);
game.idleTimeout(getPlayerId(userId));
}
}

View file

@ -13,7 +13,7 @@ public enum TableState {
CONSTRUCTING ("Constructing"),
FINISHED ("Finished");
private String text;
private final String text;
TableState(String text) {
this.text = text;

View file

@ -122,7 +122,8 @@ public interface Game extends MageItem, Serializable {
UUID getActivePlayerId();
UUID getPriorityPlayerId();
void leave(UUID playerId);
boolean isGameOver();
boolean gameOver(UUID playerId);
boolean hasEnded();
Battlefield getBattlefield();
SpellStack getStack();
Exile getExile();
@ -218,6 +219,8 @@ public interface Game extends MageItem, Serializable {
void mulligan(UUID playerId);
void endMulligan(UUID playerId);
void quit(UUID playerId);
void timerTimeout(UUID playerId);
void idleTimeout(UUID playerId);
void concede(UUID playerId);
void undo(UUID playerId);
void emptyManaPools();

View file

@ -418,8 +418,26 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
}
}
/**
* Starts check if game over or if playerId is given
* let the player concede.
*
* @param playerId
* @return
*/
@Override
public synchronized boolean isGameOver() {
public synchronized boolean gameOver(UUID playerId) {
if (playerId == null) {
boolean result = checkIfGameIsOver();
return result;
} else {
leave(playerId);
return true;
}
}
private boolean checkIfGameIsOver() {
if (state.isGameOver()) {
return true;
}
@ -447,6 +465,11 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
return false;
}
@Override
public boolean hasEnded() {
return endTime != null;
}
@Override
public String getWinner() {
if (winnerId == null) {
@ -546,13 +569,13 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
Player player = getPlayer(players.get());
boolean wasPaused = state.isPaused();
state.resume();
if (!isGameOver()) {
if (!gameOver(null)) {
fireInformEvent(new StringBuilder("Turn ").append(state.getTurnNum()).toString());
if (checkStopOnTurnOption()) {
return;
}
state.getTurn().resumePlay(this, wasPaused);
if (!isPaused() && !isGameOver()) {
if (!isPaused() && !gameOver(null)) {
endOfTurn();
player = players.getNext(this);
state.setTurnNum(state.getTurnNum() + 1);
@ -562,10 +585,10 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
}
protected void play(UUID nextPlayerId) {
if (!isPaused() && !isGameOver()) {
if (!isPaused() && !gameOver(null)) {
PlayerList players = state.getPlayerList(nextPlayerId);
Player player = getPlayer(players.get());
while (!isPaused() && !isGameOver()) {
while (!isPaused() && !gameOver(null)) {
if (!playTurn(player)) {
break;
@ -583,7 +606,7 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
player = players.getNext(this);
}
}
if (isGameOver()) {
if (gameOver(null)) {
winnerId = findWinnersAndLosers();
logger.info(new StringBuilder("Game with gameId ").append(this.getId()).append(" ended."));
}
@ -597,7 +620,7 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
state.setActivePlayerId(player.getId());
player.becomesActivePlayer();
state.getTurn().play(this, player.getId());
if (isPaused() || isGameOver()) {
if (isPaused() || gameOver(null)) {
return false;
}
endOfTurn();
@ -866,8 +889,29 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
public synchronized void quit(UUID playerId) {
Player player = state.getPlayer(playerId);
if (player != null) {
fireInformEvent(player.getName() + " quits the match.");
player.quit(this);
}else {
logger.error(new StringBuilder("player not found - playerId: ").append(playerId));
}
}
@Override
public synchronized void timerTimeout(UUID playerId) {
Player player = state.getPlayer(playerId);
if (player != null) {
player.timerTimeout(this);
} else {
logger.error(new StringBuilder("player not found - playerId: ").append(playerId));
}
}
@Override
public synchronized void idleTimeout(UUID playerId) {
Player player = state.getPlayer(playerId);
if (player != null) {
player.idleTimeout(this);
} else {
logger.error(new StringBuilder("player not found - playerId: ").append(playerId));
}
}
@ -923,7 +967,7 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
int bookmark = 0;
clearAllBookmarks();
try {
while (!isPaused() && !isGameOver()) {
while (!isPaused() && !gameOver(null)) {
if (!resuming) {
state.getPlayers().resetPassed();
state.getPlayerList().setCurrent(activePlayerId);
@ -933,13 +977,13 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
}
fireUpdatePlayersEvent();
Player player;
while (!isPaused() && !isGameOver()) {
while (!isPaused() && !gameOver(null)) {
try {
//if (bookmark == 0)
//bookmark = bookmarkState();
player = getPlayer(state.getPlayerList().get());
state.setPriorityPlayerId(player.getId());
while (!player.isPassed() && player.isInGame() && !isPaused() && !isGameOver()) {
while (!player.isPassed() && player.isInGame() && !isPaused() && !gameOver(null)) {
if (!resuming) {
if (checkStateAndTriggered()) {
applyEffects();
@ -947,7 +991,7 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
//resetLKI();
applyEffects();
saveState(false);
if (isPaused() || isGameOver()) {
if (isPaused() || gameOver(null)) {
return;
}
// resetPassed should be called if player performs any action
@ -962,7 +1006,7 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
}
resetShortLivingLKI();
resuming = false;
if (isPaused() || isGameOver()) {
if (isPaused() || gameOver(null)) {
return;
}
if (allPassed()) {
@ -1164,9 +1208,9 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
public boolean checkStateAndTriggered() {
boolean somethingHappened = false;
//20091005 - 115.5
while (!isPaused() && !this.isGameOver()) {
while (!isPaused() && !gameOver(null)) {
if (!checkStateBasedActions() ) {
if (isPaused() || this.isGameOver() || !checkTriggered()) {
if (isPaused() || gameOver(null) || !checkTriggered()) {
break;
}
}
@ -1729,7 +1773,7 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
return;
}
player.leave();
if (this.isGameOver()) {
if (gameOver(null)) {
// no need to remove objects if only one player is left so the game is over
return;
}

View file

@ -113,7 +113,7 @@ public class Table implements Serializable {
public void initTournament() {
setState(TableState.DUELING);
tournament.setStepStartTime(new Date());
}
public void endTournament() {
@ -122,10 +122,12 @@ public class Table implements Serializable {
public void initDraft() {
setState(TableState.DRAFTING);
tournament.setStepStartTime(new Date());
}
public void construct() {
setState(TableState.CONSTRUCTING);
tournament.setStepStartTime(new Date());
}
/**

View file

@ -191,7 +191,7 @@ public class Combat implements Serializable, Copyable<Combat> {
//20101001 - 508.1d
checkAttackRequirements(player, game);
player.selectAttackers(game, attackerId);
if (game.isPaused() || game.isGameOver()) {
if (game.isPaused() || game.gameOver(null)) {
return;
}
checkAttackRestrictions(player, game);
@ -316,7 +316,7 @@ public class Combat implements Serializable, Copyable<Combat> {
}
while (choose) {
blockController.selectBlockers(game, defenderId);
if (game.isPaused() || game.isGameOver()) {
if (game.isPaused() || game.gameOver(null)) {
return;
}
if (!this.checkBlockRestrictions(defender, game)) {

View file

@ -48,7 +48,8 @@ public interface Match {
UUID getId();
String getName();
boolean isMatchOver();
boolean hasEnded();
boolean checkIfMatchEnds();
List<MatchPlayer> getPlayers();
MatchPlayer getPlayer(UUID playerId);

View file

@ -126,18 +126,29 @@ public abstract class MatchImpl implements Match {
}
@Override
public boolean isMatchOver() {
public boolean hasEnded() {
return endTime != null;
};
@Override
public boolean checkIfMatchEnds() {
int activePlayers = 0;
for (MatchPlayer player: players) {
if (!player.hasQuit()) {
MatchPlayer matchWinner = null;
for (MatchPlayer matchPlayer: players) {
if (!matchPlayer.hasQuit()) {
activePlayers++;
matchWinner = matchPlayer;
}
if (player.getWins() >= options.getWinsNeeded()) {
if (matchPlayer.getWins() >= options.getWinsNeeded()) {
matchPlayer.setMatchWinner(true);
endTime = new Date();
return true;
}
}
if (activePlayers < 2) {
if (matchWinner != null) {
matchWinner.setMatchWinner(true);
}
endTime = new Date();
return true;
}
@ -200,24 +211,28 @@ public abstract class MatchImpl implements Match {
@Override
public void endGame() {
Game game = getGame();
for (MatchPlayer player: this.players) {
Player p = game.getPlayer(player.getPlayer().getId());
if (p != null) {
for (MatchPlayer matchPlayer: this.players) {
Player player = game.getPlayer(matchPlayer.getPlayer().getId());
if (player != null) {
// get the left time from player priority timer
if (game.getPriorityTime() > 0) {
player.setPriorityTimeLeft(p.getPriorityTimeLeft());
matchPlayer.setPriorityTimeLeft(player.getPriorityTimeLeft());
}
if (p.hasQuit()) {
player.setQuit(true);
if (player.hasQuit()) {
matchPlayer.setQuit(true);
}
if (p.hasWon()) {
player.addWin();
if (player.hasTimerTimeout()) {
matchPlayer.setTimerTimeout(true);
}
if (p.hasLost()) {
player.addLose();
if (player.hasWon()) {
matchPlayer.addWin();
}
if (player.hasLost()) {
matchPlayer.addLose();
}
}
}
checkIfMatchEnds();
game.fireGameEndInfo();
}

View file

@ -39,12 +39,14 @@ import mage.players.Player;
public class MatchPlayer {
private int wins;
private int loses;
private boolean matchWinner;
private Deck deck;
private Player player;
private final String name;
private boolean quit;
private boolean timerTimeout;
private boolean doneSideboarding;
private int priorityTimeLeft;
@ -55,7 +57,9 @@ public class MatchPlayer {
this.loses = 0;
this.doneSideboarding = true;
this.quit = false;
this.timerTimeout = false;
this.name = player.getName();
this.matchWinner = false;
}
public int getPriorityTimeLeft() {
@ -126,6 +130,22 @@ public class MatchPlayer {
this.quit = quit;
}
public boolean hasTimerTimeout() {
return timerTimeout;
}
public void setTimerTimeout(boolean timerTimeout) {
this.timerTimeout = timerTimeout;
}
public boolean isMatchWinner() {
return matchWinner;
}
public void setMatchWinner(boolean matchWinner) {
this.matchWinner = matchWinner;
}
public void cleanUpOnMatchEnd() {
// Free resources that are not needed after match end
this.deck = null;

View file

@ -72,7 +72,7 @@ public class Round {
public boolean isRoundOver() {
boolean roundIsOver = true;
for (TournamentPairing pair: pairs) {
if (pair.getMatch() != null && !pair.getMatch().isMatchOver()) {
if (pair.getMatch() != null && !pair.getMatch().hasEnded()) {
roundIsOver = false;
} else {
if (!pair.isAlreadyPublished()) {

View file

@ -78,6 +78,10 @@ public interface Tournament {
// tournament times
Date getStartTime();
Date getEndTime();
Date getStepStartTime();
void setStepStartTime(Date date);
// tournament type
TournamentType getTournamentType();
void setTournamentType(TournamentType tournamentType);

View file

@ -74,6 +74,7 @@ public abstract class TournamentImpl implements Tournament {
protected Date startTime;
protected Date endTime;
protected Date stepStartTime;
protected boolean abort;
protected String tournamentState;
@ -244,7 +245,7 @@ public abstract class TournamentImpl implements Tournament {
for (Round round: rounds) {
for (TournamentPairing pair: round.getPairs()) {
Match match = pair.getMatch();
if (match != null && match.isMatchOver()) {
if (match != null && match.hasEnded()) {
TournamentPlayer tp1 = pair.getPlayer1();
TournamentPlayer tp2 = pair.getPlayer2();
MatchPlayer mp1 = match.getPlayer(pair.getPlayer1().getPlayer().getId());
@ -302,11 +303,11 @@ public abstract class TournamentImpl implements Tournament {
matchResult.append(p2.getPlayer().getName());
matchResult.append(" (").append(mp1.getWins());
if (mp1.hasQuit()) {
matchResult.append("Q");
matchResult.append(mp1.hasTimerTimeout()?"T":"Q");
}
matchResult.append("-").append(mp2.getWins());
if (mp2.hasQuit()) {
matchResult.append("Q");
matchResult.append(mp2.hasTimerTimeout()?"T":"Q");
}
matchResult.append(") ");
return matchResult.toString();
@ -479,4 +480,18 @@ public abstract class TournamentImpl implements Tournament {
this.tournamentState = tournamentState;
}
@Override
public Date getStepStartTime() {
if (stepStartTime != null) {
return new Date(stepStartTime.getTime());
}
return null;
}
@Override
public void setStepStartTime(Date stepStartTime) {
this.stepStartTime = stepStartTime;
}
}

View file

@ -72,7 +72,7 @@ public class TournamentPairing {
}
public void eliminatePlayers() {
if (match.isMatchOver()) {
if (match.hasEnded()) {
MatchPlayer mPlayer1 = match.getPlayer(player1.getPlayer().getId());
MatchPlayer mPlayer2 = match.getPlayer(player2.getPlayer().getId());
if (mPlayer1.hasQuit() || (!mPlayer2.hasQuit() && mPlayer1.getWins() < match.getWinsNeeded())) {

View file

@ -95,7 +95,7 @@ public abstract class Phase<T extends Phase<T>> implements Serializable {
}
public boolean play(Game game, UUID activePlayerId) {
if (game.isPaused() || game.isGameOver()) {
if (game.isPaused() || game.gameOver(null)) {
return false;
}
@ -104,7 +104,7 @@ public abstract class Phase<T extends Phase<T>> implements Serializable {
if (beginPhase(game, activePlayerId)) {
for (Step step: steps) {
if (game.isPaused() || game.isGameOver()) {
if (game.isPaused() || game.gameOver(null)) {
return false;
}
currentStep = step;
@ -115,7 +115,7 @@ public abstract class Phase<T extends Phase<T>> implements Serializable {
playStep(game);
}
}
if (game.isPaused() || game.isGameOver()) {
if (game.isPaused() || game.gameOver(null)) {
return false;
}
count++;
@ -136,7 +136,7 @@ public abstract class Phase<T extends Phase<T>> implements Serializable {
}
public boolean resumePlay(Game game, PhaseStep stepType, boolean wasPaused) {
if (game.isPaused() || game.isGameOver()) {
if (game.isPaused() || game.gameOver(null)) {
return false;
}
@ -150,7 +150,7 @@ public abstract class Phase<T extends Phase<T>> implements Serializable {
resumeStep(game, wasPaused);
while (it.hasNext()) {
step = it.next();
if (game.isPaused() || game.isGameOver()) {
if (game.isPaused() || game.gameOver(null)) {
return false;
}
currentStep = step;
@ -159,7 +159,7 @@ public abstract class Phase<T extends Phase<T>> implements Serializable {
}
}
if (game.isPaused() || game.isGameOver()) {
if (game.isPaused() || game.gameOver(null)) {
return false;
}
count++;
@ -194,10 +194,10 @@ public abstract class Phase<T extends Phase<T>> implements Serializable {
protected void playStep(Game game) {
if (!currentStep.skipStep(game, activePlayerId)) {
prePriority(game, activePlayerId);
if (!game.isPaused() && !game.isGameOver()) {
if (!game.isPaused() && !game.gameOver(null)) {
currentStep.priority(game, activePlayerId, false);
}
if (!game.isPaused() && !game.isGameOver()) {
if (!game.isPaused() && !game.gameOver(null)) {
postPriority(game, activePlayerId);
}
}
@ -219,11 +219,11 @@ public abstract class Phase<T extends Phase<T>> implements Serializable {
prePriority(game, activePlayerId);
}
case PRIORITY:
if (!game.isPaused() && !game.isGameOver()) {
if (!game.isPaused() && !game.gameOver(null)) {
currentStep.priority(game, activePlayerId, resuming);
}
case POST:
if (!game.isPaused() && !game.isGameOver()) {
if (!game.isPaused() && !game.gameOver(null)) {
postPriority(game, activePlayerId);
}
}

View file

@ -47,7 +47,7 @@ public class Turn implements Serializable {
private Phase currentPhase;
private UUID activePlayerId;
private List<Phase> phases = new ArrayList<Phase>();
private final List<Phase> phases = new ArrayList<>();
private boolean declareAttackersStepStarted = false;
public Turn() {
@ -109,7 +109,7 @@ public class Turn implements Serializable {
public void play(Game game, UUID activePlayerId) {
this.setDeclareAttackersStepStarted(false);
if (game.isPaused() || game.isGameOver()) {
if (game.isPaused() || game.gameOver(null)) {
return;
}
@ -123,7 +123,7 @@ public class Turn implements Serializable {
resetCounts();
game.getPlayer(activePlayerId).beginTurn(game);
for (Phase phase: phases) {
if (game.isPaused() || game.isGameOver()) {
if (game.isPaused() || game.gameOver(null)) {
return;
}
currentPhase = phase;
@ -168,7 +168,7 @@ public class Turn implements Serializable {
}
while (it.hasNext()) {
phase = it.next();
if (game.isPaused() || game.isGameOver()) {
if (game.isPaused() || game.gameOver(null)) {
return;
}
currentPhase = phase;

View file

@ -129,6 +129,10 @@ public interface Player extends MageItem, Copyable<Player> {
boolean hasWon();
boolean hasQuit();
void quit(Game game);
boolean hasTimerTimeout();
void timerTimeout(Game game);
boolean hasIdleTimeout();
void idleTimeout(Game game);
boolean hasLeft();
/**
* Player is still active in game (has not left, lost or won the game).

View file

@ -114,7 +114,10 @@ import mage.target.common.TargetDiscard;
import mage.watchers.common.BloodthirstWatcher;
import org.apache.log4j.Logger;
/**
*
* * @param <T>
*/
public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Serializable {
private static final transient Logger log = Logger.getLogger(PlayerImpl.class);
@ -160,6 +163,10 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
protected boolean left;
// set if the player quits the complete match
protected boolean quit;
// set if the player lost match because of priority timeout
protected boolean timerTimeout;
// set if the player lost match because of idle timeout
protected boolean idleTimeout;
protected RangeOfInfluence range;
protected Set<UUID> inRange = new HashSet<>();
@ -234,6 +241,8 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
this.left = player.left;
this.quit = player.quit;
this.timerTimeout = player.timerTimeout;
this.idleTimeout = player.idleTimeout;
this.range = player.range;
this.canGainLife = player.canGainLife;
this.canLoseLife = player.canLoseLife;
@ -288,6 +297,8 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
this.left = player.hasLeft();
this.quit = player.hasQuit();
this.timerTimeout = player.hasTimerTimeout();
this.idleTimeout = player.hasIdleTimeout();
this.range = player.getRange();
this.canGainLife = player.isCanGainLife();
this.canLoseLife = player.isCanLoseLife();
@ -343,7 +354,11 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
this.wins = false;
this.loses = false;
this.left = false;
this.quit = false; // reset is neccessary because in tournament player will be used for each round
// reset is neccessary because in tournament player will be used for each round
this.quit = false;
this.timerTimeout = false;
this.idleTimeout = false;
this.passed = false;
this.passedTurn = false;
this.passedAllTurns = false;
@ -732,7 +747,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
resetStoredBookmark(game);
return true;
}
if (!game.isGameOver()) { // if player left or game is over no undo is possible - this could lead to wrong winner
if (!game.hasEnded()) { // if player left or game is over no undo is possible - this could lead to wrong winner
game.restoreState(bookmark);
}
}
@ -1475,13 +1490,30 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
@Override
public void quit(Game game) {
game.informPlayers(new StringBuilder(getName()).append(" quits the match.").toString());
quit = true;
this.concede(game);
}
@Override
public void timerTimeout(Game game) {
game.informPlayers(new StringBuilder(getName()).append(" has run out of time. Loosing the Match.").toString());
quit = true;
timerTimeout = true;
this.concede(game);
}
@Override
public void idleTimeout(Game game) {
game.informPlayers(new StringBuilder(getName()).append(" has run out of time. Loosing the Match.").toString());
quit = true;
idleTimeout = true;
this.concede(game);
}
@Override
public void concede(Game game) {
game.leave(playerId);
game.gameOver(playerId);
lost(game);
this.left = true;
}
@ -2117,6 +2149,16 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
return quit;
}
@Override
public boolean hasTimerTimeout() {
return timerTimeout;
}
@Override
public boolean hasIdleTimeout() {
return idleTimeout;
}
@Override
public void setReachedNextTurnAfterLeaving(boolean reachedNextTurnAfterLeaving) {
this.reachedNextTurnAfterLeaving = reachedNextTurnAfterLeaving;