Skip to content

Commit d5612b7

Browse files
committed
Show game result for matchmaker games
1 parent efd0a54 commit d5612b7

File tree

8 files changed

+160
-22
lines changed

8 files changed

+160
-22
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.faforever.client.domain;
2+
3+
public enum GameResult {
4+
VICTORY, DEFEAT, DRAW, UNKNOWN
5+
}

src/main/java/com/faforever/client/domain/ReplayBean.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,6 @@ public ObjectProperty<List<LeagueScoreJournalBean>> leagueScoresProperty() {
322322

323323
public void setLeagueScores(List<LeagueScoreJournalBean> scores) {
324324
leagueScores.set(scores);
325-
System.out.println(scores);
326325
}
327326

328327
@Value

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

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33

44
import com.faforever.client.domain.GameBean;
55
import com.faforever.client.domain.GamePlayerStatsBean;
6+
import com.faforever.client.domain.GameResult;
67
import com.faforever.client.domain.PlayerBean;
78
import com.faforever.client.domain.SubdivisionBean;
89
import com.faforever.client.fx.Controller;
910
import com.faforever.client.fx.FxApplicationThreadExecutor;
11+
import com.faforever.client.fx.JavaFxUtil;
1012
import com.faforever.client.fx.SimpleChangeListener;
1113
import com.faforever.client.i18n.I18n;
1214
import com.faforever.client.player.PlayerService;
@@ -19,6 +21,7 @@
1921
import javafx.beans.property.SimpleObjectProperty;
2022
import javafx.beans.value.ObservableValue;
2123
import javafx.collections.FXCollections;
24+
import javafx.css.PseudoClass;
2225
import javafx.scene.Node;
2326
import javafx.scene.control.Label;
2427
import javafx.scene.layout.Pane;
@@ -44,6 +47,12 @@
4447
@Slf4j
4548
@RequiredArgsConstructor
4649
public class TeamCardController implements Controller<Node> {
50+
51+
private static final PseudoClass VICTORY = PseudoClass.getPseudoClass("victory");
52+
private static final PseudoClass DEFEAT = PseudoClass.getPseudoClass("defeat");
53+
private static final PseudoClass DRAW = PseudoClass.getPseudoClass("draw");
54+
private static final PseudoClass UNKNOWN = PseudoClass.getPseudoClass("unknown");
55+
4756
private final I18n i18n;
4857
private final PlayerService playerService;
4958
private final FxApplicationThreadExecutor fxApplicationThreadExecutor;
@@ -52,20 +61,23 @@ public class TeamCardController implements Controller<Node> {
5261
public Pane teamPaneRoot;
5362
public VBox teamPane;
5463
public Label teamNameLabel;
64+
public Label gameResultLabel;
5565

5666
private final ObjectProperty<List<Integer>> playerIds = new SimpleObjectProperty<>(List.of());
5767
private final ObjectProperty<List<PlayerBean>> players = new SimpleObjectProperty<>(List.of());
5868
private final ObjectProperty<Function<PlayerBean, Integer>> ratingProvider = new SimpleObjectProperty<>();
5969
private final ObjectProperty<Function<PlayerBean, SubdivisionBean>> divisionProvider = new SimpleObjectProperty<>();
6070
private final ObjectProperty<Function<PlayerBean, Faction>> factionProvider = new SimpleObjectProperty<>();
6171
private final ObjectProperty<RatingPrecision> ratingPrecision = new SimpleObjectProperty<>();
72+
private final ObjectProperty<GameResult> teamResult = new SimpleObjectProperty<>();
6273
private final IntegerProperty teamId = new SimpleIntegerProperty();
6374
private final SimpleChangeListener<List<PlayerBean>> playersListener = this::populateTeamContainer;
6475
private final ObservableValue<Integer> teamRating = ratingProvider.flatMap(provider -> ratingPrecision.flatMap(precision -> players.map(playerBeans -> playerBeans.stream()
6576
.map(provider)
6677
.filter(Objects::nonNull)
6778
.map(rating -> precision == RatingPrecision.ROUNDED ? RatingUtil.getRoundedRating(rating) : rating)
6879
.reduce(0, Integer::sum))));
80+
// we need to set this null when no nunNull objects are found
6981

7082
private final Map<PlayerBean, PlayerCardController> playerCardControllersMap = new HashMap<>();
7183

@@ -76,13 +88,18 @@ public void initialize() {
7688
case GameBean.OBSERVERS_TEAM -> i18n.get("game.tooltip.observers");
7789
default -> {
7890
try {
79-
yield i18n.get("game.tooltip.teamTitle", id.intValue() - 1, teamRating);
91+
if (teamRating == null) {
92+
yield i18n.get("game.tooltip.teamTitleNoRating", id.intValue() - 1);
93+
} else {
94+
yield i18n.get("game.tooltip.teamTitle", id.intValue() - 1, teamRating);
95+
}
8096
} catch (NumberFormatException e) {
8197
yield "";
8298
}
8399
}
84100
})));
85-
101+
JavaFxUtil.bindManagedToVisible(gameResultLabel);
102+
gameResultLabel.visibleProperty().bind(gameResultLabel.textProperty().isEmpty().not());
86103
players.addListener(playersListener);
87104
}
88105

