Skip to content

Commit 14e3664

Browse files
authored
Merge branch 'develop' into feature/veto-system
2 parents 2b5abde + da6d6df commit 14e3664

File tree

8 files changed

+85
-29
lines changed

8 files changed

+85
-29
lines changed

readme.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@
1010

1111
The official client for [Forged Alliance Forever (FAF)](https://www.faforever.com/)
1212

13-
## Support Development
14-
15-
Post a bounty on Issue Hunt. You can reward and financially help developers that solve the issue. For new features, it can be helpful to first ask whether other people also see your change as an improvement. This helps avoid feature requests that will not be accepted.
16-
17-
[![Issue hunt](https://github.com/BoostIO/issuehunt-materials/raw/master/v1/issuehunt-button-v1.svg?sanitize=true)](https://issuehunt.io/r/FAForever/downlords-faf-client)
18-
1913
## How To Run
2014
1. Use [Temurin](https://adoptium.net/) 25 or Oracle JDK 25 (others might not work)
2115
1. Clone the project with Git

src/main/java/com/faforever/client/game/GameRunner.java

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.faforever.client.preferences.PreferencesService;
3939
import com.faforever.client.remote.FafServerAccessor;
4040
import com.faforever.client.replay.ReplayServer;
41+
import com.faforever.client.task.TaskService;
4142
import com.faforever.client.theme.UiService;
4243
import com.faforever.client.ui.StageHolder;
4344
import com.faforever.client.util.ConcurrentUtil;
@@ -118,6 +119,7 @@ public class GameRunner implements InitializingBean {
118119
private final NotificationPrefs notificationPrefs;
119120
private final FxApplicationThreadExecutor fxApplicationThreadExecutor;
120121
private final LogAnalyzerService logAnalyzerService;
122+
private final TaskService taskService;
121123

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

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

165167

166-
fafServerAccessor.connectionStateProperty().addListener((observable, oldValue, newValue) -> {
167-
if (isRunning() && newValue == ConnectionState.CONNECTED && oldValue != ConnectionState.CONNECTED) {
168+
fafServerAccessor.connectionStateProperty().subscribe((oldState, newState) -> {
169+
if (isRunning() && newState == ConnectionState.CONNECTED && oldState != ConnectionState.CONNECTED) {
168170
fafServerAccessor.restoreGameSession(runningGameId.get());
169171
}
170172
});
@@ -183,16 +185,16 @@ CompletableFuture<Void> startOnlineGame(GameLaunchResponse gameLaunchResponse) {
183185
String mapFolderName = gameLaunchResponse.getMapName();
184186
CompletableFuture<Void> downloadMapFuture = mapFolderName == null ? completedFuture(
185187
null) : mapService.downloadIfNecessary(mapFolderName).toFuture();
186-
CompletableFuture<League> leagueFuture = hasLeague ? completedFuture(null) : getDivisionInfo(
187-
leaderboard).toFuture();
188-
CompletableFuture<Integer> startReplayServerFuture = replayServer.start(uid);
189-
CompletableFuture<Integer> startIceAdapterFuture = startIceAdapter(uid);
190-
191-
return CompletableFuture.allOf(downloadMapFuture, leagueFuture, startIceAdapterFuture, startReplayServerFuture)
192-
.thenApply(_ -> gameMapper.map(gameLaunchResponse, leagueFuture.join()))
193-
.thenApply(parameters -> launchOnlineGame(parameters, startIceAdapterFuture.join(),
194-
startReplayServerFuture.join()))
195-
.whenCompleteAsync((process, throwable) -> {
188+
189+
CompletableFuture<League> loadLeagueInfoFuture = hasLeague ? completedFuture(null) : taskService.submitFutureTask("league.loadInfo", () -> getDivisionInfo(leaderboard).toFuture());
190+
CompletableFuture<Integer> runReplayServerFuture = taskService.submitFutureTask("replayServer.connecting", () -> replayServer.start(uid));
191+
CompletableFuture<Integer> runIceAdapterFuture = taskService.submitFutureTask("iceAdapter.connecting", () -> startIceAdapter(uid));
192+
193+
return CompletableFuture.allOf(downloadMapFuture, loadLeagueInfoFuture, runReplayServerFuture, runIceAdapterFuture)
194+
.thenApply(_ -> gameMapper.map(gameLaunchResponse, loadLeagueInfoFuture.join()))
195+
.thenApply(parameters -> launchOnlineGame(parameters, runIceAdapterFuture.join(),
196+
runReplayServerFuture.join()))
197+
.whenCompleteAsync((process, _) -> {
196198
if (process != null) {
197199
this.process.set(process);
198200
runningGameId.set(uid);
@@ -483,7 +485,7 @@ private Optional<Parent> getAnalysisButtonIfNecessary(Optional<String> logFileCo
483485
infoIcon.getStyleClass().add("info-icon");
484486
final Button showAnalysisBtn = new Button(i18n.get("game.log.analysis.solutionBtn"), infoIcon);
485487
showAnalysisBtn.setDefaultButton(true);
486-
showAnalysisBtn.setOnAction(event -> notificationService.addNotification(
488+
showAnalysisBtn.setOnAction(_ -> notificationService.addNotification(
487489
new ImmediateNotification(i18n.get("game.log.analysis"), message.toString(), WARN, actions)));
488490

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

511513
if (!preferencesService.hasValidGamePath()) {
512-
gamePathHandler.chooseAndValidateGameDirectory().thenAccept(path -> launchTutorial(mapVersion, technicalMapName));
514+
gamePathHandler.chooseAndValidateGameDirectory().thenAccept(_ -> launchTutorial(mapVersion, technicalMapName));
513515
return;
514516
}
515517

@@ -547,12 +549,12 @@ public void startOffline() {
547549
}
548550

549551
if (!preferencesService.hasValidGamePath()) {
550-
gamePathHandler.chooseAndValidateGameDirectory().thenAccept(path -> startOffline());
552+
gamePathHandler.chooseAndValidateGameDirectory().thenAccept(_ -> startOffline());
551553
return;
552554
}
553555

554556
CompletableFuture.supplyAsync(() -> forgedAllianceLaunchService.launchOfflineGame(null))
555-
.whenCompleteAsync((process, throwable) -> {
557+
.whenCompleteAsync((process, _) -> {
556558
if (process != null) {
557559
this.process.set(process);
558560
}

src/main/java/com/faforever/client/task/CompletableTask.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,16 @@ public abstract class CompletableTask<V> extends Task<V> implements PrioritizedC
1212
private final CompletableFuture<V> future;
1313
private Priority priority;
1414

15+
public CompletableTask() {
16+
this(Priority.MEDIUM);
17+
}
18+
1519
public CompletableTask(Priority priority) {
1620
this.priority = priority;
1721
this.future = new CompletableFuture<>();
18-
setOnCancelled(event -> future.cancel(true));
19-
setOnFailed(event -> future.completeExceptionally(getException()));
20-
setOnSucceeded(event -> future.complete(getValue()));
22+
setOnCancelled(_ -> future.cancel(true));
23+
setOnFailed(_ -> future.completeExceptionally(getException()));
24+
setOnSucceeded(_ -> future.complete(getValue()));
2125
}
2226

2327
@Override
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.faforever.client.task;
2+
3+
import org.apache.commons.lang3.StringUtils;
4+
5+
import java.util.concurrent.CompletableFuture;
6+
import java.util.concurrent.Future;
7+
import java.util.function.Supplier;
8+
9+
public class SimpleTask<V> extends CompletableTask<V> {
10+
11+
private final Supplier<? extends CompletableFuture<V>> futureSupplier;
12+
13+
public SimpleTask(Supplier<? extends CompletableFuture<V>> futureSupplier) {
14+
this(null, futureSupplier);
15+
}
16+
17+
public SimpleTask(String title, Supplier<? extends CompletableFuture<V>> futureSupplier) {
18+
this.futureSupplier = futureSupplier;
19+
updateTitle(StringUtils.defaultIfBlank(title, StringUtils.EMPTY));
20+
}
21+
22+
@Override
23+
protected V call() throws Exception {
24+
Future<V> task = futureSupplier.get();
25+
return task.get();
26+
}
27+
}

src/main/java/com/faforever/client/task/TaskService.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.faforever.client.task;
22

33
import com.faforever.client.fx.FxApplicationThreadExecutor;
4+
import com.faforever.client.i18n.I18n;
45
import javafx.collections.FXCollections;
56
import javafx.collections.ObservableList;
67
import javafx.concurrent.Worker;
@@ -9,7 +10,9 @@
910
import org.springframework.context.annotation.Lazy;
1011
import org.springframework.stereotype.Service;
1112

13+
import java.util.concurrent.CompletableFuture;
1214
import java.util.concurrent.ExecutorService;
15+
import java.util.function.Supplier;
1316

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

2629
private final ExecutorService taskExecutor;
2730
private final FxApplicationThreadExecutor fxApplicationThreadExecutor;
31+
private final I18n i18n;
2832

2933
private final ObservableList<Worker<?>> activeTasks = FXCollections.synchronizedObservableList(FXCollections.observableArrayList());
3034
private final ObservableList<Worker<?>> unmodifiableObservableList = FXCollections.unmodifiableObservableList(activeTasks);
@@ -36,8 +40,8 @@ public class TaskService {
3640
* @param task the task to execute
3741
*/
3842
public <T extends PrioritizedCompletableTask<?>> T submitTask(T task) {
39-
task.getFuture().whenComplete((o, throwable) -> {
40-
activeTasks.remove(task);
43+
task.getFuture().whenComplete((_, throwable) -> {
44+
fxApplicationThreadExecutor.execute(() -> activeTasks.remove(task));
4145
if (throwable != null) {
4246
log.error("Task failed", throwable);
4347
}
@@ -50,6 +54,15 @@ public <T extends PrioritizedCompletableTask<?>> T submitTask(T task) {
5054
return task;
5155
}
5256

57+
public <V> CompletableFuture<V> submitFutureTask(Supplier<CompletableFuture<V>> task) {
58+
return submitFutureTask(null, task);
59+
}
60+
61+
public <V> CompletableFuture<V> submitFutureTask(String titleI18nKey, Supplier<CompletableFuture<V>> task) {
62+
String title = titleI18nKey != null ? i18n.get(titleI18nKey) : null;
63+
return submitTask(new SimpleTask<>(title, task)).getFuture();
64+
}
65+
5366
public ObservableList<Worker<?>> getActiveWorkers() {
5467
return unmodifiableObservableList;
5568
}

src/main/java/com/faforever/client/ui/statusbar/StatusBarController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ protected void onInitialize() {
9292
}
9393
}));
9494

95-
JavaFxUtil.addListener(taskService.getActiveWorkers(), (Observable observable) -> {
95+
JavaFxUtil.addListener(taskService.getActiveWorkers(), (Observable _) -> {
9696
Collection<Worker<?>> runningWorkers = taskService.getActiveWorkers();
9797
if (runningWorkers.isEmpty()) {
9898
setCurrentWorkerInStatusBar(null);

src/main/resources/i18n/messages.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,3 +1319,6 @@ teammatchmaking.searchButton.memberInGame = Can't join matchmaking while someone
13191319
teammatchmaking.searchButton.inParty = The party leader will start matchmaking
13201320
teammatchmaking.searchButton.inQueue = Your party is now searching for a game
13211321
leaderboard.tmm_3v3.name = 3v3
1322+
iceAdapter.connecting = Connecting to ICE adapter
1323+
replayServer.connecting = Connecting to replay server
1324+
league.loadInfo = Loading a league information

src/test/java/com/faforever/client/game/GameRunnerTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
import com.faforever.client.preferences.PreferencesService;
3838
import com.faforever.client.remote.FafServerAccessor;
3939
import com.faforever.client.replay.ReplayServer;
40+
import com.faforever.client.task.SimpleTask;
41+
import com.faforever.client.task.TaskService;
4042
import com.faforever.client.test.ServiceTest;
4143
import com.faforever.client.theme.UiService;
4244
import com.faforever.client.ui.StageHolder;
@@ -51,6 +53,7 @@
5153
import org.junit.jupiter.api.Test;
5254
import org.mapstruct.factory.Mappers;
5355
import org.mockito.ArgumentCaptor;
56+
import org.mockito.Captor;
5457
import org.mockito.InjectMocks;
5558
import org.mockito.Mock;
5659
import org.mockito.Spy;
@@ -62,6 +65,7 @@
6265
import java.util.Set;
6366
import java.util.concurrent.CompletableFuture;
6467
import java.util.concurrent.ExecutorService;
68+
import java.util.function.Supplier;
6569

6670
import static java.util.concurrent.CompletableFuture.completedFuture;
6771
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -135,6 +139,8 @@ public class GameRunnerTest extends ServiceTest {
135139
@Mock
136140
private FxApplicationThreadExecutor fxApplicationThreadExecutor;
137141
@Mock
142+
private TaskService taskService;
143+
@Mock
138144
private GamePathHandler gamePathHandler;
139145
@Spy
140146
private GameMapper gameMapper = Mappers.getMapper(GameMapper.class);
@@ -144,6 +150,8 @@ public class GameRunnerTest extends ServiceTest {
144150
private LastGamePrefs lastGamePrefs;
145151
@Spy
146152
private NotificationPrefs notificationPrefs;
153+
@Captor
154+
private ArgumentCaptor<Supplier<CompletableFuture<Object>>> simpleTaskCaptor;
147155

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

0 commit comments

Comments
 (0)