diff --git a/API/src/main/java/cc/fascinated/common/DateUtils.java b/API/src/main/java/cc/fascinated/common/DateUtils.java index 3976426..eccdc18 100644 --- a/API/src/main/java/cc/fascinated/common/DateUtils.java +++ b/API/src/main/java/cc/fascinated/common/DateUtils.java @@ -3,17 +3,36 @@ package cc.fascinated.common; import lombok.experimental.UtilityClass; import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.Date; +import java.util.Locale; /** * @author Fascinated (fascinated7) */ @UtilityClass public class DateUtils { + private static final ZoneId ZONE_ID = ZoneId.of("Europe/London"); + private static final DateTimeFormatter ISO_FORMATTER = DateTimeFormatter.ISO_INSTANT + .withLocale(Locale.UK) + .withZone(ZONE_ID); + private static final DateTimeFormatter SIMPLE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd") + .withLocale(Locale.UK) + .withZone(ZONE_ID); - private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_INSTANT; + /** + * Gets the date from a string. + * + * @param date The date string. + * @return The date. + */ + public static Date getDateFromIsoString(String date) { + return Date.from(Instant.from(ISO_FORMATTER.parse(date))); + } /** * Gets the date from a string. @@ -22,13 +41,25 @@ public class DateUtils { * @return The date. */ public static Date getDateFromString(String date) { - return Date.from(Instant.from(FORMATTER.parse(date))); + LocalDate localDate = LocalDate.parse(date, SIMPLE_FORMATTER); + ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZONE_ID); + return Date.from(zonedDateTime.toInstant()); + } + + /** + * Formats a date to a string. + * + * @param date The date to format. + * @return The formatted date. + */ + public String formatDate(Date date) { + return SIMPLE_FORMATTER.format(date.toInstant()); } /** * Aligns the date to the current hour. *

- * eg: 00:05 -> 00:00 + * eg: 00:05 -> 00:00 *

* * @param date The date to align. @@ -47,4 +78,13 @@ public class DateUtils { public static Date getDaysAgo(int days) { return Date.from(Instant.now().minus(days, ChronoUnit.DAYS)); } + + /** + * Gets the date for midnight today. + * + * @return The date. + */ + public static Date getMidnightToday() { + return Date.from(Instant.now().truncatedTo(ChronoUnit.DAYS)); + } } diff --git a/API/src/main/java/cc/fascinated/controller/UserController.java b/API/src/main/java/cc/fascinated/controller/UserController.java index 53e8ce6..833d7f4 100644 --- a/API/src/main/java/cc/fascinated/controller/UserController.java +++ b/API/src/main/java/cc/fascinated/controller/UserController.java @@ -17,7 +17,8 @@ public class UserController { /** * The user service to use */ - @NonNull private final UserService userService; + @NonNull + private final UserService userService; @Autowired public UserController(@NonNull UserService userService) { @@ -36,4 +37,18 @@ public class UserController { public ResponseEntity getUser(@PathVariable String id) { return ResponseEntity.ok(userService.getUser(id).getAsDTO()); } + + /** + * A GET mapping to retrieve a user's statistic + * history using the users steam id. + * + * @param id the id of the user + * @return the user's statistic history + * @throws BadRequestException if the user is not found + */ + @ResponseBody + @GetMapping(value = "/histories/{id}") + public ResponseEntity getUserHistories(@PathVariable String id) { + return ResponseEntity.ok(userService.getUser(id).getHistory().getPreviousHistories(30)); + } } diff --git a/API/src/main/java/cc/fascinated/model/score/DeviceInformation.java b/API/src/main/java/cc/fascinated/model/score/DeviceInformation.java new file mode 100644 index 0000000..a85b5c3 --- /dev/null +++ b/API/src/main/java/cc/fascinated/model/score/DeviceInformation.java @@ -0,0 +1,39 @@ +package cc.fascinated.model.score; + +import cc.fascinated.model.user.hmd.DeviceController; +import cc.fascinated.model.user.hmd.DeviceHeadset; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author Fascinated (fascinated7) + */ +@AllArgsConstructor +@Getter +public class DeviceInformation { + /** + * The headset that was used to set the score. + */ + private final DeviceHeadset headset; + + /** + * The left controller that was used to set the score. + */ + private final DeviceController leftController; + + /** + * The right controller that was used to set the score. + */ + private final DeviceController rightController; + + /** + * Checks if the device information contains unknown values. + * + * @return if the device information contains unknown values + */ + public boolean containsUnknownDevices() { + return headset == DeviceHeadset.UNKNOWN + || leftController == DeviceController.UNKNOWN + || rightController == DeviceController.UNKNOWN; + } +} diff --git a/API/src/main/java/cc/fascinated/model/score/Score.java b/API/src/main/java/cc/fascinated/model/score/Score.java index 757cf27..fb56cd3 100644 --- a/API/src/main/java/cc/fascinated/model/score/Score.java +++ b/API/src/main/java/cc/fascinated/model/score/Score.java @@ -36,7 +36,8 @@ public class Score { /** * The ID of the player that set the score. */ - @Indexed @JsonIgnore + @Indexed + @JsonIgnore private final String playerId; /** @@ -59,7 +60,8 @@ public class Score { /** * The ID of the leaderboard the score was set on. */ - @Indexed @JsonIgnore + @Indexed + @JsonIgnore private final String leaderboardId; /** @@ -100,6 +102,14 @@ public class Score { */ private final Integer badCuts; + /** + * The device information that was used to set the score. + *

+ * Headset and controllers information. + *

+ */ + private final DeviceInformation deviceInformation; + /** * The score history for map the score was set on. */ @@ -155,4 +165,13 @@ public class Score { public List getPreviousScores() { return previousScores == null ? List.of() : previousScores; } + + /** + * Gets if the score is ranked. + * + * @return true if the score is ranked, false otherwise + */ + public boolean isRanked() { + return pp != null; + } } diff --git a/API/src/main/java/cc/fascinated/model/score/impl/scoresaber/ScoreSaberScore.java b/API/src/main/java/cc/fascinated/model/score/impl/scoresaber/ScoreSaberScore.java index b0f4fc1..2f679f4 100644 --- a/API/src/main/java/cc/fascinated/model/score/impl/scoresaber/ScoreSaberScore.java +++ b/API/src/main/java/cc/fascinated/model/score/impl/scoresaber/ScoreSaberScore.java @@ -1,5 +1,6 @@ package cc.fascinated.model.score.impl.scoresaber; +import cc.fascinated.model.score.DeviceInformation; import cc.fascinated.model.score.Score; import cc.fascinated.platform.Platform; import lombok.Getter; @@ -28,10 +29,10 @@ public class ScoreSaberScore extends Score { private final int maxCombo; public ScoreSaberScore(long id, String playerId, Platform.Platforms platform, String platformScoreId, String leaderboardId, int rank, - double accuracy, Double pp, int score, String[] modifiers, Integer misses, Integer badCuts, List previousScores, - Date timestamp, Double weight, double multiplier, int maxCombo) { + double accuracy, Double pp, int score, String[] modifiers, Integer misses, Integer badCuts, DeviceInformation deviceInformation, + List previousScores, Date timestamp, Double weight, double multiplier, int maxCombo) { super(id, playerId, platform, platformScoreId, leaderboardId, rank, accuracy, pp, score, modifiers, misses, - badCuts, previousScores, timestamp); + badCuts, deviceInformation, previousScores, timestamp); this.weight = weight; this.multiplier = multiplier; this.maxCombo = maxCombo; diff --git a/API/src/main/java/cc/fascinated/model/score/impl/scoresaber/ScoreSaberScoreResponse.java b/API/src/main/java/cc/fascinated/model/score/impl/scoresaber/ScoreSaberScoreResponse.java index 6f5759c..e852928 100644 --- a/API/src/main/java/cc/fascinated/model/score/impl/scoresaber/ScoreSaberScoreResponse.java +++ b/API/src/main/java/cc/fascinated/model/score/impl/scoresaber/ScoreSaberScoreResponse.java @@ -1,6 +1,7 @@ package cc.fascinated.model.score.impl.scoresaber; import cc.fascinated.model.leaderboard.Leaderboard; +import cc.fascinated.model.score.DeviceInformation; import cc.fascinated.model.score.Score; import cc.fascinated.model.user.UserDTO; import cc.fascinated.platform.Platform; @@ -25,10 +26,11 @@ public class ScoreSaberScoreResponse extends ScoreSaberScore { private final Leaderboard leaderboard; public ScoreSaberScoreResponse(long id, String playerId, Platform.Platforms platform, String platformScoreId, String leaderboardId, int rank, - double accuracy, double pp, int score, String[] modifiers, int misses, int badCuts, List previousScores, - Date timestamp, double weight, double multiplier, int maxCombo, UserDTO user, Leaderboard leaderboard) { + double accuracy, double pp, int score, String[] modifiers, int misses, int badCuts, DeviceInformation deviceInformation, + List previousScores, Date timestamp, double weight, double multiplier, int maxCombo, UserDTO user, + Leaderboard leaderboard) { super(id, playerId, platform, platformScoreId, leaderboardId, rank, accuracy, pp, score, modifiers, misses, badCuts, - previousScores, timestamp, weight, multiplier, maxCombo); + deviceInformation, previousScores, timestamp, weight, multiplier, maxCombo); this.user = user; this.leaderboard = leaderboard; } diff --git a/API/src/main/java/cc/fascinated/model/user/ScoreSaberAccount.java b/API/src/main/java/cc/fascinated/model/user/ScoreSaberAccount.java index 86c77c6..1d4025d 100644 --- a/API/src/main/java/cc/fascinated/model/user/ScoreSaberAccount.java +++ b/API/src/main/java/cc/fascinated/model/user/ScoreSaberAccount.java @@ -56,7 +56,7 @@ public class ScoreSaberAccount { token.getCountry(), token.getRank(), token.getCountryRank(), - DateUtils.getDateFromString(token.getFirstSeen()), + DateUtils.getDateFromIsoString(token.getFirstSeen()), new Date() ); } diff --git a/API/src/main/java/cc/fascinated/model/user/User.java b/API/src/main/java/cc/fascinated/model/user/User.java index f6bb890..63e08bf 100644 --- a/API/src/main/java/cc/fascinated/model/user/User.java +++ b/API/src/main/java/cc/fascinated/model/user/User.java @@ -1,18 +1,17 @@ package cc.fascinated.model.user; -import cc.fascinated.model.user.statistic.Statistic; -import cc.fascinated.platform.Platform; +import cc.fascinated.model.user.history.History; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.*; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.log4j.Log4j2; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; import java.util.UUID; /** @@ -21,11 +20,10 @@ import java.util.UUID; @RequiredArgsConstructor @Getter @Setter +@Log4j2 @ToString @Document("user") public class User { - private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("EEE MMM d HH:mm:ss zzz yyyy"); - /** * The ID of the user. */ @@ -67,55 +65,16 @@ public class User { /** * The user's statistic history. */ - public Map> histories; + public History history; /** - * The user's history points history. + * Gets the user's statistic history */ - @JsonIgnore - public Map> getHistories() { - if (this.histories == null) { - this.histories = new HashMap<>(); + public History getHistory() { + if (this.history == null) { + this.history = new History(); } - Map> toReturn = new HashMap<>(); - for (Platform.Platforms platform : histories.keySet()) { - toReturn.put(platform, getHistory(platform)); - } - return toReturn; - } - - /** - * Gets the history points for a platform. - * - * @param platform the platform to get the statistics for - * @return the statistics - */ - @SneakyThrows - public Map getHistory(@NonNull Platform.Platforms platform) { - if (this.histories == null) { - this.histories = new HashMap<>(); - } - Map statisticMap = this.histories.computeIfAbsent(platform, k -> new HashMap<>()); - Map statistics = new HashMap<>(); - for (Map.Entry entry : statisticMap.entrySet()) { - statistics.put(DATE_FORMAT.parse(entry.getKey()), entry.getValue()); - } - return statistics; - } - - /** - * Adds a history point to the user's history. - * - * @param platform the platform to add the statistic for - * @param date the date of the statistic - * @param statistic the statistic to add - */ - public void addHistory(@NonNull Platform.Platforms platform, @NonNull Date date, @NonNull Statistic statistic) { - if (this.histories == null) { - this.histories = new HashMap<>(); - } - Map statisticMap = this.histories.computeIfAbsent(platform, k -> new HashMap<>()); - statisticMap.put(String.valueOf(date.toString()), statistic); + return this.history; } /** diff --git a/API/src/main/java/cc/fascinated/model/user/history/History.java b/API/src/main/java/cc/fascinated/model/user/history/History.java new file mode 100644 index 0000000..9a195ef --- /dev/null +++ b/API/src/main/java/cc/fascinated/model/user/history/History.java @@ -0,0 +1,78 @@ +package cc.fascinated.model.user.history; + +import cc.fascinated.common.DateUtils; +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.util.*; + +/** + * @author Fascinated (fascinated7) + */ +public class History { + /** + * The user's history points in time. + */ + private Map histories; + + /** + * The user's history points history. + */ + @JsonIgnore + public Map getHistories() { + if (this.histories == null) { + this.histories = new HashMap<>(); + } + Map toReturn = new HashMap<>(); + this.histories.forEach((key, value) -> toReturn.put(DateUtils.getDateFromString(key), value)); + return toReturn; + } + + /** + * Gets the user's history for today. + * + * @return the user's history for today + */ + @JsonIgnore + public HistoryPoint getTodayHistory() { + if (this.histories == null) { + this.histories = new HashMap<>(); + } + Date midnight = DateUtils.getMidnightToday(); + return this.histories.computeIfAbsent(DateUtils.formatDate(midnight), key -> new HistoryPoint()); + } + + /** + * Gets the user's history for a specific date. + * + * @param date the date to get the history for + * @return the user's history for the date + */ + public HistoryPoint getHistoryForDate(Date date) { + if (this.histories == null) { + this.histories = new HashMap<>(); + } + return this.histories.get(DateUtils.formatDate(date)); + } + + /** + * Gets the user's HistoryPoint history for + * an amount of days ago. + * + * @param days the amount of days ago + * @return the user's HistoryPoint history + */ + public TreeMap getPreviousHistories(int days) { + Date date = DateUtils.getDaysAgo(days); + Map toReturn = new HashMap<>(); + for (Map.Entry history : getHistories().entrySet()) { + if (history.getKey().after(date)) { + toReturn.put(DateUtils.formatDate(history.getKey()), history.getValue()); + } + } + + // Sort the history by date (newest > oldest) + TreeMap sorted = new TreeMap<>(Comparator.comparing(DateUtils::getDateFromString).reversed()); + sorted.putAll(toReturn); + return sorted; + } +} diff --git a/API/src/main/java/cc/fascinated/model/user/history/HistoryPoint.java b/API/src/main/java/cc/fascinated/model/user/history/HistoryPoint.java new file mode 100644 index 0000000..22652aa --- /dev/null +++ b/API/src/main/java/cc/fascinated/model/user/history/HistoryPoint.java @@ -0,0 +1,60 @@ +package cc.fascinated.model.user.history; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author Fascinated (fascinated7) + */ +@Getter +@Setter +public class HistoryPoint { + /** + * The rank of the player. + */ + private Integer rank; + + /** + * The pp of the player. + */ + private Integer countryRank; + + /** + * The pp of the player. + */ + private Double pp; + + /** + * Play count of all the player's scores. + */ + private Integer totalPlayCount; + + /** + * Play count of all the player's ranked scores. + */ + private Integer totalRankedPlayCount; + + /** + * Play count for this day's unranked scores. + */ + private Integer unrankedPlayCount = 0; + + /** + * Play count for this day's ranked scores. + */ + private Integer rankedPlayCount = 0; + + /** + * Increment the total ranked play count for this day. + */ + public void incrementRankedPlayCount() { + rankedPlayCount++; + } + + /** + * Increment the total unranked play count for this day. + */ + public void incrementUnrankedPlayCount() { + unrankedPlayCount++; + } +} diff --git a/API/src/main/java/cc/fascinated/model/user/hmd/DeviceController.java b/API/src/main/java/cc/fascinated/model/user/hmd/DeviceController.java new file mode 100644 index 0000000..9a0a083 --- /dev/null +++ b/API/src/main/java/cc/fascinated/model/user/hmd/DeviceController.java @@ -0,0 +1,50 @@ +package cc.fascinated.model.user.hmd; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author Fascinated (fascinated7) + */ +@AllArgsConstructor +@Getter +public enum DeviceController { + UNKNOWN("Unknown"), + + /** + * Oculus Controllers + */ + OCULUS_QUEST_TOUCH("Touch"), + OCULUS_QUEST_2_TOUCH("Quest 2 Touch"), + OCULUS_QUEST_3_TOUCH("Quest 3 Touch"), + + /** + * HP Controllers + */ + HP_REVERB("HP Reverb"), + + /** + * Valve Controllers + */ + VALVE_KNUCKLES("Knuckles"); + + /** + * The controller name + */ + private final String name; + + /** + * Gets a controller by its name. + * + * @param name the name of the controller + * @return the controller + */ + public static DeviceController getByName(String name) { + for (DeviceController deviceController : values()) { + if (deviceController.getName().equalsIgnoreCase(name)) { + return deviceController; + } + } + return null; + } +} diff --git a/API/src/main/java/cc/fascinated/model/user/hmd/DeviceHeadset.java b/API/src/main/java/cc/fascinated/model/user/hmd/DeviceHeadset.java new file mode 100644 index 0000000..eaf8089 --- /dev/null +++ b/API/src/main/java/cc/fascinated/model/user/hmd/DeviceHeadset.java @@ -0,0 +1,57 @@ +package cc.fascinated.model.user.hmd; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author Fascinated (fascinated7) + */ +@AllArgsConstructor +@Getter +public enum DeviceHeadset { + UNKNOWN("Unknown"), + + /** + * Oculus HMDs + */ + OCULUS_CV1("Rift"), + OCULUS_QUEST("Quest"), + OCULUS_QUEST_2("Quest 2"), + OCULUS_QUEST_3("Quest 3"), + OCULUS_RIFT_S("Rift S"), + + /** + * HTC HMDs + */ + HTC_VIVE("Vive"), + + /** + * HP HMDs + */ + HP_REVERB("HP Reverb"), + + /** + * Valve HMDs + */ + VALVE_INDEX("Valve Index"); + + /** + * The name of the headset. + */ + private final String name; + + /** + * Gets a headset by its name. + * + * @param name the name of the headset + * @return the headset + */ + public static DeviceHeadset getByName(String name) { + for (DeviceHeadset deviceHeadset : values()) { + if (deviceHeadset.getName().equalsIgnoreCase(name)) { + return deviceHeadset; + } + } + return null; + } +} diff --git a/API/src/main/java/cc/fascinated/model/user/statistic/Statistic.java b/API/src/main/java/cc/fascinated/model/user/statistic/Statistic.java deleted file mode 100644 index a5d5b3e..0000000 --- a/API/src/main/java/cc/fascinated/model/user/statistic/Statistic.java +++ /dev/null @@ -1,23 +0,0 @@ -package cc.fascinated.model.user.statistic; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.data.mongodb.core.mapping.Document; - -/** - * @author Fascinated (fascinated7) - */ -@AllArgsConstructor -@Getter -@Document("player_histories") -public class Statistic { - /** - * The rank of the player. - */ - private final int rank; - - /** - * The pp of the player. - */ - private final int countryRank; -} diff --git a/API/src/main/java/cc/fascinated/model/user/statistic/impl/ScoreSaberStatistic.java b/API/src/main/java/cc/fascinated/model/user/statistic/impl/ScoreSaberStatistic.java deleted file mode 100644 index e1820a8..0000000 --- a/API/src/main/java/cc/fascinated/model/user/statistic/impl/ScoreSaberStatistic.java +++ /dev/null @@ -1,38 +0,0 @@ -package cc.fascinated.model.user.statistic.impl; - -import cc.fascinated.model.user.statistic.Statistic; -import lombok.Getter; - -/** - * @author Fascinated (fascinated7) - */ -@Getter -public class ScoreSaberStatistic extends Statistic { - /** - * The total score of the player. - */ - private final long totalScore; - - /** - * The total ranked score of the player. - */ - private final long totalRankedScore; - - /** - * The average ranked accuracy of the player. - */ - private final double averageRankedAccuracy; - - /** - * The total play count of the player. - */ - private final int totalPlayCount; - - public ScoreSaberStatistic(int rank, int countryRank, long totalScore, long totalRankedScore, double averageRankedAccuracy, int totalPlayCount) { - super(rank, countryRank); - this.totalScore = totalScore; - this.totalRankedScore = totalRankedScore; - this.averageRankedAccuracy = averageRankedAccuracy; - this.totalPlayCount = totalPlayCount; - } -} diff --git a/API/src/main/java/cc/fascinated/platform/Platform.java b/API/src/main/java/cc/fascinated/platform/Platform.java index ce54c28..b939959 100644 --- a/API/src/main/java/cc/fascinated/platform/Platform.java +++ b/API/src/main/java/cc/fascinated/platform/Platform.java @@ -72,7 +72,7 @@ public abstract class Platform { * Called to update the players * data in QuestDB. */ - public abstract void updatePlayers(); + public abstract void trackPlayerMetrics(); /** * Called to update the metrics diff --git a/API/src/main/java/cc/fascinated/platform/impl/ScoreSaberPlatform.java b/API/src/main/java/cc/fascinated/platform/impl/ScoreSaberPlatform.java index b7402a3..c2b2ae1 100644 --- a/API/src/main/java/cc/fascinated/platform/impl/ScoreSaberPlatform.java +++ b/API/src/main/java/cc/fascinated/platform/impl/ScoreSaberPlatform.java @@ -7,7 +7,7 @@ import cc.fascinated.model.token.ScoreSaberAccountToken; import cc.fascinated.model.token.ScoreSaberLeaderboardPageToken; import cc.fascinated.model.token.ScoreSaberLeaderboardToken; import cc.fascinated.model.user.User; -import cc.fascinated.model.user.statistic.impl.ScoreSaberStatistic; +import cc.fascinated.model.user.history.HistoryPoint; import cc.fascinated.platform.CurvePoint; import cc.fascinated.platform.Platform; import cc.fascinated.services.ScoreSaberService; @@ -137,21 +137,19 @@ public class ScoreSaberPlatform extends Platform { } @Override - public void updatePlayers() { - Date date = DateUtils.alignToCurrentHour(new Date()); + public void trackPlayerMetrics() { + Date date = DateUtils.getMidnightToday(); for (User user : this.userService.getUsers(false)) { if (!user.isLinkedAccount()) { // Check if the user has linked their account continue; } ScoreSaberAccountToken account = scoreSaberService.getAccount(user); // Get the account from the ScoreSaber API - user.addHistory(this.getPlatform(), date, new ScoreSaberStatistic( - account.getRank(), - account.getCountryRank(), - account.getScoreStats().getTotalScore(), - account.getScoreStats().getTotalRankedScore(), - account.getScoreStats().getAverageRankedAccuracy(), - account.getScoreStats().getTotalPlayCount() - )); // Add the statistic to the user's history + HistoryPoint history = user.getHistory().getHistoryForDate(date); + history.setPp(account.getPp()); + history.setRank(account.getRank()); + history.setCountryRank(account.getCountryRank()); + history.setTotalPlayCount(account.getScoreStats().getTotalPlayCount()); + history.setTotalRankedPlayCount((int) account.getScoreStats().getTotalRankedScore()); this.userService.saveUser(user); // Save the user } } diff --git a/API/src/main/java/cc/fascinated/services/PlatformService.java b/API/src/main/java/cc/fascinated/services/PlatformService.java index 681424d..5734cef 100644 --- a/API/src/main/java/cc/fascinated/services/PlatformService.java +++ b/API/src/main/java/cc/fascinated/services/PlatformService.java @@ -43,7 +43,7 @@ public class PlatformService { /** * Updates the platform metrics. *

- * This method is scheduled to run every minute. + * This method is scheduled to run every 5 minutes. *

*/ @Scheduled(cron = "0 */5 * * * *") @@ -65,7 +65,7 @@ public class PlatformService { public void updatePlayerMetrics() { log.info("Updating %s platform player metrics...".formatted(this.platforms.size())); for (Platform platform : this.platforms) { - platform.updatePlayers(); + platform.trackPlayerMetrics(); } log.info("Finished updating platform player metrics."); } diff --git a/API/src/main/java/cc/fascinated/services/ScoreService.java b/API/src/main/java/cc/fascinated/services/ScoreService.java index b17eddd..1d09b8c 100644 --- a/API/src/main/java/cc/fascinated/services/ScoreService.java +++ b/API/src/main/java/cc/fascinated/services/ScoreService.java @@ -5,6 +5,7 @@ import cc.fascinated.common.EnumUtils; import cc.fascinated.common.MathUtils; import cc.fascinated.common.Tuple; import cc.fascinated.model.leaderboard.Leaderboard; +import cc.fascinated.model.score.DeviceInformation; import cc.fascinated.model.score.Score; import cc.fascinated.model.score.TotalScoresResponse; import cc.fascinated.model.score.impl.scoresaber.ScoreSaberScore; @@ -14,6 +15,9 @@ import cc.fascinated.model.token.ScoreSaberPlayerScoreToken; import cc.fascinated.model.token.ScoreSaberScoreToken; import cc.fascinated.model.user.User; import cc.fascinated.model.user.UserDTO; +import cc.fascinated.model.user.history.HistoryPoint; +import cc.fascinated.model.user.hmd.DeviceController; +import cc.fascinated.model.user.hmd.DeviceHeadset; import cc.fascinated.platform.Platform; import cc.fascinated.repository.ScoreRepository; import lombok.NonNull; @@ -96,6 +100,7 @@ public class ScoreService { score.getModifiers(), score.getMisses(), score.getBadCuts(), + score.getDeviceInformation(), score.getPreviousScores(), score.getTimestamp(), scoreSaberScore.getWeight(), @@ -130,6 +135,12 @@ public class ScoreService { } } + /** + * Gets the total scores for the platform. + * + * @param platform The platform to get the scores from. + * @return The total scores. + */ public TotalScoresResponse getTotalScores(Platform.Platforms platform) { return new TotalScoresResponse( scoreRepository.getTotalScores(platform), @@ -179,15 +190,27 @@ public class ScoreService { modifiers.length == 0 ? null : modifiers, // no modifiers, set to null to save data score.getMissedNotes() == 0 ? null : score.getMissedNotes(), // no misses, set to null to save data score.getBadCuts() == 0 ? null : score.getBadCuts(), // no bad cuts, set to null to save data + new DeviceInformation( + score.getDeviceHmd() == null ? DeviceHeadset.UNKNOWN : DeviceHeadset.getByName(score.getDeviceHmd()), + score.getDeviceControllerLeft() == null ? DeviceController.UNKNOWN : DeviceController.getByName(score.getDeviceControllerLeft()), + score.getDeviceControllerRight() == null ? DeviceController.UNKNOWN : DeviceController.getByName(score.getDeviceControllerRight()) + ), previousScores, - DateUtils.getDateFromString(score.getTimeSet()), + DateUtils.getDateFromIsoString(score.getTimeSet()), score.getWeight() == 0 ? null : score.getWeight(), // no weight, set to null to save data score.getMultiplier(), score.getMaxCombo() ); - scoreRepository.save(scoreSaberScore); + this.saveScore(user, scoreSaberScore); this.logScore(Platform.Platforms.SCORESABER, Leaderboard.getFromScoreSaberToken(leaderboard), scoreSaberScore, user, previousScoreExists && previousScore.getScore() < scoreSaberScore.getScore()); + if (scoreSaberScore.getDeviceInformation().containsUnknownDevices()) { + log.warn(" - Score contains unknown device: hmd: {}, controller left: {}, controller right: {}", + score.getDeviceHmd(), + score.getDeviceControllerLeft(), + score.getDeviceControllerRight() + ); + } } /** @@ -239,6 +262,22 @@ public class ScoreService { return scoreRepository.getBestImprovedScores(platform, DateUtils.getDaysAgo(days)); } + /** + * Saves a score. + * + * @param score The score to save. + */ + public void saveScore(User user, Score score) { + HistoryPoint todayHistory = user.getHistory().getTodayHistory(); + if (score.isRanked()) { + todayHistory.incrementRankedPlayCount(); + } else { + todayHistory.incrementUnrankedPlayCount(); + } + userService.saveUser(user); // Save the user + scoreRepository.save(score); // Save the score + } + /** * Logs a score. * @@ -250,6 +289,7 @@ public class ScoreService { @NonNull User user, boolean improvedScore) { String platformName = EnumUtils.getEnumName(platform); boolean isRanked = score.getPp() != 0; + log.info("[{}] {}Tracked{} Score! id: {}, acc: {}%, {} score id: {},{} leaderboard: {}, difficulty: {}, player: {} ({})", platformName, improvedScore ? "Improved " : "",