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 " : "",