Skip to content

Commit f9c32ef

Browse files
committed
Merge branch 'development' into feature/coopTabUpdates
# Conflicts: # src/main/resources/theme/icons.css
2 parents c914d48 + 5456695 commit f9c32ef

38 files changed

+1721
-311
lines changed

build.gradle

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import com.install4j.gradle.Install4jTask
22
import org.gradle.internal.os.OperatingSystem
33

44
plugins {
5-
id("org.springframework.boot") version "3.5.6"
5+
id("org.springframework.boot") version "3.5.7"
66
id("io.spring.dependency-management") version "1.1.7"
77
id("com.install4j.gradle") version "11.0.1"
88
id("de.undercouch.download") version "5.6.0"
@@ -256,7 +256,7 @@ dependencies {
256256
implementation("io.projectreactor.addons:reactor-extra")
257257
implementation("io.projectreactor:reactor-tools")
258258

259-
def commonsVersion = "20250913-317e19f"
259+
def commonsVersion = "20251112-0a26247"
260260

261261
implementation("com.faforever.commons:data:${commonsVersion}") {
262262
exclude module: 'guava'
@@ -296,14 +296,14 @@ dependencies {
296296
implementation("org.fxmisc.flowless:flowless:0.7.4")
297297

298298
implementation("org.javassist:javassist:3.30.2-GA")
299-
implementation("org.checkerframework:checker-qual:3.51.1")
299+
implementation("org.checkerframework:checker-qual:3.52.0")
300300

301301
implementation("io.github.sheikah45.fx2j:fx2j-api:0.1.0")
302302
implementation("com.install4j:install4j-runtime:11.0.1")
303303

304304
testImplementation("org.instancio:instancio-junit:5.5.1")
305-
testImplementation("com.squareup.okhttp3:okhttp:5.2.1")
306-
testImplementation("com.squareup.okhttp3:mockwebserver:5.2.1")
305+
testImplementation("com.squareup.okhttp3:okhttp:5.3.0")
306+
testImplementation("com.squareup.okhttp3:mockwebserver:5.3.0")
307307
testImplementation("io.projectreactor:reactor-test")
308308
testImplementation("org.testfx:testfx-core:4.0.18") {
309309
exclude module: 'guava'

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/chat/PrivateChatTabController.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.faforever.client.avatar.AvatarService;
44
import com.faforever.client.domain.server.PlayerInfo;
55
import com.faforever.client.fx.JavaFxUtil;
6+
import com.faforever.client.player.PlayerService;
67
import com.faforever.client.player.PrivatePlayerInfoController;
78
import javafx.beans.value.ObservableValue;
89
import javafx.scene.Node;
@@ -20,6 +21,7 @@
2021
public class PrivateChatTabController extends AbstractChatTabController {
2122

2223
private final AvatarService avatarService;
24+
private final PlayerService playerService;
2325

2426
public Tab privateChatTabRoot;
2527
public ImageView avatarImageView;
@@ -29,9 +31,10 @@ public class PrivateChatTabController extends AbstractChatTabController {
2931
public ScrollPane gameDetailScrollPane;
3032

3133
@Autowired
32-
public PrivateChatTabController(ChatService chatService, AvatarService avatarService) {
34+
public PrivateChatTabController(ChatService chatService, AvatarService avatarService, PlayerService playerService) {
3335
super(chatService);
3436
this.avatarService = avatarService;
37+
this.playerService = playerService;
3538
}
3639

3740
@Override
@@ -46,14 +49,12 @@ protected void onInitialize() {
4649

4750
privateChatTabRoot.textProperty().bind(channelName.when(attached));
4851

49-
ObservableValue<ChatChannelUser> chatUser = chatChannel.flatMap(
50-
channel -> channelName.map(chanName -> channel.getUser(chanName).orElse(null)));
51-
privatePlayerInfoController.chatUserProperty().bind(chatUser.when(showing));
52+
ObservableValue<PlayerInfo> player = channelName.map(
53+
playerName -> playerService.getPlayerByNameIfOnline(playerName).orElse(null));
54+
privatePlayerInfoController.playerProperty().bind(player.when(showing));
5255

53-
avatarImageView.imageProperty().bind(chatUser
54-
.flatMap(ChatChannelUser::playerProperty)
55-
.flatMap(PlayerInfo::avatarProperty)
56-
.map(avatarService::loadAvatar).when(showing));
56+
avatarImageView.imageProperty()
57+
.bind(player.flatMap(PlayerInfo::avatarProperty).map(avatarService::loadAvatar).when(showing));
5758
}
5859

5960
@Override

src/main/java/com/faforever/client/config/PreferencesConfig.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.faforever.client.preferences.ReplayHistoryPrefs;
2020
import com.faforever.client.preferences.UserPrefs;
2121
import com.faforever.client.preferences.VaultPrefs;
22+
import com.faforever.client.preferences.VetoKey;
2223
import com.faforever.client.preferences.WindowPrefs;
2324
import com.faforever.client.serialization.ColorMixin;
2425
import com.faforever.client.serialization.FactionMixin;
@@ -27,6 +28,8 @@
2728
import com.faforever.client.serialization.SimpleListPropertyInstantiator;
2829
import com.faforever.client.serialization.SimpleMapPropertyInstantiator;
2930
import com.faforever.client.serialization.SimpleSetPropertyInstantiator;
31+
import com.faforever.client.serialization.VetoKeyDeserializer;
32+
import com.faforever.client.serialization.VetoKeySerializer;
3033
import com.faforever.commons.api.dto.Faction;
3134
import com.fasterxml.jackson.annotation.JsonInclude.Include;
3235
import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -185,6 +188,8 @@ private ObjectMapper configureObjectMapper(ObjectMapper objectMapper) {
185188

186189
Module preferencesModule = new SimpleModule().addSerializer(Path.class, new PathSerializer())
187190
.addDeserializer(Path.class, new PathDeserializer())
191+
.addKeySerializer(VetoKey.class, new VetoKeySerializer())
192+
.addKeyDeserializer(VetoKey.class, new VetoKeyDeserializer())
188193
.addValueInstantiator(SimpleMapProperty.class, new SimpleMapPropertyInstantiator(configuredObjectMapper.getDeserializationConfig(), typeFactory.constructType(SimpleMapProperty.class)))
189194
.addValueInstantiator(SimpleListProperty.class, new SimpleListPropertyInstantiator(configuredObjectMapper.getDeserializationConfig(), typeFactory.constructType(SimpleListProperty.class)))
190195
.addValueInstantiator(SimpleSetProperty.class, new SimpleSetPropertyInstantiator(configuredObjectMapper.getDeserializationConfig(), typeFactory.constructType(SimpleSetProperty.class)))

src/main/java/com/faforever/client/domain/api/MatchmakerQueueMapPool.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,8 @@
55
public record MatchmakerQueueMapPool(
66
Integer id,
77
Double minRating,
8-
Double maxRating, MatchmakerQueueInfo matchmakerQueue
8+
Double maxRating, MatchmakerQueueInfo matchmakerQueue,
9+
Integer vetoTokensPerPlayer,
10+
Integer maxTokensPerMap,
11+
Float minimalMapsAllowed
912
) {}

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/map/MapService.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -632,17 +632,14 @@ public Mono<Void> downloadAllMatchmakerMaps(MatchmakerQueueInfo matchmakerQueue)
632632
}
633633

634634
@Cacheable(value = CacheNames.MATCHMAKER_POOLS, sync = true)
635-
public Mono <java.util.Map<MatchmakerQueueMapPool, List<MapVersion>>> getMatchmakerBrackets(MatchmakerQueueInfo matchmakerQueue) {
635+
public Mono <java.util.Map<MatchmakerQueueMapPool, List<com.faforever.client.domain.api.MapPoolAssignment>>> getMatchmakerBrackets(MatchmakerQueueInfo matchmakerQueue) {
636636
ElideNavigatorOnCollection<MapPoolAssignment> navigator = ElideNavigator
637637
.of(MapPoolAssignment.class).collection()
638638
.setFilter(qBuilder().intNum("mapPool.matchmakerQueueMapPool.matchmakerQueue.id").eq(matchmakerQueue.getId()));
639639

640640
return fafApiAccessor.getMany(navigator)
641641
.map(matchmakerMapper::map)
642-
.collect(Collectors.groupingBy(assignment -> assignment.mapPool().mapPool(),
643-
Collectors.mapping(
644-
com.faforever.client.domain.api.MapPoolAssignment::mapVersion,
645-
Collectors.toList())));
642+
.collect(Collectors.groupingBy(assignment -> assignment.mapPool().mapPool()));
646643

647644
}
648645

src/main/java/com/faforever/client/player/PrivatePlayerInfoController.java

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

33
import com.faforever.client.achievements.AchievementService;
4-
import com.faforever.client.chat.ChatChannelUser;
54
import com.faforever.client.domain.api.Leaderboard;
65
import com.faforever.client.domain.server.GameInfo;
76
import com.faforever.client.domain.server.PlayerInfo;
@@ -18,7 +17,6 @@
1817
import com.faforever.commons.lobby.GameStatus;
1918
import javafx.beans.property.ObjectProperty;
2019
import javafx.beans.property.SimpleObjectProperty;
21-
import javafx.beans.value.ChangeListener;
2220
import javafx.beans.value.ObservableValue;
2321
import javafx.scene.Node;
2422
import javafx.scene.control.Label;
@@ -34,7 +32,6 @@
3432
import reactor.core.publisher.Mono;
3533
import reactor.function.TupleUtils;
3634

37-
import java.util.Objects;
3835
import java.util.stream.Collectors;
3936

4037
@Component
@@ -63,14 +60,7 @@ public class PrivatePlayerInfoController extends NodeController<Node> {
6360
public Label unlockedAchievementsLabel;
6461
public Separator separator;
6562

66-
private final ObjectProperty<ChatChannelUser> chatUser = new SimpleObjectProperty<>();
67-
68-
private final ChangeListener<PlayerInfo> playerChangeListener = (observable, oldValue, newValue) -> {
69-
if (newValue != null && !Objects.equals(oldValue, newValue)) {
70-
loadReceiverRatingInformation(newValue);
71-
populateUnlockedAchievementsLabel(newValue);
72-
}
73-
};
63+
private final ObjectProperty<PlayerInfo> player = new SimpleObjectProperty<>();
7464

7565
@Override
7666
public Node getRoot() {
@@ -85,8 +75,7 @@ protected void onInitialize() {
8575
gameDetailController.setPlaytimeVisible(true);
8676
gameDetailWrapper.setVisible(false);
8777

88-
ObservableValue<Boolean> playerExistsProperty = chatUser.flatMap(user -> user.playerProperty().isNotNull())
89-
.when(showing);
78+
ObservableValue<Boolean> playerExistsProperty = player.isNotNull().when(showing);
9079
userImageView.visibleProperty().bind(playerExistsProperty);
9180
country.visibleProperty().bind(playerExistsProperty);
9281
ratingsLabels.visibleProperty().bind(playerExistsProperty);
@@ -96,36 +85,30 @@ protected void onInitialize() {
9685
unlockedAchievements.visibleProperty().bind(playerExistsProperty);
9786
unlockedAchievementsLabel.visibleProperty().bind(playerExistsProperty);
9887

99-
ObservableValue<PlayerInfo> playerObservable = chatUser.flatMap(ChatChannelUser::playerProperty);
100-
10188
gamesPlayed.textProperty()
102-
.bind(playerObservable.flatMap(PlayerInfo::numberOfGamesProperty).map(i18n::number).when(showing));
89+
.bind(player.flatMap(PlayerInfo::numberOfGamesProperty).map(i18n::number).when(showing));
10390

104-
username.textProperty().bind(chatUser.map(ChatChannelUser::getUsername).when(showing));
91+
username.textProperty().bind(player.map(PlayerInfo::getUsername).when(showing));
10592
country.textProperty()
106-
.bind(
107-
playerObservable.flatMap(PlayerInfo::countryProperty).map(i18n::getCountryNameLocalized).when(showing));
93+
.bind(player.flatMap(PlayerInfo::countryProperty).map(i18n::getCountryNameLocalized).when(showing));
10894
userImageView.imageProperty()
109-
.bind(playerObservable.map(PlayerInfo::getId).map(IdenticonUtil::createIdenticon).when(showing));
110-
ObservableValue<GameInfo> gameObservable = playerObservable.flatMap(PlayerInfo::gameProperty);
95+
.bind(player.map(PlayerInfo::getId).map(IdenticonUtil::createIdenticon).when(showing));
96+
ObservableValue<GameInfo> gameObservable = player.flatMap(PlayerInfo::gameProperty);
11197
gameDetailController.gameProperty().bind(gameObservable.when(showing));
11298
gameDetailWrapper.visibleProperty().bind(gameObservable.flatMap(GameInfo::statusProperty)
11399
.map(status -> status == GameStatus.OPEN || status == GameStatus.PLAYING)
114100
.orElse(false)
115101
.when(showing));
116-
chatUser.flatMap(ChatChannelUser::playerProperty).addListener(playerChangeListener);
117-
}
118-
119-
public void setChatUser(ChatChannelUser chatUser) {
120-
this.chatUser.set(chatUser);
121-
}
122-
123-
public ChatChannelUser getChatUser() {
124-
return chatUser.get();
102+
player.when(showing).subscribe((playerInfo) -> {
103+
if (playerInfo != null) {
104+
loadReceiverRatingInformation(playerInfo);
105+
populateUnlockedAchievementsLabel(playerInfo);
106+
}
107+
});
125108
}
126109

127-
public ObjectProperty<ChatChannelUser> chatUserProperty() {
128-
return chatUser;
110+
public ObjectProperty<PlayerInfo> playerProperty() {
111+
return player;
129112
}
130113

131114
private void populateUnlockedAchievementsLabel(PlayerInfo player) {

src/main/java/com/faforever/client/preferences/MatchmakerPrefs.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
import com.faforever.commons.lobby.Faction;
44
import javafx.collections.ObservableList;
5+
import javafx.collections.ObservableMap;
56
import javafx.collections.ObservableSet;
67

78
import static javafx.collections.FXCollections.observableArrayList;
9+
import static javafx.collections.FXCollections.observableHashMap;
810
import static javafx.collections.FXCollections.observableSet;
911

1012

@@ -13,11 +15,18 @@ public class MatchmakerPrefs {
1315
Faction.SERAPHIM);
1416
private final ObservableSet<Integer> unselectedQueueIds = observableSet();
1517

18+
private final ObservableMap<VetoKey, Integer> appliedVetoes = observableHashMap();
19+
1620
public ObservableList<Faction> getFactions() {
1721
return factions;
1822
}
1923

2024
public ObservableSet<Integer> getUnselectedQueueIds() {
2125
return unselectedQueueIds;
2226
}
27+
28+
public ObservableMap<VetoKey, Integer> getAppliedVetoes() {
29+
return appliedVetoes;
30+
}
31+
2332
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.faforever.client.preferences;
2+
3+
import com.faforever.client.domain.api.MapPoolAssignment;
4+
5+
public record VetoKey(
6+
int matchmakerQueueMapPoolId,
7+
int mapPoolMapVersionId
8+
) {
9+
10+
public static VetoKey of(MapPoolAssignment assignment) {
11+
return new VetoKey(assignment.mapPool().mapPool().id(), assignment.id());
12+
}
13+
14+
}

0 commit comments

Comments
 (0)