diff --git a/src/main/java/com/faforever/client/preferences/VetoKey.java b/src/main/java/com/faforever/client/preferences/VetoKey.java index 034b5d84b0..e200a828a6 100644 --- a/src/main/java/com/faforever/client/preferences/VetoKey.java +++ b/src/main/java/com/faforever/client/preferences/VetoKey.java @@ -1,6 +1,14 @@ package com.faforever.client.preferences; +import com.faforever.client.domain.api.MapPoolAssignment; + public record VetoKey( int matchmakerQueueMapPoolId, int mapPoolMapVersionId -) {} \ No newline at end of file +) { + + public static VetoKey of(MapPoolAssignment assignment) { + return new VetoKey(assignment.mapPool().mapPool().id(), assignment.id()); + } + +} \ No newline at end of file diff --git a/src/main/java/com/faforever/client/teammatchmaking/TeamMatchmakingMapTileController.java b/src/main/java/com/faforever/client/teammatchmaking/TeamMatchmakingMapTileController.java index 94008fe020..f101c54458 100644 --- a/src/main/java/com/faforever/client/teammatchmaking/TeamMatchmakingMapTileController.java +++ b/src/main/java/com/faforever/client/teammatchmaking/TeamMatchmakingMapTileController.java @@ -4,9 +4,9 @@ import com.faforever.client.domain.api.MapPoolAssignment; import com.faforever.client.domain.api.MapVersion; import com.faforever.client.fx.ImageViewHelper; +import com.faforever.client.fx.NodeController; import com.faforever.client.i18n.I18n; import com.faforever.client.map.MapService; -import com.faforever.client.fx.NodeController; import com.faforever.client.map.MapService.PreviewSize; import com.faforever.client.map.generator.MapGeneratorService; import com.faforever.client.preferences.MatchmakerPrefs; @@ -25,13 +25,14 @@ import javafx.scene.image.ImageView; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; -import javafx.scene.layout.VBox; import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; import lombok.RequiredArgsConstructor; import lombok.Setter; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; + import java.util.function.Consumer; /** @@ -101,9 +102,10 @@ void setVetoModeEnabled(BooleanProperty vetoModeEnabled) { public void bindVetoModeDependentProperties() { vetoesBox.mouseTransparentProperty().bind(vetoModeEnabled.not()); vetoesBox.visibleProperty().bind(vetoModeEnabled.or(tokenCount.greaterThan(0))); - root.cursorProperty().bind(Bindings.when( - Bindings.createBooleanBinding(() -> isGeneratedMap.getValue() || vetoModeEnabled.get(), isGeneratedMap, vetoModeEnabled) - ).then(Cursor.DEFAULT).otherwise(Cursor.HAND)); + root.cursorProperty() + .bind(Bindings.when( + Bindings.createBooleanBinding(() -> isGeneratedMap.getValue() || vetoModeEnabled.get(), isGeneratedMap, + vetoModeEnabled)).then(Cursor.DEFAULT).otherwise(Cursor.HAND)); } @Override @@ -112,9 +114,9 @@ protected void onInitialize() { isGeneratedMap = mapObservable.map(map -> mapGeneratorService.isGeneratedMap(map.displayName())).orElse(false); thumbnailImageView.imageProperty() - .bind(assignment - .map(assignmentBean -> mapService.loadPreview(assignmentBean.mapVersion(), PreviewSize.SMALL)) - .flatMap(imageViewHelper::createPlaceholderImageOnErrorObservable)); + .bind(assignment.map( + assignmentBean -> mapService.loadPreview(assignmentBean.mapVersion(), PreviewSize.SMALL)) + .flatMap(imageViewHelper::createPlaceholderImageOnErrorObservable)); nameLabel.textProperty().bind(mapObservable.map(map -> { if (isGeneratedMap.getValue()) { @@ -123,9 +125,7 @@ protected void onInitialize() { return map.displayName(); })); - authorBox.visibleProperty() - .bind(mapObservable.map( - map -> (map.author() != null) || isGeneratedMap.getValue())); + authorBox.visibleProperty().bind(mapObservable.map(map -> (map.author() != null) || isGeneratedMap.getValue())); authorBox.managedProperty().bind(authorBox.visibleProperty()); authorLabel.textProperty().bind(mapObservable.map(map -> { @@ -165,27 +165,26 @@ protected void onInitialize() { }); vetoButton.setOnAction(event -> { - if (assignment.getValue() == null) { + MapPoolAssignment value = assignment.getValue(); + if (value == null) { return; } int currentTokenCount = tokenCount.get(); int currentMaxPerMap = maxPerMap.get(); if ((isMaxPerMapDynamic.get() || currentTokenCount < currentMaxPerMap) && currentTokenCount < vetoTokensMax.get() && vetoTokensLeft.getValue() > 0) { - teamMatchmakingService.setTokensForMap( - new VetoKey(assignment.getValue().mapPool().mapPool().id(), assignment.getValue().id()), - currentTokenCount + 1); + teamMatchmakingService.setTokensForMap(VetoKey.of(value), currentTokenCount + 1); } event.consume(); }); minusButton.setOnAction(event -> { - if (assignment.getValue() == null) { + MapPoolAssignment value = assignment.getValue(); + if (value == null) { return; } int current = tokenCount.get(); if (current > 0) { - teamMatchmakingService.setTokensForMap( - new VetoKey(assignment.getValue().mapPool().mapPool().id(), assignment.getValue().id()), current - 1); + teamMatchmakingService.setTokensForMap(VetoKey.of(value), current - 1); } event.consume(); }); @@ -213,11 +212,12 @@ private void updateBannedState() { } private void updateVetoes() { - if (assignment.getValue() == null) { + MapPoolAssignment value = assignment.getValue(); + if (value == null) { tokenCount.set(0); return; } - VetoKey key = new VetoKey(assignment.getValue().mapPool().mapPool().id(), assignment.getValue().id()); + VetoKey key = VetoKey.of(value); int usedTokens = matchmakerPrefs.getAppliedVetoes().getOrDefault(key, 0); tokenCount.set(usedTokens); } diff --git a/src/main/java/com/faforever/client/teammatchmaking/TeamMatchmakingService.java b/src/main/java/com/faforever/client/teammatchmaking/TeamMatchmakingService.java index f7bcef2f85..b58a0385b9 100644 --- a/src/main/java/com/faforever/client/teammatchmaking/TeamMatchmakingService.java +++ b/src/main/java/com/faforever/client/teammatchmaking/TeamMatchmakingService.java @@ -84,6 +84,7 @@ import java.time.Instant; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; @@ -245,12 +246,6 @@ public void afterPropertiesSet() throws Exception { } }); - matchmakerPrefs.getAppliedVetoes().subscribe(()->{ - if (fafServerAccessor.getConnectionState() == ConnectionState.CONNECTED) { - sendVetoes(); - } - }); - party.ownerProperty().subscribe((oldValue, newValue) -> { if (oldValue != null) { chatService.leaveChannel("#" + oldValue.getUsername() + PARTY_CHANNEL_SUFFIX); @@ -274,27 +269,37 @@ private void sendVetoes() { fafServerAccessor.setPlayerVetoes(getVetoesAsList()); } - public void setTokensForMap(VetoKey vetoKey, Integer vetoTokensApplied) { - matchmakerPrefs.getAppliedVetoes().put(vetoKey, vetoTokensApplied); + public void setTokensForMap(VetoKey key, int vetoTokensApplied) { + Integer previousValue; + if (vetoTokensApplied == 0) { + previousValue = matchmakerPrefs.getAppliedVetoes().remove(key); + } else { + previousValue = matchmakerPrefs.getAppliedVetoes().put(key, vetoTokensApplied); + } + + if ((previousValue == null && vetoTokensApplied > 0) || (previousValue != null && previousValue != vetoTokensApplied)) { + sendVetoes(); + } } - public void setAllVetoes(List vetoes) { + public void updateVetoes(List vetoes) { + Map vetosMap = vetoes.stream() + .collect(Collectors.toMap( + vetoData -> new VetoKey(vetoData.getMatchmakerQueueMapPoolId(), + vetoData.getMapPoolMapVersionId()), + VetoData::getVetoTokensApplied)); matchmakerPrefs.getAppliedVetoes().clear(); - vetoes.forEach(v -> matchmakerPrefs.getAppliedVetoes().put( - new VetoKey(v.getMatchmakerQueueMapPoolId(), v.getMapPoolMapVersionId()), - v.getVetoTokensApplied() - )); + matchmakerPrefs.getAppliedVetoes().putAll(vetosMap); } private void onVetoesChanged(VetoesChangedInfo vetoesChangedInfo) { - setAllVetoes(vetoesChangedInfo.getVetoes()); + updateVetoes(vetoesChangedInfo.getVetoes()); if (vetoesChangedInfo.getForced()) { - notificationService.addNotification( - new ImmediateNotification(i18n.get("teammatchmaking.vetoes.forced.title"), - i18n.get("teammatchmaking.vetoes.forced.message"), - Severity.INFO, - Collections.singletonList(new DismissAction(i18n)))); + notificationService.addNotification(new ImmediateNotification(i18n.get("teammatchmaking.vetoes.forced.title"), + i18n.get("teammatchmaking.vetoes.forced.message"), + Severity.INFO, Collections.singletonList( + new DismissAction(i18n)))); } } @@ -303,10 +308,8 @@ private List getVetoesAsList() { .entrySet() .stream() .filter(entry -> entry.getValue() > 0) - .map(entry -> new VetoData( - entry.getKey().mapPoolMapVersionId(), - entry.getValue(), - entry.getKey().matchmakerQueueMapPoolId())) + .map(entry -> new VetoData(entry.getKey().mapPoolMapVersionId(), entry.getValue(), + entry.getKey().matchmakerQueueMapPoolId())) .collect(Collectors.toList()); } @@ -435,8 +438,7 @@ private Mono getQueueFromApi(MatchmakerInfo.MatchmakerQueue .collection() .setFilter(qBuilder().string("technicalName") .eq(matchmakerQueue.getName())); - return fafApiAccessor.getMany(navigator) - .next().map(matchmakerMapper::map) + return fafApiAccessor.getMany(navigator).next().map(matchmakerMapper::map) .map(queue -> matchmakerMapper.update(matchmakerQueue, queue)) .doOnNext(queue -> queue.setSelected( !matchmakerPrefs.getUnselectedQueueIds().contains(queue.getId()))) @@ -464,20 +466,21 @@ public CompletableFuture joinQueues() { return featuredModService.updateFeaturedModToLatest(FAF.getTechnicalName(), false) .thenCompose(aVoid -> validQueues.stream() - .map(this::joinQueue) - .reduce((future1, future2) -> future1.thenCombine(future2, - (result1, result2) -> result1 || result2)) - .orElse(CompletableFuture.completedFuture(false))) + .map(this::joinQueue) + .reduce((future1, future2) -> future1.thenCombine(future2, + (result1, result2) -> result1 || result2)) + .orElse(CompletableFuture.completedFuture(false))) .exceptionally(throwable -> { - throwable = ConcurrentUtil.unwrapIfCompletionException(throwable); - log.error("Unable to join queues", throwable); - if (throwable instanceof NotifiableException notifiableException) { - notificationService.addErrorNotification(notifiableException); - } else { - notificationService.addImmediateErrorNotification(throwable, "teammatchmaking.couldNotStart"); - } - return false; - }); + throwable = ConcurrentUtil.unwrapIfCompletionException(throwable); + log.error("Unable to join queues", throwable); + if (throwable instanceof NotifiableException notifiableException) { + notificationService.addErrorNotification(notifiableException); + } else { + notificationService.addImmediateErrorNotification(throwable, + "teammatchmaking.couldNotStart"); + } + return false; + }); } public void leaveQueues() { diff --git a/src/test/java/com/faforever/client/teammatchmaking/TeamMatchmakingMapTileControllerTest.java b/src/test/java/com/faforever/client/teammatchmaking/TeamMatchmakingMapTileControllerTest.java index 1c09df01e8..2819460f06 100644 --- a/src/test/java/com/faforever/client/teammatchmaking/TeamMatchmakingMapTileControllerTest.java +++ b/src/test/java/com/faforever/client/teammatchmaking/TeamMatchmakingMapTileControllerTest.java @@ -34,7 +34,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/src/test/java/com/faforever/client/teammatchmaking/TeamMatchmakingServiceTest.java b/src/test/java/com/faforever/client/teammatchmaking/TeamMatchmakingServiceTest.java index 4e28c5661f..3a30635077 100644 --- a/src/test/java/com/faforever/client/teammatchmaking/TeamMatchmakingServiceTest.java +++ b/src/test/java/com/faforever/client/teammatchmaking/TeamMatchmakingServiceTest.java @@ -637,13 +637,14 @@ public void testSendVetoesOnConnection() { @Test public void testSendVetoesOnVetoesChange() { connectionState.set(ConnectionState.CONNECTED); - when(fafServerAccessor.getConnectionState()).thenReturn(ConnectionState.CONNECTED); - // Clear invocations from connection state change - org.mockito.Mockito.clearInvocations(fafServerAccessor); - matchmakerPrefs.getAppliedVetoes().put(new VetoKey(1, 1), 1); + instance.setTokensForMap(new VetoKey(1, 1), 1); + + verify(fafServerAccessor, times(1)).setPlayerVetoes(List.of(new VetoData(1, 1, 1))); + + instance.setTokensForMap(new VetoKey(1, 1), 1); - verify(fafServerAccessor, times(1)).setPlayerVetoes(anyList()); + verify(fafServerAccessor, times(1)).setPlayerVetoes(List.of(new VetoData(1, 1, 1))); } @Test