Moved the creation of games and broadcasting outside of tournament lock to prevent theoretical deadlocks. These actions are now made through a list of actions returned from tournament advance method.

This commit is contained in:
MarcinSc
2024-08-28 11:52:41 +07:00
parent 55644ef77f
commit d433c4d859
8 changed files with 106 additions and 44 deletions

View File

@@ -12,6 +12,9 @@ import com.gempukku.lotro.draft.DraftPack;
import com.gempukku.lotro.game.*;
import com.gempukku.lotro.logic.vo.LotroDeck;
import com.gempukku.lotro.packs.ProductLibrary;
import com.gempukku.lotro.tournament.action.BroadcastAction;
import com.gempukku.lotro.tournament.action.CreateGameAction;
import com.gempukku.lotro.tournament.action.TournamentProcessAction;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
@@ -288,47 +291,45 @@ public class DefaultTournament implements Tournament {
}
@Override
public boolean advanceTournament(TournamentCallback tournamentCallback, CollectionsManager collectionsManager) {
public List<TournamentProcessAction> advanceTournament(CollectionsManager collectionsManager) {
writeLock.lock();
try {
boolean result = false;
List<TournamentProcessAction> result = new LinkedList<>();
if (_nextTask == null) {
if (_tournamentStage == Stage.DRAFT) {
_draft.advanceDraft(tournamentCallback);
// Temporary - don't want to involve draft in this
_draft.advanceDraft(null);
if (_draft.isFinished()) {
tournamentCallback.broadcastMessage("Drafting in tournament " + _tournamentName + " is finished, starting deck building");
result.add(new BroadcastAction("Drafting in tournament " + _tournamentName + " is finished, starting deck building"));
_tournamentStage = Stage.DECK_BUILDING;
_tournamentService.updateTournamentStage(_tournamentId, _tournamentStage);
_deckBuildStartTime = System.currentTimeMillis();
_draft = null;
result = true;
}
}
if (_tournamentStage == Stage.DECK_BUILDING) {
if (_deckBuildStartTime + DeckBuildTime < System.currentTimeMillis()
if (_deckBuildStartTime + DeckBuildTime > System.currentTimeMillis()
|| _playerDecks.size() == _players.size()) {
_tournamentStage = Stage.PLAYING_GAMES;
_tournamentService.updateTournamentStage(_tournamentId, _tournamentStage);
result = true;
result.add(new BroadcastAction("Deck building in tournament " + _tournamentName + " has finished"));
}
}
if (_tournamentStage == Stage.PLAYING_GAMES) {
if (_currentlyPlayingPlayers.isEmpty()) {
if (_pairingMechanism.isFinished(_tournamentRound, _players, _droppedPlayers)) {
finishTournament(tournamentCallback, collectionsManager);
result.add(finishTournament(collectionsManager));
} else {
tournamentCallback.broadcastMessage("Tournament " + _tournamentName + " will start round "+(_tournamentRound+1)+" in 1 minute.");
result.add(new BroadcastAction("Tournament " + _tournamentName + " will start round "+(_tournamentRound+1)+" in 1 minute."));
_nextTask = new PairPlayers();
}
result = true;
}
}
}
if (_nextTask != null && _nextTask.getExecuteAfter() <= System.currentTimeMillis()) {
TournamentTask task = _nextTask;
_nextTask = null;
task.executeTask(tournamentCallback, collectionsManager);
result = true;
task.executeTask(result, collectionsManager);
}
return result;
} finally {
@@ -351,11 +352,11 @@ public class DefaultTournament implements Tournament {
}
}
private void finishTournament(TournamentCallback tournamentCallback, CollectionsManager collectionsManager) {
private TournamentProcessAction finishTournament(CollectionsManager collectionsManager) {
_tournamentStage = Stage.FINISHED;
_tournamentService.updateTournamentStage(_tournamentId, _tournamentStage);
tournamentCallback.broadcastMessage("Tournament " + _tournamentName + " is finished");
awardPrizes(collectionsManager);
return new BroadcastAction("Tournament " + _tournamentName + " is finished");
}
private void awardPrizes(CollectionsManager collectionsManager) {
@@ -370,12 +371,12 @@ public class DefaultTournament implements Tournament {
}
}
private void createNewGame(TournamentCallback tournamentCallback, String playerOne, String playerTwo) {
tournamentCallback.createGame(playerOne, _playerDecks.get(playerOne),
private TournamentProcessAction createNewGame(String playerOne, String playerTwo) {
return new CreateGameAction(playerOne, _playerDecks.get(playerOne),
playerTwo, _playerDecks.get(playerTwo));
}
private void doPairing(TournamentCallback tournamentCallback, CollectionsManager collectionsManager) {
private void doPairing(List<TournamentProcessAction> actions, CollectionsManager collectionsManager) {
_tournamentRound++;
_tournamentService.updateTournamentRound(_tournamentId, _tournamentRound);
Map<String, String> pairingResults = new HashMap<>();
@@ -386,7 +387,7 @@ public class DefaultTournament implements Tournament {
boolean finished = _pairingMechanism.pairPlayers(_tournamentRound, _players, _droppedPlayers, _playerByes,
getCurrentStandings(), previouslyPaired, pairingResults, byeResults);
if (finished) {
finishTournament(tournamentCallback, collectionsManager);
actions.add(finishTournament(collectionsManager));
} else {
for (Map.Entry<String, String> pairing : pairingResults.entrySet()) {
String playerOne = pairing.getKey();
@@ -394,11 +395,11 @@ public class DefaultTournament implements Tournament {
_tournamentService.addMatch(_tournamentId, _tournamentRound, playerOne, playerTwo);
_currentlyPlayingPlayers.add(playerOne);
_currentlyPlayingPlayers.add(playerTwo);
createNewGame(tournamentCallback, playerOne, playerTwo);
actions.add(createNewGame(playerOne, playerTwo));
}
if (!byeResults.isEmpty()) {
tournamentCallback.broadcastMessage("Bye awarded to: "+ StringUtils.join(byeResults, ", "));
actions.add(new BroadcastAction("Bye awarded to: "+ StringUtils.join(byeResults, ", ")));
}
for (String bye : byeResults) {
@@ -424,8 +425,8 @@ public class DefaultTournament implements Tournament {
private final long _taskStart = System.currentTimeMillis() + PairingDelayTime;
@Override
public void executeTask(TournamentCallback tournamentCallback, CollectionsManager collectionsManager) {
doPairing(tournamentCallback, collectionsManager);
public void executeTask(List<TournamentProcessAction> actions, CollectionsManager collectionsManager) {
doPairing(actions, collectionsManager);
}
@Override
@@ -442,11 +443,11 @@ public class DefaultTournament implements Tournament {
}
@Override
public void executeTask(TournamentCallback tournamentCallback, CollectionsManager collectionsManager) {
public void executeTask(List<TournamentProcessAction> actions, CollectionsManager collectionsManager) {
for (Map.Entry<String, String> pairings : _gamesToCreate.entrySet()) {
String playerOne = pairings.getKey();
String playerTwo = pairings.getValue();
createNewGame(tournamentCallback, playerOne, playerTwo);
actions.add(createNewGame(playerOne, playerTwo));
}
}

View File

@@ -6,11 +6,9 @@ import com.gempukku.lotro.competitive.PlayerStanding;
import com.gempukku.lotro.db.vo.CollectionType;
import com.gempukku.lotro.draft.Draft;
import com.gempukku.lotro.game.CardNotFoundException;
import com.gempukku.lotro.game.LotroCardBlueprintLibrary;
import com.gempukku.lotro.game.SortAndFilterCards;
import com.gempukku.lotro.game.formats.LotroFormatLibrary;
import com.gempukku.lotro.logic.vo.LotroDeck;
import com.gempukku.lotro.packs.ProductLibrary;
import com.gempukku.lotro.tournament.action.TournamentProcessAction;
import java.util.List;
@@ -75,7 +73,7 @@ public interface Tournament {
public int getPlayersInCompetitionCount();
public String getPlayerList();
public boolean advanceTournament(TournamentCallback tournamentCallback, CollectionsManager collectionsManager);
public List<TournamentProcessAction> advanceTournament(CollectionsManager collectionsManager);
public void reportGameFinished(String winner, String loser);

View File

@@ -14,6 +14,7 @@ import com.gempukku.lotro.hall.HallServer;
import com.gempukku.lotro.logic.vo.LotroDeck;
import com.gempukku.lotro.packs.DraftPackStorage;
import com.gempukku.lotro.packs.ProductLibrary;
import com.gempukku.lotro.tournament.action.TournamentProcessAction;
import java.io.IOException;
import java.sql.SQLException;
@@ -169,7 +170,12 @@ public class TournamentService {
var tourneyID = entry.getKey();
var tourney = entry.getValue();
tournamentsChanged |= tourney.advanceTournament(hall.getTournamentCallback(tourney), _collectionsManager);
TournamentCallback tournamentCallback = hall.getTournamentCallback(tourney);
List<TournamentProcessAction> actions = tourney.advanceTournament(_collectionsManager);
tournamentsChanged |= !actions.isEmpty();
for (TournamentProcessAction action : actions) {
action.process(tournamentCallback);
}
if (tourney.getTournamentStage() == Tournament.Stage.FINISHED)
_activeTournaments.remove(tourneyID);
}

View File

@@ -1,9 +1,12 @@
package com.gempukku.lotro.tournament;
import com.gempukku.lotro.collection.CollectionsManager;
import com.gempukku.lotro.tournament.action.TournamentProcessAction;
import java.util.List;
public interface TournamentTask {
public void executeTask(TournamentCallback tournamentCallback, CollectionsManager collectionsManager);
public void executeTask(List<TournamentProcessAction> actions, CollectionsManager collectionsManager);
public long getExecuteAfter();
}

View File

@@ -0,0 +1,16 @@
package com.gempukku.lotro.tournament.action;
import com.gempukku.lotro.tournament.TournamentCallback;
public class BroadcastAction implements TournamentProcessAction {
private final String message;
public BroadcastAction(String message) {
this.message = message;
}
@Override
public void process(TournamentCallback callback) {
callback.broadcastMessage(message);
}
}

View File

@@ -0,0 +1,23 @@
package com.gempukku.lotro.tournament.action;
import com.gempukku.lotro.logic.vo.LotroDeck;
import com.gempukku.lotro.tournament.TournamentCallback;
public class CreateGameAction implements TournamentProcessAction{
private String playerOne;
private LotroDeck playerOneDeck;
private String playerTwo;
private LotroDeck playerTwoDeck;
public CreateGameAction(String playerOne, LotroDeck playerOneDeck, String playerTwo, LotroDeck playerTwoDeck) {
this.playerOne = playerOne;
this.playerOneDeck = playerOneDeck;
this.playerTwo = playerTwo;
this.playerTwoDeck = playerTwoDeck;
}
@Override
public void process(TournamentCallback callback) {
callback.createGame(playerOne, playerOneDeck, playerTwo, playerTwoDeck);
}
}

View File

@@ -0,0 +1,7 @@
package com.gempukku.lotro.tournament.action;
import com.gempukku.lotro.tournament.TournamentCallback;
public interface TournamentProcessAction {
void process(TournamentCallback callback);
}

View File

@@ -6,6 +6,7 @@ import com.gempukku.lotro.db.vo.CollectionType;
import com.gempukku.lotro.draft.DraftPack;
import com.gempukku.lotro.logic.vo.LotroDeck;
import com.gempukku.lotro.packs.ProductLibrary;
import com.gempukku.lotro.tournament.action.TournamentProcessAction;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.internal.verification.Times;
@@ -110,13 +111,13 @@ public class DefaultTournamentTest extends AbstractAtTest {
}
).when(tournamentCallback).broadcastMessage(Mockito.anyString());
tournament.advanceTournament(tournamentCallback, collectionsManager);
advanceTournament(tournament, collectionsManager, tournamentCallback);
Mockito.verify(tournamentCallback).broadcastMessage(Mockito.anyString());
Mockito.verifyNoMoreInteractions(collectionsManager, tournamentCallback);
Thread.sleep(_waitForPairingsTime);
tournament.advanceTournament(tournamentCallback, collectionsManager);
advanceTournament(tournament, collectionsManager, tournamentCallback);
Mockito.verify(tournamentCallback, new Times(1)).createGame("p1", playerDecks.get("p1"), "p2", playerDecks.get("p2"));
Mockito.verify(tournamentCallback, new Times(1)).createGame("p3", playerDecks.get("p3"), "p4", playerDecks.get("p4"));
@@ -127,32 +128,32 @@ public class DefaultTournamentTest extends AbstractAtTest {
assertEquals(1, tournament.getCurrentRound());
tournament.advanceTournament(tournamentCallback, collectionsManager);
advanceTournament(tournament, collectionsManager, tournamentCallback);
Mockito.verifyNoMoreInteractions(collectionsManager, tournamentCallback);
tournament.reportGameFinished("p1", "p2");
tournament.advanceTournament(tournamentCallback, collectionsManager);
advanceTournament(tournament, collectionsManager, tournamentCallback);
Mockito.verifyNoMoreInteractions(collectionsManager, tournamentCallback);
tournament.reportGameFinished("p3", "p4");
tournament.advanceTournament(tournamentCallback, collectionsManager);
advanceTournament(tournament, collectionsManager, tournamentCallback);
Mockito.verifyNoMoreInteractions(collectionsManager, tournamentCallback);
tournament.reportGameFinished("p5", "p6");
tournament.advanceTournament(tournamentCallback, collectionsManager);
advanceTournament(tournament, collectionsManager, tournamentCallback);
Mockito.verifyNoMoreInteractions(collectionsManager, tournamentCallback);
tournament.reportGameFinished("p7", "p8");
tournament.advanceTournament(tournamentCallback, collectionsManager);
advanceTournament(tournament, collectionsManager, tournamentCallback);
Mockito.verify(tournamentCallback, new Times(2)).broadcastMessage(Mockito.anyString());
Mockito.verifyNoMoreInteractions(collectionsManager, tournamentCallback);
Thread.sleep(_waitForPairingsTime);
tournament.advanceTournament(tournamentCallback, collectionsManager);
advanceTournament(tournament, collectionsManager, tournamentCallback);
Mockito.verify(tournamentCallback, new Times(1)).createGame("p1", playerDecks.get("p1"), "p3", playerDecks.get("p3"));
Mockito.verify(tournamentCallback, new Times(1)).createGame("p5", playerDecks.get("p5"), "p7", playerDecks.get("p7"));
@@ -161,22 +162,22 @@ public class DefaultTournamentTest extends AbstractAtTest {
assertEquals(2, tournament.getCurrentRound());
tournament.advanceTournament(tournamentCallback, collectionsManager);
advanceTournament(tournament, collectionsManager, tournamentCallback);
Mockito.verifyNoMoreInteractions(collectionsManager, tournamentCallback);
tournament.reportGameFinished("p1", "p3");
tournament.advanceTournament(tournamentCallback, collectionsManager);
advanceTournament(tournament, collectionsManager, tournamentCallback);
Mockito.verifyNoMoreInteractions(collectionsManager, tournamentCallback);
tournament.reportGameFinished("p5", "p7");
tournament.advanceTournament(tournamentCallback, collectionsManager);
advanceTournament(tournament, collectionsManager, tournamentCallback);
Mockito.verify(tournamentCallback, new Times(3)).broadcastMessage(Mockito.anyString());
Mockito.verifyNoMoreInteractions(collectionsManager, tournamentCallback);
Thread.sleep(_waitForPairingsTime);
tournament.advanceTournament(tournamentCallback, collectionsManager);
advanceTournament(tournament, collectionsManager, tournamentCallback);
Mockito.verify(tournamentCallback, new Times(1)).createGame("p1", playerDecks.get("p1"), "p5", playerDecks.get("p5"));
@@ -184,12 +185,12 @@ public class DefaultTournamentTest extends AbstractAtTest {
assertEquals(3, tournament.getCurrentRound());
tournament.advanceTournament(tournamentCallback, collectionsManager);
advanceTournament(tournament, collectionsManager, tournamentCallback);
Mockito.verifyNoMoreInteractions(collectionsManager, tournamentCallback);
tournament.reportGameFinished("p1", "p5");
tournament.advanceTournament(tournamentCallback, collectionsManager);
advanceTournament(tournament, collectionsManager, tournamentCallback);
Mockito.verify(tournamentCallback, new Times(4)).broadcastMessage(Mockito.anyString());
Mockito.verify(collectionsManager).addItemsToPlayerCollection(Mockito.eq(true), Mockito.anyString(), Mockito.eq("p1"), Mockito.eq(CollectionType.MY_CARDS), Mockito.anyCollection());
@@ -202,4 +203,11 @@ public class DefaultTournamentTest extends AbstractAtTest {
assertEquals(3, tournament.getCurrentRound());
assertEquals(Tournament.Stage.FINISHED, tournament.getTournamentStage());
}
private void advanceTournament(Tournament tournament, CollectionsManager collectionsManager, TournamentCallback callback) {
List<TournamentProcessAction> actions = tournament.advanceTournament(collectionsManager);
for (TournamentProcessAction action : actions) {
action.process(callback);
}
}
}