Skip to content

Commit

Permalink
Add Generate map button in game detail window for generated map (#2859
Browse files Browse the repository at this point in the history
)

* add `show map preview` button for generated map

* add error handling

* fix tests

* refactoring

* improve code

* add tests

* improve tests

* codacy you are stupid and blind :P

* improve tests

* more clean title of the button, some improving of code

* refactoring

* add tests

Co-authored-by: Ivan <v23620@gmail.com>
  • Loading branch information
Marc-Spector and IvanPavilionG7 committed Dec 2, 2022
1 parent 6c13f7b commit 46507ac
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 34 deletions.
17 changes: 17 additions & 0 deletions src/main/java/com/faforever/client/game/CustomGamesController.java
Expand Up @@ -8,6 +8,7 @@
import com.faforever.client.i18n.I18n;
import com.faforever.client.main.event.HostGameEvent;
import com.faforever.client.main.event.NavigateEvent;
import com.faforever.client.map.generator.MapGeneratedEvent;
import com.faforever.client.preferences.Preferences;
import com.faforever.client.preferences.PreferencesService;
import com.faforever.client.theme.UiService;
Expand All @@ -18,6 +19,7 @@
import com.faforever.commons.lobby.GameType;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import javafx.beans.InvalidationListener;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.IntegerBinding;
Expand Down Expand Up @@ -234,6 +236,21 @@ public void onTilesButtonClicked() {
gamesTilesContainerController.createTiledFlowPane(filteredItems, chooseSortingTypeChoiceBox);
}

@Subscribe
public void onMapGeneratedEvent(MapGeneratedEvent event) {
filteredItems.stream()
.filter(game -> game.getMapFolderName().equals(event.mapName()) && game.getStatus() == GameStatus.OPEN)
.findFirst()
.ifPresent(game -> {
if (gamesTilesContainerController != null) {
gamesTilesContainerController.recreateTile(event.mapName());
}
if (gamesTableController != null) {
gamesTableController.refreshTable();
}
});
}