@@ -111,6 +128,27 @@ private List<PlayerCardController> createPlayerCardControllers(List<PlayerBean>
111128
return controller;
112129
}).toList();
113130
}
131+
132+
public void showGameResult() {
133+
switch (teamResult.get()) {
134+
case VICTORY -> {
135+
gameResultLabel.setText(i18n.get("game.resultVictory"));
136+
gameResultLabel.pseudoClassStateChanged(VICTORY, true);
137+
}
138+
case DEFEAT -> {
139+
gameResultLabel.setText(i18n.get("game.resultDefeat"));
140+
gameResultLabel.pseudoClassStateChanged(DEFEAT, true);
141+
}
142+
case DRAW -> {
143+
gameResultLabel.setText(i18n.get("game.resultDraw"));
144+
gameResultLabel.pseudoClassStateChanged(DRAW, true);
145+
}
146+
case UNKNOWN -> {
147+
gameResultLabel.setText(i18n.get("game.resultUnknown"));
148+
gameResultLabel.pseudoClassStateChanged(UNKNOWN, true);
149+
}
150+
}
151+
}
114152

115153
public void bindPlayersToPlayerIds() {
116154
players.bind(playerIds.map(ids -> ids.stream()
@@ -163,6 +201,18 @@ public ObjectProperty<Function<PlayerBean, Integer>> ratingProviderProperty() {
163201
return ratingProvider;
164202
}
165203

204+
public void setTeamResult(GameResult teamResult) {
205+
this.teamResult.set(teamResult);
206+
}
207+
208+
public GameResult getTeamResult() {
209+
return teamResult.get();
210+
}
211+
212+
public ObjectProperty<GameResult> teamResult() {
213+
return teamResult;
214+
}
215+
166216
public void setStats(List<GamePlayerStatsBean> teamPlayerStats) {
167217
for (GamePlayerStatsBean playerStats : teamPlayerStats) {
168218
PlayerCardController controller = playerCardControllersMap.get(playerStats.getPlayer());

src/main/java/com/faforever/client/replay/ReplayDetailController.java

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.faforever.client.replay;
22

33
import com.faforever.client.config.ClientProperties;
4+
import com.faforever.client.domain.AbstractEntityBean;
45
import com.faforever.client.domain.FeaturedModBean;
56
import com.faforever.client.domain.GamePlayerStatsBean;
7+
import com.faforever.client.domain.GameResult;
68
import com.faforever.client.domain.LeagueScoreJournalBean;
79
import com.faforever.client.domain.MapBean;
810
import com.faforever.client.domain.MapVersionBean;
@@ -45,6 +47,7 @@
4547
import com.faforever.commons.api.dto.Validity;
4648
import com.google.common.annotations.VisibleForTesting;
4749
import com.google.common.eventbus.EventBus;
50+
import javafx.application.Platform;
4851
import javafx.beans.binding.Bindings;
4952
import javafx.beans.binding.BooleanExpression;
5053
import javafx.beans.binding.StringExpression;
@@ -85,6 +88,7 @@
8588
import java.util.List;
8689
import java.util.Map;
8790
import java.util.Objects;
91+
import java.util.Set;
8892
import java.util.concurrent.CompletableFuture;
8993
import java.util.function.Function;
9094
import java.util.stream.Collectors;
@@ -305,7 +309,13 @@ private void onReplayChanged(ReplayBean newValue) {
305309
enrichReplayLater(newValue.getReplayFile(), newValue);
306310
}
307311

308-
leaderboardService.getLeagueScoreJournalForReplay(newValue).thenAccept(newValue::setLeagueScores);
312+
leaderboardService.getLeagueScoreJournalForReplay(newValue)
313+
.thenAccept(scores -> Platform.runLater(() -> {
314+
newValue.setLeagueScores(scores);
315+
// This looks extractResultFromJournal bit ugly. Ideally we should wait with drawing the window until we have the league scores,
316+
// then we don't need to trigger extractResultFromJournal redraw here
317+
populateTeamsContainer(teams.getValue());
318+
}));
309319

310320
reviewsController.setCanWriteReview(true);
311321

@@ -445,6 +455,7 @@ private List<TeamCardController> createTeamCardControllers(Map<String, List<Game
445455

446456
TeamCardController controller = uiService.loadFxml("theme/team_card.fxml");
447457

458+
controller.setTeamResult(calculateGameResult(statsByPlayer.keySet()));
448459
controller.setRatingPrecision(RatingPrecision.EXACT);
449460
controller.setRatingProvider(player -> getPlayerRating(player, statsByPlayer));
450461
controller.setDivisionProvider(this::getPlayerDivision);
@@ -455,6 +466,45 @@ private List<TeamCardController> createTeamCardControllers(Map<String, List<Game
455466
return controller;
456467
}).toList();
457468
}
469+
470+
private GameResult calculateGameResult(Set<PlayerBean> playerBeans) {
471+
int change;
472+
if (replay.get().getLeagueScores() != null) {
473+
change = replay
474+
.map(ReplayBean::getLeagueScores)
475+
.map(leagueScoreJournalBeans -> leagueScoreJournalBeans.stream()
476+
.filter(leagueScoreJournalBean -> playerBeans.stream()
477+
.map(AbstractEntityBean::getId)
478+
.toList()
479+
.contains(leagueScoreJournalBean.getLoginId()))
480+
.map(this::extractResultFromJournal)
481+
.reduce(0, Integer::sum)
482+
).getValue();
483+
} else {
484+
// calculate from league player stats score changes
485+
return GameResult.UNKNOWN;
486+
}
487+
488+
if (change > 0) {
489+
return GameResult.VICTORY;
490+
} else if (change < 0) {
491+
return GameResult.DEFEAT;
492+
} else {
493+
return GameResult.DRAW;
494+
}
495+
}
496+
497+
private int extractResultFromJournal(LeagueScoreJournalBean journalEntry) {
498+
int result;
499+
if (journalEntry.getDivisionAfter() == journalEntry.getDivisionBefore()) {
500+
result = Integer.compare(journalEntry.getScoreAfter(), journalEntry.getScoreBefore());
501+
} else if (journalEntry.getDivisionAfter().getDivision() == journalEntry.getDivisionBefore().getDivision()) {
502+
result = Integer.compare(journalEntry.getDivisionAfter().getIndex(), journalEntry.getDivisionBefore().getIndex());
503+
} else {
504+
result = Integer.compare(journalEntry.getDivisionAfter().getDivision().getIndex(), journalEntry.getDivisionBefore().getDivision().getIndex());
505+
}
506+
return result;
507+
}
458508

459509
private Faction getPlayerFaction(PlayerBean player, Map<PlayerBean, GamePlayerStatsBean> statsByPlayerId) {
460510
GamePlayerStatsBean playerStats = statsByPlayerId.get(player);
@@ -463,21 +513,24 @@ private Faction getPlayerFaction(PlayerBean player, Map<PlayerBean, GamePlayerSt
463513

464514
private Integer getPlayerRating(PlayerBean player, Map<PlayerBean, GamePlayerStatsBean> statsByPlayerId) {
465515
GamePlayerStatsBean playerStats = statsByPlayerId.get(player);
466-
return playerStats == null ? null : playerStats.getLeaderboardRatingJournals()
467-
.stream()
468-
.findFirst()
469-
.filter(ratingJournal -> ratingJournal.getMeanBefore() != null)
470-
.filter(ratingJournal -> ratingJournal.getDeviationBefore() != null)
471-
.map(RatingUtil::getRating)
472-
.orElse(null);
516+
if (replay.get().getLeagueScores() != null || playerStats == null) {
517+
return null;
518+
}
519+
return playerStats.getLeaderboardRatingJournals()
520+
.stream()
521+
.findFirst()
522+
.filter(ratingJournal -> ratingJournal.getMeanBefore() != null)
523+
.filter(ratingJournal -> ratingJournal.getDeviationBefore() != null)
524+
.map(RatingUtil::getRating)
525+
.orElse(null);
473526
}
474-
527+
475528
private SubdivisionBean getPlayerDivision(PlayerBean player) {
476529
return replay.map(ReplayBean::getLeagueScores).map(leagueScoreJournalBeans -> leagueScoreJournalBeans
477530
.stream()
478531
.filter(leagueScoreJournalBean -> leagueScoreJournalBean.getLoginId() == player.getId())
479532
.findFirst()
480-
.map(LeagueScoreJournalBean::getDivisionAfter)
533+
.map(LeagueScoreJournalBean::getDivisionBefore)
481534
.orElse(null)
482535
).getValue();
483536
}
@@ -531,9 +584,11 @@ public void copyLink() {
531584
}
532585

533586
public void showRatingChange() {
534-
Map<String, List<GamePlayerStatsBean>> teamsValue = teams.get();
535-
536-
teamCardControllers.forEach(teamCardController -> teamCardController.setStats(teamsValue.get(String.valueOf(teamCardController.getTeamId()))));
587+
teamCardControllers.forEach(TeamCardController::showGameResult);
588+
if (replay.get().getLeagueScores().isEmpty()) {
589+
Map<String, List<GamePlayerStatsBean>> teamsValue = teams.get();
590+
teamCardControllers.forEach(teamCardController -> teamCardController.setStats(teamsValue.get(String.valueOf(teamCardController.getTeamId()))));
591+
}
537592
}
538593

539594
public void onMapPreviewImageClicked() {

src/main/resources/i18n/messages.properties

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ game.cancel = Cancel
181181
game.host = Host
182182
game.tooltip.teamTitle = Team {0} ({1,number,#})
183183
game.tooltip.teamTitleNoTeam = No Team
184+
game.tooltip.teamTitleNoRating = Team {0}
184185
game.tooltip.observers = Observers
185186
game.join.passwordPrompt = Enter game password
186187
game.join.passwordWrong = Incorrect password
@@ -544,7 +545,11 @@ mod.filename = Filename
544545
mod.version = Version
545546
UIDNotExecuted = UID binary could not be executed. It might have been removed, not found or inaccessible.
546547
replay.copyUrl = Copy replay URL
547-
game.showRatingChange = Show rating change
548+
game.showGameResult = Show game result
549+
game.resultVictory = Victory
550+
game.resultDefeat = Defeat
551+
game.resultDraw = Draw
552+
game.resultUnknown = Unknown
548553
game.notValid = The game was not rated
549554
game.notRatedYet = Rating change is not yet available
550555
game.ended = Did you enjoy "{0}"? Let other people know\!

src/main/resources/theme/style.css

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1216,7 +1216,29 @@
12161216
-fx-font-size: 1.5em;
12171217
}
12181218

1219-
/***************** Rating change*****************/
1219+
/***************** Game Result *****************/
1220+
1221+
.game-result-label {
1222+
-fx-font-weight: bold;
1223+
}
1224+
1225+
.game-result-label:victory {
1226+
-fx-text-fill: -good;
1227+
}
1228+
1229+
.game-result-label:defeat {
1230+
-fx-text-fill: -bad;
1231+
}
1232+
1233+
.game-result-label:draw {
1234+
-fx-text-fill: CHART_COLOR_3;
1235+
}
1236+
1237+
.game-result-label:unknown {
1238+
-fx-text-fill: -warn;
1239+
}
1240+
1241+
/***************** Rating change *****************/
12201242

12211243
.rating-change-label {
12221244
-fx-font-size: 9px;

src/main/resources/theme/team_card.fxml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
<?import javafx.geometry.Insets?>
44
<?import javafx.scene.control.Label?>
55
<?import javafx.scene.layout.VBox?>
6+
<?import javafx.scene.layout.HBox?>
67
<VBox xmlns:fx="http://javafx.com/fxml/1" VBox.vgrow="ALWAYS" fx:id="teamPaneRoot"
78
xmlns="http://javafx.com/javafx/10.0.2" fx:controller="com.faforever.client.game.TeamCardController">
89
<VBox VBox.vgrow="ALWAYS" prefWidth="230.0" spacing="20.0" styleClass="card">
9-
<children>
10+
<HBox spacing="20.0">
1011
<Label fx:id="teamNameLabel" styleClass="h3" text="Label"/>
11-
<VBox fx:id="teamPane"/>
12-
</children>
12+
<Label fx:id="gameResultLabel" styleClass="game-result-label" visible="false"/>
13+
</HBox>
14+
<VBox fx:id="teamPane"/>
1315
<padding>
1416
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
1517
</padding>

src/main/resources/theme/vault/replay/replay_detail.fxml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@
128128
<Label maxWidth="1.7976931348623157E308" styleClass="h2" text="%game.lineUp"
129129
HBox.hgrow="ALWAYS"/>
130130
<Button fx:id="showRatingChangeButton" alignment="CENTER" mnemonicParsing="false"
131-
onAction="#showRatingChange" text="%game.showRatingChange"/>
131+
onAction="#showRatingChange" text="%game.showGameResult"/>
132132
<Label fx:id="notRatedReasonLabel" alignment="CENTER"/>
133133
</HBox>
134134
<HBox fx:id="teamsContainer" alignment="TOP_CENTER" spacing="20.0"/>

0 commit comments

Comments
 (0)