Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 19 additions & 17 deletions src/main/java/com/faforever/client/game/GameRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import com.faforever.client.preferences.PreferencesService;
import com.faforever.client.remote.FafServerAccessor;
import com.faforever.client.replay.ReplayServer;
import com.faforever.client.task.TaskService;
import com.faforever.client.theme.UiService;
import com.faforever.client.ui.StageHolder;
import com.faforever.client.util.ConcurrentUtil;
Expand Down Expand Up @@ -118,6 +119,7 @@ public class GameRunner implements InitializingBean {
private final NotificationPrefs notificationPrefs;
private final FxApplicationThreadExecutor fxApplicationThreadExecutor;
private final LogAnalyzerService logAnalyzerService;
private final TaskService taskService;

private final MaskPatternLayout logMasker = new MaskPatternLayout();
private final SimpleObjectProperty<Integer> runningGameId = new SimpleObjectProperty<>();
Expand Down Expand Up @@ -148,7 +150,7 @@ public void afterPropertiesSet() {

fafServerAccessor.getEvents(NoticeInfo.class)
.filter(notice -> Objects.equals(notice.getStyle(), "kill"))
.doOnNext(notice -> {
.doOnNext(_ -> {
log.info("Game close requested by server");
String linksRules = clientProperties.getLinks().get("linksRules");
ImmediateNotification notification = new ImmediateNotification(i18n.get("game.kicked.title"),
Expand All @@ -163,8 +165,8 @@ public void afterPropertiesSet() {
.subscribe();


fafServerAccessor.connectionStateProperty().addListener((observable, oldValue, newValue) -> {
if (isRunning() && newValue == ConnectionState.CONNECTED && oldValue != ConnectionState.CONNECTED) {
fafServerAccessor.connectionStateProperty().subscribe((oldState, newState) -> {
if (isRunning() && newState == ConnectionState.CONNECTED && oldState != ConnectionState.CONNECTED) {
fafServerAccessor.restoreGameSession(runningGameId.get());
}
});
Expand All @@ -183,16 +185,16 @@ CompletableFuture<Void> startOnlineGame(GameLaunchResponse gameLaunchResponse) {
String mapFolderName = gameLaunchResponse.getMapName();
CompletableFuture<Void> downloadMapFuture = mapFolderName == null ? completedFuture(
null) : mapService.downloadIfNecessary(mapFolderName).toFuture();
CompletableFuture<League> leagueFuture = hasLeague ? completedFuture(null) : getDivisionInfo(
leaderboard).toFuture();
CompletableFuture<Integer> startReplayServerFuture = replayServer.start(uid);
CompletableFuture<Integer> startIceAdapterFuture = startIceAdapter(uid);

return CompletableFuture.allOf(downloadMapFuture, leagueFuture, startIceAdapterFuture, startReplayServerFuture)
.thenApply(_ -> gameMapper.map(gameLaunchResponse, leagueFuture.join()))
.thenApply(parameters -> launchOnlineGame(parameters, startIceAdapterFuture.join(),
startReplayServerFuture.join()))
.whenCompleteAsync((process, throwable) -> {

CompletableFuture<League> loadLeagueInfoFuture = hasLeague ? completedFuture(null) : taskService.submitFutureTask("league.loadInfo", () -> getDivisionInfo(leaderboard).toFuture());
CompletableFuture<Integer> runReplayServerFuture = taskService.submitFutureTask("replayServer.connecting", () -> replayServer.start(uid));
CompletableFuture<Integer> runIceAdapterFuture = taskService.submitFutureTask("iceAdapter.connecting", () -> startIceAdapter(uid));

return CompletableFuture.allOf(downloadMapFuture, loadLeagueInfoFuture, runReplayServerFuture, runIceAdapterFuture)
.thenApply(_ -> gameMapper.map(gameLaunchResponse, loadLeagueInfoFuture.join()))
.thenApply(parameters -> launchOnlineGame(parameters, runIceAdapterFuture.join(),
runReplayServerFuture.join()))
.whenCompleteAsync((process, _) -> {
if (process != null) {
this.process.set(process);
runningGameId.set(uid);
Expand Down Expand Up @@ -483,7 +485,7 @@ private Optional<Parent> getAnalysisButtonIfNecessary(Optional<String> logFileCo
infoIcon.getStyleClass().add("info-icon");
final Button showAnalysisBtn = new Button(i18n.get("game.log.analysis.solutionBtn"), infoIcon);
showAnalysisBtn.setDefaultButton(true);
showAnalysisBtn.setOnAction(event -> notificationService.addNotification(
showAnalysisBtn.setOnAction(_ -> notificationService.addNotification(
new ImmediateNotification(i18n.get("game.log.analysis"), message.toString(), WARN, actions)));

return showAnalysisBtn;
Expand All @@ -509,7 +511,7 @@ public void launchTutorial(MapVersion mapVersion, String technicalMapName) {
}

if (!preferencesService.hasValidGamePath()) {
gamePathHandler.chooseAndValidateGameDirectory().thenAccept(path -> launchTutorial(mapVersion, technicalMapName));
gamePathHandler.chooseAndValidateGameDirectory().thenAccept(_ -> launchTutorial(mapVersion, technicalMapName));
return;
}

Expand Down Expand Up @@ -547,12 +549,12 @@ public void startOffline() {
}

if (!preferencesService.hasValidGamePath()) {
gamePathHandler.chooseAndValidateGameDirectory().thenAccept(path -> startOffline());
gamePathHandler.chooseAndValidateGameDirectory().thenAccept(_ -> startOffline());
return;
}

CompletableFuture.supplyAsync(() -> forgedAllianceLaunchService.launchOfflineGame(null))
.whenCompleteAsync((process, throwable) -> {
.whenCompleteAsync((process, _) -> {
if (process != null) {
this.process.set(process);
}
Expand Down
10 changes: 7 additions & 3 deletions src/main/java/com/faforever/client/task/CompletableTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ public abstract class CompletableTask<V> extends Task<V> implements PrioritizedC
private final CompletableFuture<V> future;
private Priority priority;

public CompletableTask() {
this(Priority.MEDIUM);
}

public CompletableTask(Priority priority) {
this.priority = priority;
this.future = new CompletableFuture<>();
setOnCancelled(event -> future.cancel(true));
setOnFailed(event -> future.completeExceptionally(getException()));
setOnSucceeded(event -> future.complete(getValue()));
setOnCancelled(_ -> future.cancel(true));
setOnFailed(_ -> future.completeExceptionally(getException()));
setOnSucceeded(_ -> future.complete(getValue()));
}

@Override
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/com/faforever/client/task/SimpleTask.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.faforever.client.task;

import org.apache.commons.lang3.StringUtils;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.function.Supplier;

public class SimpleTask<V> extends CompletableTask<V> {

private final Supplier<? extends CompletableFuture<V>> futureSupplier;

public SimpleTask(Supplier<? extends CompletableFuture<V>> futureSupplier) {
this(null, futureSupplier);
}

public SimpleTask(String title, Supplier<? extends CompletableFuture<V>> futureSupplier) {
this.futureSupplier = futureSupplier;
updateTitle(StringUtils.defaultIfBlank(title, StringUtils.EMPTY));
}

@Override
protected V call() throws Exception {
Future<V> task = futureSupplier.get();
return task.get();
}
}
17 changes: 15 additions & 2 deletions src/main/java/com/faforever/client/task/TaskService.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.faforever.client.task;

import com.faforever.client.fx.FxApplicationThreadExecutor;
import com.faforever.client.i18n.I18n;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Worker;
Expand All @@ -9,7 +10,9 @@
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.function.Supplier;

/**
* Enqueues and runs tasks in background. Services that need to run a task (tasks that finish, not long-running
Expand All @@ -25,6 +28,7 @@ public class TaskService {

private final ExecutorService taskExecutor;
private final FxApplicationThreadExecutor fxApplicationThreadExecutor;
private final I18n i18n;

private final ObservableList<Worker<?>> activeTasks = FXCollections.synchronizedObservableList(FXCollections.observableArrayList());
private final ObservableList<Worker<?>> unmodifiableObservableList = FXCollections.unmodifiableObservableList(activeTasks);
Expand All @@ -36,8 +40,8 @@ public class TaskService {
* @param task the task to execute
*/
public <T extends PrioritizedCompletableTask<?>> T submitTask(T task) {
task.getFuture().whenComplete((o, throwable) -> {
activeTasks.remove(task);
task.getFuture().whenComplete((_, throwable) -> {
fxApplicationThreadExecutor.execute(() -> activeTasks.remove(task));
if (throwable != null) {
log.error("Task failed", throwable);
}
Expand All @@ -50,6 +54,15 @@ public <T extends PrioritizedCompletableTask<?>> T submitTask(T task) {
return task;
}

public <V> CompletableFuture<V> submitFutureTask(Supplier<CompletableFuture<V>> task) {
return submitFutureTask(null, task);
}

public <V> CompletableFuture<V> submitFutureTask(String titleI18nKey, Supplier<CompletableFuture<V>> task) {
String title = titleI18nKey != null ? i18n.get(titleI18nKey) : null;
return submitTask(new SimpleTask<>(title, task)).getFuture();
}

public ObservableList<Worker<?>> getActiveWorkers() {
return unmodifiableObservableList;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ protected void onInitialize() {
}
}));

JavaFxUtil.addListener(taskService.getActiveWorkers(), (Observable observable) -> {
JavaFxUtil.addListener(taskService.getActiveWorkers(), (Observable _) -> {
Collection<Worker<?>> runningWorkers = taskService.getActiveWorkers();
if (runningWorkers.isEmpty()) {
setCurrentWorkerInStatusBar(null);
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/i18n/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1314,3 +1314,6 @@ teammatchmaking.searchButton.memberInGame = Can't join matchmaking while someone
teammatchmaking.searchButton.inParty = The party leader will start matchmaking
teammatchmaking.searchButton.inQueue = Your party is now searching for a game
leaderboard.tmm_3v3.name = 3v3
iceAdapter.connecting = Connecting to ICE adapter
replayServer.connecting = Connecting to replay server
league.loadInfo = Loading a league information
13 changes: 13 additions & 0 deletions src/test/java/com/faforever/client/game/GameRunnerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
import com.faforever.client.preferences.PreferencesService;
import com.faforever.client.remote.FafServerAccessor;
import com.faforever.client.replay.ReplayServer;
import com.faforever.client.task.SimpleTask;
import com.faforever.client.task.TaskService;
import com.faforever.client.test.ServiceTest;
import com.faforever.client.theme.UiService;
import com.faforever.client.ui.StageHolder;
Expand All @@ -51,6 +53,7 @@
import org.junit.jupiter.api.Test;
import org.mapstruct.factory.Mappers;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
Expand All @@ -62,6 +65,7 @@
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.function.Supplier;

import static java.util.concurrent.CompletableFuture.completedFuture;
import static org.junit.jupiter.api.Assertions.assertEquals;
Expand Down Expand Up @@ -135,6 +139,8 @@ public class GameRunnerTest extends ServiceTest {
@Mock
private FxApplicationThreadExecutor fxApplicationThreadExecutor;
@Mock
private TaskService taskService;
@Mock
private GamePathHandler gamePathHandler;
@Spy
private GameMapper gameMapper = Mappers.getMapper(GameMapper.class);
Expand All @@ -144,6 +150,8 @@ public class GameRunnerTest extends ServiceTest {
private LastGamePrefs lastGamePrefs;
@Spy
private NotificationPrefs notificationPrefs;
@Captor
private ArgumentCaptor<Supplier<CompletableFuture<Object>>> simpleTaskCaptor;

@Mock
private EnterPasswordController enterPasswordController;
Expand Down Expand Up @@ -194,6 +202,11 @@ private void mockStartGameProcess(GameLaunchResponse gameLaunchResponse) throws
lenient().when(iceAdapter.start(anyInt(), anyBoolean())).thenReturn(completedFuture(GPG_PORT));
lenient().when(coturnService.getIceSession(anyInt()))
.thenReturn(Mono.just(new IceSession("someSessionId", false, List.of())));
lenient().when(taskService.submitFutureTask(anyString(), simpleTaskCaptor.capture())).thenAnswer(_ -> {
CompletableFuture<Object> task = simpleTaskCaptor.getValue().get();
task.join();
return task;
});
lenient().when(process.onExit()).thenReturn(new CompletableFuture<>());
lenient().when(process.exitValue()).thenReturn(0);
lenient().when(process.isAlive()).thenReturn(true);
Expand Down