private void disposeGamesContainer() {
if (gamesTilesContainerController != null) {
JavaFxUtil.removeListener(gamesTilesContainerController.selectedGameProperty(), gameChangeListener);
Expand Down
Expand Up @@ -9,20 +9,26 @@
import com.faforever.client.i18n.I18n;
import com.faforever.client.map.MapService;
import com.faforever.client.map.MapService.PreviewSize;
import com.faforever.client.map.generator.MapGeneratedEvent;
import com.faforever.client.map.generator.MapGeneratorService;
import com.faforever.client.mod.ModService;
import com.faforever.client.notification.NotificationService;
import com.faforever.client.theme.UiService;
import com.faforever.client.util.PopupUtil;
import com.faforever.client.util.RatingUtil;
import com.faforever.client.util.TimeService;
import com.faforever.client.vault.replay.WatchButtonController;
import com.faforever.commons.lobby.GameStatus;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import javafx.animation.Animation.Status;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.InvalidationListener;
import javafx.beans.WeakInvalidationListener;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
Expand Down Expand Up @@ -55,6 +61,9 @@ public class GameDetailController implements Controller<Pane> {
private final ImageViewHelper imageViewHelper;
private final JoinGameHelper joinGameHelper;
private final ContextMenuBuilder contextMenuBuilder;
private final MapGeneratorService mapGeneratorService;
private final NotificationService notificationService;
private final EventBus eventBus;

public Pane gameDetailRoot;
public Label gameTypeLabel;
Expand All @@ -69,6 +78,7 @@ public class GameDetailController implements Controller<Pane> {
public Node joinButton;
public WatchButtonController watchButtonController;
public Node watchButton;
public Button generateMapButton;

private GameBean game;
private boolean playtimeVisible;
Expand All @@ -84,14 +94,16 @@ public void initialize() {
imageViewHelper.setDefaultPlaceholderImage(mapImageView, true);
contextMenuBuilder.addCopyLabelContextMenu(gameTitleLabel, mapLabel, gameTypeLabel);
JavaFxUtil.bindManagedToVisible(joinButton, watchButton, gameTitleLabel, hostLabel, mapLabel, numberOfPlayersLabel,
mapPreviewContainer, gameTypeLabel, playtimeLabel);
mapPreviewContainer, gameTypeLabel, playtimeLabel, generateMapButton);
JavaFxUtil.bind(mapPreviewContainer.visibleProperty(), mapImageView.imageProperty().isNotNull());
gameDetailRoot.parentProperty().addListener(observable -> {
if (!(gameDetailRoot.getParent() instanceof Pane)) {
return;
}
gameDetailRoot.maxWidthProperty().bind(((Pane) gameDetailRoot.getParent()).widthProperty());
});

eventBus.register(this);
}

private void onGameStatusChanged() {
Expand Down Expand Up @@ -171,6 +183,7 @@ public void setGame(GameBean game) {
}

showGameDetail();
showActionsForGeneratedMap();

WeakInvalidationListener weakTeamListener = new WeakInvalidationListener(teamsInvalidationListener);
WeakInvalidationListener weakGameStatusListener = new WeakInvalidationListener(gameStatusInvalidationListener);
Expand Down Expand Up @@ -266,6 +279,42 @@ public void onMapPreviewImageClicked() {
}
}

private void showActionsForGeneratedMap() {
String mapName = game.getMapFolderName();
generateMapButton.setVisible(mapGeneratorService.isGeneratedMap(mapName) && !mapService.isInstalled(mapName));
}

public void onGenerateMapClicked() {
setGeneratingMapInProgress(true);
mapService.generateIfNotInstalled(game.getMapFolderName())
.exceptionally(throwable -> {
notificationService.addImmediateErrorNotification(throwable, "game.mapGeneration.failed.title");
return null;
})
.whenComplete((unused, throwable) -> setGeneratingMapInProgress(false));
}

@Subscribe
public void onMapGeneratedEvent(MapGeneratedEvent event) {
reloadMapImage(event.mapName());
}

private void reloadMapImage(String mapName) {
if (game != null && mapName.equals(game.getMapFolderName())) {
JavaFxUtil.runLater(() -> {
mapImageView.setImage(mapService.loadPreview(mapName, PreviewSize.LARGE));
generateMapButton.setVisible(false);
});
}
}

private void setGeneratingMapInProgress(boolean inProgress) {
JavaFxUtil.runLater(() -> {
generateMapButton.setDisable(inProgress);
generateMapButton.setText(i18n.get(inProgress ? "game.mapGeneration.notification.title" : "game.create.generatedMap2"));
});
}

@VisibleForTesting
protected Timeline getPlayTimeTimeline() {
return playTimeTimeline;
Expand Down
Expand Up @@ -62,6 +62,7 @@ public class GamesTableController implements Controller<Node> {
private final ImageViewHelper imageViewHelper;
private final PreferencesService preferencesService;
private final PlayerService playerService;

public TableView<GameBean> gamesTable;
public TableColumn<GameBean, Image> mapPreviewColumn;
public TableColumn<GameBean, String> gameTitleColumn;
Expand Down Expand Up @@ -275,4 +276,8 @@ public void removeListeners() {
JavaFxUtil.removeListener(tooltip.showingProperty(), tooltipShowingListener);
JavaFxUtil.removeListener(gamesTable.getSelectionModel().selectedItemProperty(), selectedItemListener);
}

public void refreshTable() {
JavaFxUtil.runLater(() -> gamesTable.refresh());
}
}
Expand Up @@ -5,6 +5,7 @@
import com.faforever.client.fx.JavaFxUtil;
import com.faforever.client.preferences.PreferencesService;
import com.faforever.client.theme.UiService;
import com.faforever.commons.lobby.GameStatus;
import com.google.common.annotations.VisibleForTesting;
import javafx.beans.InvalidationListener;
import javafx.beans.property.ObjectProperty;
Expand Down Expand Up @@ -62,6 +63,7 @@ public class GamesTilesContainerController implements Controller<Node> {
gameTooltipController.setGame(null);
}
};

private void sortNodes() {
ObservableList<Node> sortedChildren = tiledFlowPane.getChildren().sorted(appliedComparator);
tiledFlowPane.getChildren().setAll(sortedChildren);
Expand All @@ -85,18 +87,7 @@ public void initialize() {

gameListChangeListener = change -> JavaFxUtil.runLater(() -> {
while (change.next()) {
change.getRemoved().forEach(game -> {
Node card = gameIdToGameCard.remove(game.getId());
if (card != null) {
Tooltip.uninstall(card, tooltip);
boolean remove = tiledFlowPane.getChildren().remove(card);
if (!remove) {
log.warn("Tried to remove game tile that did not exist in UI.");
}
} else {
log.warn("Tried to remove game tile that did not exist.");
}
});
change.getRemoved().forEach(this::removeGameCard);
change.getAddedSubList().forEach(GamesTilesContainerController.this::addGameCard);
sortNodes();
}
Expand Down Expand Up @@ -125,9 +116,7 @@ private void initializeChoiceBox() {

private void selectFirstGame() {
ObservableList<Node> cards = tiledFlowPane.getChildren();
if (!cards.isEmpty()) {
selectedGame.set((GameBean) cards.get(0).getUserData());
}
selectedGame.set(!cards.isEmpty() ? (GameBean) cards.get(0).getUserData() : null);
}

private void addGameCard(GameBean game) {
Expand All @@ -139,7 +128,7 @@ private void addGameCard(GameBean game) {
root.setUserData(game);
tiledFlowPane.getChildren().add(root);
gameIdToGameCard.put(game.getId(), root);

root.setOnMouseEntered(event -> {
gameTooltipController.setGame(game);
if (tooltip.isShowing()) {
Expand All @@ -149,6 +138,37 @@ private void addGameCard(GameBean game) {
Tooltip.install(root, tooltip);
}

private void removeGameCard(GameBean game) {
Node card = gameIdToGameCard.remove(game.getId());
if (card != null) {
Tooltip.uninstall(card, tooltip);
if (!tiledFlowPane.getChildren().remove(card)) {
log.warn("Tried to remove game tile that did not exist in UI.");
} else {
clearSelectedGame(game);
}
} else {
log.warn("Tried to remove game tile that did not exist.");
}
}

private void clearSelectedGame(GameBean game) {
if (game.equals(selectedGame.getValue()) && game.getStatus() != GameStatus.OPEN) {
selectFirstGame();
}
}

public void recreateTile(String mapName) {
games.stream()
.filter(game -> game.getMapFolderName().equals(mapName))
.findFirst()
.ifPresentOrElse(game -> JavaFxUtil.runLater(() -> {
removeGameCard(game);
addGameCard(game);
sortNodes();
}), () -> log.warn("No tile with {} map to recreate", mapName));
}

public Node getRoot() {
return tiledScrollPane;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/faforever/client/map/MapService.java
Expand Up @@ -276,7 +276,7 @@ void tryAddInstalledMap(Path path) {

@Subscribe
public void onMapGenerated(MapGeneratedEvent event) {
tryAddInstalledMap(getPathForMap(event.getMapName()));
tryAddInstalledMap(getPathForMap(event.mapName()));
}


Expand Down
@@ -1,8 +1,4 @@
package com.faforever.client.map.generator;

import lombok.Value;

@Value
public class MapGeneratedEvent {
private String mapName;
public record MapGeneratedEvent(String mapName) {
}
1 change: 1 addition & 0 deletions src/main/resources/i18n/messages.properties
Expand Up @@ -713,6 +713,7 @@ menu.revealReplayFolder = Show replay folder
menu.revealGamePrefsFile = Show game.prefs file
ranked1v1.queuePopTimer = Matching in {0,number,0}\:{1,number,00}
game.create.generatedMap = Generate new map
game.create.generatedMap2 = Generate the map
game.avgRating.format = {0,number,#}
game.mapGeneration.notification.title = Generating map...
game.mapGeneration.notification.message = Please stand by. (Version\: {0}, Seed\: {1})
Expand Down
26 changes: 15 additions & 11 deletions src/main/resources/theme/play/game_detail.fxml
Expand Up @@ -11,9 +11,8 @@
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>

<GridPane fx:id="gameDetailRoot" maxHeight="1.7976931348623157E308" maxWidth="-Infinity" minWidth="50.0"
styleClass="game-detail" vgap="10.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
styleClass="game-detail" vgap="10.0" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.faforever.client.game.GameDetailController">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0"/>
Expand All @@ -30,12 +29,18 @@
<RowConstraints minHeight="10.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="10.0" vgrow="SOMETIMES"/>
</rowConstraints>
<StackPane fx:id="mapPreviewContainer" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER">
<ImageView fx:id="mapImageView" fitHeight="256.0" fitWidth="256.0" preserveRatio="true"
styleClass="map-preview"/>
<Pane maxHeight="256.0" maxWidth="256.0" onMouseClicked="#onMapPreviewImageClicked" styleClass="clickable"
StackPane.alignment="CENTER"/>
</StackPane>
<VBox alignment="CENTER" spacing="5.0" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER">
<children>
<StackPane fx:id="mapPreviewContainer">
<ImageView fx:id="mapImageView" fitHeight="256.0" fitWidth="256.0" preserveRatio="true"
styleClass="map-preview"/>
<Pane maxHeight="256.0" maxWidth="256.0" onMouseClicked="#onMapPreviewImageClicked"
styleClass="clickable" StackPane.alignment="CENTER"/>
</StackPane>
<Button fx:id="generateMapButton" prefWidth="256.0" mnemonicParsing="false"
onAction="#onGenerateMapClicked" text="%game.create.generatedMap2"/>
</children>
</VBox>
<Label fx:id="gameTitleLabel" maxWidth="1.7976931348623157E308" minWidth="0.0" styleClass="game-title"
text="&lt;A pretty long game title&gt;" wrapText="true" GridPane.columnSpan="2147483647"
GridPane.hgrow="ALWAYS" GridPane.rowIndex="1"/>
Expand All @@ -61,13 +66,12 @@
</Label>
<VBox alignment="CENTER" GridPane.columnSpan="2147483647" GridPane.rowIndex="7">
<fx:include fx:id="watchButton" source="../vault/replay/watch_button.fxml"/>
<Button fx:id="joinButton" mnemonicParsing="false" onAction="#onJoinButtonClicked" text="%game.join"/>
<Button fx:id="joinButton" prefWidth="256.0" mnemonicParsing="false" onAction="#onJoinButtonClicked" text="%game.join"/>
</VBox>
<VBox fx:id="teamListPane" maxWidth="1.7976931348623157E308" spacing="10.0" GridPane.columnSpan="2147483647"
GridPane.rowIndex="8"/>
<Label fx:id="playtimeLabel" maxWidth="1.7976931348623157E308" minWidth="0.0" text="&lt;Playtime&gt;"
wrapText="true"
GridPane.rowIndex="6" visible="false">
visible="false" wrapText="true" GridPane.rowIndex="6">
<graphic>
<Region styleClass="icon,time-icon"/>
</graphic>
Expand Down

0 comments on commit 46507ac

Please sign in to comment.