From 8dfdc8c535ef782942a4f7fb394d2e3698c66f7b Mon Sep 17 00:00:00 2001 From: Liam Date: Thu, 1 Aug 2024 23:24:34 +0100 Subject: [PATCH] API: switch to Mongo for score tracking --- API/src/main/java/cc/fascinated/Main.java | 2 +- .../java/cc/fascinated/common/EnumUtils.java | 24 +++ .../controller/ScoresController.java | 28 +-- .../java/cc/fascinated/model/Counter.java | 25 +++ .../model/leaderboard/Leaderboard.java | 4 +- .../java/cc/fascinated/model/score/Score.java | 105 ++++++++++ .../fascinated/model/score/ScoreResponse.java | 91 -------- .../model/score/ScoresOverResponse.java | 24 --- .../fascinated/model/score/TrackedScore.java | 99 --------- .../impl/scoresaber/ScoreSaberScore.java | 49 +++++ .../scoresaber/ScoreSaberScoreResponse.java | 33 +++ .../model/user/ScoreSaberAccount.java | 1 - .../java/cc/fascinated/model/user/User.java | 5 +- .../platform/impl/ScoreSaberPlatform.java | 51 ++--- .../couchdb/TrackedScoreRepository.java | 90 -------- .../repository/mongo/CounterRepository.java | 9 + .../repository/mongo/ScoreRepository.java | 78 +++++++ .../fascinated/services/CounterService.java | 54 +++++ .../fascinated/services/PlatformService.java | 4 + .../cc/fascinated/services/ScoreService.java | 196 ++++++++++++++++++ .../services/TrackedScoreService.java | 165 --------------- .../cc/fascinated/services/UserService.java | 3 +- .../websocket/impl/ScoreSaberWebsocket.java | 54 +---- 23 files changed, 614 insertions(+), 580 deletions(-) create mode 100644 API/src/main/java/cc/fascinated/common/EnumUtils.java create mode 100644 API/src/main/java/cc/fascinated/model/Counter.java create mode 100644 API/src/main/java/cc/fascinated/model/score/Score.java delete mode 100644 API/src/main/java/cc/fascinated/model/score/ScoreResponse.java delete mode 100644 API/src/main/java/cc/fascinated/model/score/ScoresOverResponse.java delete mode 100644 API/src/main/java/cc/fascinated/model/score/TrackedScore.java create mode 100644 API/src/main/java/cc/fascinated/model/score/impl/scoresaber/ScoreSaberScore.java create mode 100644 API/src/main/java/cc/fascinated/model/score/impl/scoresaber/ScoreSaberScoreResponse.java delete mode 100644 API/src/main/java/cc/fascinated/repository/couchdb/TrackedScoreRepository.java create mode 100644 API/src/main/java/cc/fascinated/repository/mongo/CounterRepository.java create mode 100644 API/src/main/java/cc/fascinated/repository/mongo/ScoreRepository.java create mode 100644 API/src/main/java/cc/fascinated/services/CounterService.java create mode 100644 API/src/main/java/cc/fascinated/services/ScoreService.java delete mode 100644 API/src/main/java/cc/fascinated/services/TrackedScoreService.java diff --git a/API/src/main/java/cc/fascinated/Main.java b/API/src/main/java/cc/fascinated/Main.java index e074458..f36cd55 100644 --- a/API/src/main/java/cc/fascinated/Main.java +++ b/API/src/main/java/cc/fascinated/Main.java @@ -22,7 +22,7 @@ import java.util.Objects; @EnableMongoRepositories(basePackages = "cc.fascinated.repository.mongo") @EnableScheduling @SpringBootApplication(exclude = UserDetailsServiceAutoConfiguration.class) -@Log4j2(topic = "Ember") +@Log4j2(topic = "Score Tracker") public class Main { @SneakyThrows public static void main(@NonNull String[] args) { diff --git a/API/src/main/java/cc/fascinated/common/EnumUtils.java b/API/src/main/java/cc/fascinated/common/EnumUtils.java new file mode 100644 index 0000000..3f47a51 --- /dev/null +++ b/API/src/main/java/cc/fascinated/common/EnumUtils.java @@ -0,0 +1,24 @@ +package cc.fascinated.common; + +import lombok.experimental.UtilityClass; + +/** + * @author Fascinated (fascinated7) + */ +@UtilityClass +public class EnumUtils { + /** + * Gets the name of the enum + * + * @param e the enum + * @return the name + */ + public static String getEnumName(Enum e) { + String[] split = e.name().split("_"); + StringBuilder builder = new StringBuilder(); + for (String s : split) { + builder.append(s.substring(0, 1).toUpperCase()).append(s.substring(1).toLowerCase()).append(" "); + } + return builder.toString().trim(); + } +} diff --git a/API/src/main/java/cc/fascinated/controller/ScoresController.java b/API/src/main/java/cc/fascinated/controller/ScoresController.java index bcbc6ae..b7486df 100644 --- a/API/src/main/java/cc/fascinated/controller/ScoresController.java +++ b/API/src/main/java/cc/fascinated/controller/ScoresController.java @@ -2,7 +2,7 @@ package cc.fascinated.controller; import cc.fascinated.exception.impl.BadRequestException; import cc.fascinated.platform.Platform; -import cc.fascinated.services.TrackedScoreService; +import cc.fascinated.services.ScoreService; import lombok.NonNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; @@ -20,11 +20,11 @@ public class ScoresController { * The tracked score service to use. */ @NonNull - private final TrackedScoreService trackedScoreService; + private final ScoreService scoreService; @Autowired - public ScoresController(@NonNull TrackedScoreService trackedScoreService) { - this.trackedScoreService = trackedScoreService; + public ScoresController(@NonNull ScoreService scoreService) { + this.scoreService = scoreService; } /** @@ -37,8 +37,8 @@ public class ScoresController { */ @ResponseBody @GetMapping(value = "/top/{platform}") - public ResponseEntity> getTopScores(@PathVariable String platform) { - return ResponseEntity.ok(trackedScoreService.getTopScores(Platform.Platforms.getPlatform(platform), 50)); + public ResponseEntity> getTopScores(@PathVariable String platform, @RequestParam(defaultValue = "false") boolean scoresonly) { + return ResponseEntity.ok(scoreService.getTopRankedScores(Platform.Platforms.getPlatform(platform), 50, scoresonly)); } /** @@ -52,20 +52,6 @@ public class ScoresController { @ResponseBody @GetMapping(value = "/count/{platform}") public ResponseEntity getScoresCount(@PathVariable String platform) { - return ResponseEntity.ok(trackedScoreService.getTotalScores(Platform.Platforms.getPlatform(platform))); - } - - /** - * A GET mapping to retrieve the total - * amount of scores over pp thresholds - * - * @param platform the platform to get the scores from - * @return the amount of scores - * @throws BadRequestException if there were no scores found - */ - @ResponseBody - @GetMapping(value = "/ppthresholds/{platform}") - public ResponseEntity getScoresOver(@PathVariable String platform) { - return ResponseEntity.ok(trackedScoreService.getScoresOver(Platform.Platforms.getPlatform(platform))); + return ResponseEntity.ok(scoreService.getTotalScores(Platform.Platforms.getPlatform(platform))); } } diff --git a/API/src/main/java/cc/fascinated/model/Counter.java b/API/src/main/java/cc/fascinated/model/Counter.java new file mode 100644 index 0000000..b43e816 --- /dev/null +++ b/API/src/main/java/cc/fascinated/model/Counter.java @@ -0,0 +1,25 @@ +package cc.fascinated.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.springframework.data.annotation.Id; + +/** + * @author Fascinated (fascinated7) + */ +@AllArgsConstructor +@Getter +@Setter +public class Counter { + /** + * The ID of the counter. + */ + @Id + private String id; + + /** + * The next number in the counter. + */ + private long next; +} diff --git a/API/src/main/java/cc/fascinated/model/leaderboard/Leaderboard.java b/API/src/main/java/cc/fascinated/model/leaderboard/Leaderboard.java index 47c6a65..e913c4b 100644 --- a/API/src/main/java/cc/fascinated/model/leaderboard/Leaderboard.java +++ b/API/src/main/java/cc/fascinated/model/leaderboard/Leaderboard.java @@ -46,9 +46,9 @@ public class Leaderboard { */ private double stars; /** - * The cover image for this leaderboard. + * The image of the song for this leaderboard. */ - private String coverImage; + private String image; /** * The difficulty of the song. diff --git a/API/src/main/java/cc/fascinated/model/score/Score.java b/API/src/main/java/cc/fascinated/model/score/Score.java new file mode 100644 index 0000000..b927666 --- /dev/null +++ b/API/src/main/java/cc/fascinated/model/score/Score.java @@ -0,0 +1,105 @@ +package cc.fascinated.model.score; + +import cc.fascinated.platform.Platform; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.index.Indexed; +import org.springframework.data.mongodb.core.mapping.Document; + +import java.util.Date; + +/** + * @author Fascinated (fascinated7) + */ +@AllArgsConstructor +@Getter +@Setter +@Document("scores") +public class Score { + /** + * The ID of the score. + *

+ * This is an internal ID to avoid clashing with other scores. + * This is not the ID of the score on the platform. + *

+ */ + @Id + @JsonIgnore + private final long id; + + /** + * The ID of the player that set the score. + */ + @Indexed @JsonIgnore + private final String playerId; + + /** + * The platform the score was set on. + *

+ * eg: {@link Platform.Platforms#SCORESABER} + *

+ */ + @Indexed + @JsonIgnore + private final Platform.Platforms platform; + + /** + * The ID of the score of the platform it was set on. + */ + @Indexed + @JsonProperty("scoreId") + private final String platformScoreId; + + /** + * The ID of the leaderboard the score was set on. + */ + @Indexed @JsonIgnore + private final String leaderboardId; + + /** + * The rank of the score when it was set. + */ + private int rank; + + /** + * The accuracy of the score in a percentage. + */ + private final double accuracy; + + /** + * The PP of the score. + *

+ * e.g. 500pp + *

+ */ + private double pp; + + /** + * The score of the score. + */ + private final int score; + + /** + * The list of modifiers used in the score. + */ + private final String[] modifiers; + + /** + * The number of misses in the score. + */ + private final int misses; + + /** + * The number of bad cuts in the score. + */ + private final int badCuts; + + /** + * The timestamp of when the score was set. + */ + private final Date timestamp; +} diff --git a/API/src/main/java/cc/fascinated/model/score/ScoreResponse.java b/API/src/main/java/cc/fascinated/model/score/ScoreResponse.java deleted file mode 100644 index 774711e..0000000 --- a/API/src/main/java/cc/fascinated/model/score/ScoreResponse.java +++ /dev/null @@ -1,91 +0,0 @@ -package cc.fascinated.model.score; - -import cc.fascinated.model.leaderboard.Leaderboard; -import cc.fascinated.model.user.ScoreSaberAccount; -import cc.fascinated.model.user.User; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Date; - -/** - * @author Fascinated (fascinated7) - */ -@AllArgsConstructor -@Getter -public class ScoreResponse { - /** - * The ID of the score. - */ - private String scoreId; - - /** - * The PP of the score. - */ - private Double pp; - - /** - * The rank of the score. - */ - private Long rank; - - /** - * The base score of the score. - */ - private Long score; - - /** - * The modified score of the score. - */ - private Long modifiedScore; - - /** - * The weight of the score. - */ - private Double weight; - - /** - * The modifiers of the score. - */ - private String modifiers; - - /** - * The multiplier of the score. - */ - private double multiplier; - - /** - * The number of misses in the score. - */ - private Long missedNotes; - - /** - * The number of bad cuts in the score. - */ - private Long badCuts; - - /** - * The highest combo in the score. - */ - private Long maxCombo; - - /** - * The accuracy of the score. - */ - private Double accuracy; - - /** - * The user who set the score. - */ - private User user; - - /** - * The ID of the leaderboard. - */ - private Leaderboard leaderboard; - - /** - * The timestamp of the score. - */ - private Date timestamp; -} diff --git a/API/src/main/java/cc/fascinated/model/score/ScoresOverResponse.java b/API/src/main/java/cc/fascinated/model/score/ScoresOverResponse.java deleted file mode 100644 index 8db77d0..0000000 --- a/API/src/main/java/cc/fascinated/model/score/ScoresOverResponse.java +++ /dev/null @@ -1,24 +0,0 @@ -package cc.fascinated.model.score; - -import java.util.HashMap; -import java.util.Map; - -/** - * @author Fascinated (fascinated7) - */ -public class ScoresOverResponse { - /** - * Scores over a certain pp threshold. - */ - public Map scoresOver = new HashMap<>(); - - /** - * Adds scores over a certain pp threshold. - * - * @param pp the pp threshold - * @param scoreAmount the amount of scores over the pp threshold - */ - public void addScores(int pp, int scoreAmount) { - scoresOver.put(pp, scoreAmount); - } -} diff --git a/API/src/main/java/cc/fascinated/model/score/TrackedScore.java b/API/src/main/java/cc/fascinated/model/score/TrackedScore.java deleted file mode 100644 index 6aa823d..0000000 --- a/API/src/main/java/cc/fascinated/model/score/TrackedScore.java +++ /dev/null @@ -1,99 +0,0 @@ -package cc.fascinated.model.score; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; -import lombok.Getter; -import lombok.Setter; - -import java.util.Date; - -/** - * @author Fascinated (fascinated7) - */ -@Entity -@Getter -@Setter -@Table(name = "score") -public class TrackedScore { - /** - * The ID of the score. - */ - @Id - private String scoreId; - - /** - * The ID of the player who set the score. - */ - private String playerId; - - /** - * The ID of the leaderboard. - */ - private String leaderboardId; - - /** - * The PP of the score. - */ - private Double pp; - - /** - * The rank of the score. - */ - private Long rank; - - /** - * The base score of the score. - */ - private Long score; - - /** - * The modified score of the score. - */ - private Long modifiedScore; - - /** - * The weight of the score. - */ - private Double weight; - - /** - * The modifiers of the score. - */ - private String modifiers; - - /** - * The multiplier of the score. - */ - private double multiplier; - - /** - * The number of misses in the score. - */ - private Long missedNotes; - - /** - * The number of bad cuts in the score. - */ - private Long badCuts; - - /** - * The highest combo in the score. - */ - private Long maxCombo; - - /** - * The accuracy of the score. - */ - private Double accuracy; - - /** - * The difficulty the score was set on. - */ - private String difficulty; - - /** - * The timestamp of the score. - */ - private Date timestamp; -} 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 new file mode 100644 index 0000000..8905f45 --- /dev/null +++ b/API/src/main/java/cc/fascinated/model/score/impl/scoresaber/ScoreSaberScore.java @@ -0,0 +1,49 @@ +package cc.fascinated.model.score.impl.scoresaber; + +import cc.fascinated.model.score.Score; +import cc.fascinated.platform.Platform; +import lombok.Getter; + +import java.util.Date; + +/** + * @author Fascinated (fascinated7) + */ +@Getter +public class ScoreSaberScore extends Score { + /** + * The weight of the score. + */ + private final double weight; + + /** + * The multiplier of the score. + */ + + private final double multiplier; + /** + * The maximum combo achieved in the score. + */ + private final int maxCombo; + + /** + * Gets the modified score. + * + * @return the modified score + */ + public int getModifiedScore() { + if (multiplier == 1) { + return getScore(); + } + return (int) (getScore() * multiplier); + } + + public ScoreSaberScore(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, Date timestamp, + double weight, double multiplier, int maxCombo) { + super(id, playerId, platform, platformScoreId, leaderboardId, rank, accuracy, pp, score, modifiers, misses, badCuts, 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 new file mode 100644 index 0000000..9ce34f2 --- /dev/null +++ b/API/src/main/java/cc/fascinated/model/score/impl/scoresaber/ScoreSaberScoreResponse.java @@ -0,0 +1,33 @@ +package cc.fascinated.model.score.impl.scoresaber; + +import cc.fascinated.model.leaderboard.Leaderboard; +import cc.fascinated.model.user.User; +import cc.fascinated.platform.Platform; +import lombok.Getter; + +import java.util.Date; + +/** + * @author Fascinated (fascinated7) + */ +@Getter +public class ScoreSaberScoreResponse extends ScoreSaberScore { + /** + * The user that set the score. + */ + private final User user; + + /** + * The leaderboard the score was set on. + */ + 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, Date timestamp, + double weight, double multiplier, int maxCombo, User user, Leaderboard leaderboard) { + super(id, playerId, platform, platformScoreId, leaderboardId, rank, accuracy, pp, score, modifiers, misses, badCuts, + 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 b5dba62..e97f245 100644 --- a/API/src/main/java/cc/fascinated/model/user/ScoreSaberAccount.java +++ b/API/src/main/java/cc/fascinated/model/user/ScoreSaberAccount.java @@ -5,7 +5,6 @@ import cc.fascinated.model.token.ScoreSaberAccountToken; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.AllArgsConstructor; import lombok.Getter; -import org.springframework.data.annotation.Transient; import java.util.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 f67af17..fbfc9bf 100644 --- a/API/src/main/java/cc/fascinated/model/user/User.java +++ b/API/src/main/java/cc/fascinated/model/user/User.java @@ -1,6 +1,7 @@ package cc.fascinated.model.user; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; @@ -23,7 +24,7 @@ public class User { /** * The ID of the user. */ - @Id + @Id @JsonIgnore private final UUID id; /** @@ -38,7 +39,7 @@ public class User { /** * The ID of the users steam profile. */ - @Indexed(unique = true) + @Indexed @JsonProperty("id") private String steamId; /** 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 935d4df..af97705 100644 --- a/API/src/main/java/cc/fascinated/platform/impl/ScoreSaberPlatform.java +++ b/API/src/main/java/cc/fascinated/platform/impl/ScoreSaberPlatform.java @@ -1,8 +1,8 @@ package cc.fascinated.platform.impl; import cc.fascinated.common.MathUtils; +import cc.fascinated.model.score.Score; import cc.fascinated.model.score.TotalScoresResponse; -import cc.fascinated.model.score.TrackedScore; import cc.fascinated.model.token.ScoreSaberAccountToken; import cc.fascinated.model.token.ScoreSaberLeaderboardPageToken; import cc.fascinated.model.token.ScoreSaberLeaderboardToken; @@ -11,12 +11,13 @@ import cc.fascinated.platform.CurvePoint; import cc.fascinated.platform.Platform; import cc.fascinated.services.QuestDBService; import cc.fascinated.services.ScoreSaberService; -import cc.fascinated.services.TrackedScoreService; +import cc.fascinated.services.ScoreService; import cc.fascinated.services.UserService; import io.questdb.client.Sender; import lombok.NonNull; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; import java.util.HashMap; @@ -27,13 +28,9 @@ import java.util.Map; * @author Fascinated (fascinated7) */ @Component +@DependsOn("scoreService") @Log4j2 public class ScoreSaberPlatform extends Platform { - /** - * Delay in ms for requests per minute. - */ - private static final long UPDATE_DELAY = 1000L / 250L; // 150 requests per minute - /** * The base multiplier for stars. */ @@ -57,15 +54,9 @@ public class ScoreSaberPlatform extends Platform { @NonNull private final QuestDBService questDBService; - /** - * The tracked score service to use - */ - @NonNull - private final TrackedScoreService trackedScoreService; - @Autowired public ScoreSaberPlatform(@NonNull ScoreSaberService scoreSaberService, @NonNull UserService userService, @NonNull QuestDBService questDBService, - @NonNull TrackedScoreService trackedScoreService) { + @NonNull ScoreService scoreService) { super(Platforms.SCORESABER, 1, Map.of( 1, new CurvePoint[]{ new CurvePoint(0, 0), @@ -110,7 +101,6 @@ public class ScoreSaberPlatform extends Platform { this.scoreSaberService = scoreSaberService; this.userService = userService; this.questDBService = questDBService; - this.trackedScoreService = trackedScoreService; } /** @@ -134,7 +124,10 @@ public class ScoreSaberPlatform extends Platform { CurvePoint point = curve[i]; CurvePoint nextPoint = curve[i + 1]; if (accuracy >= point.getAcc() && accuracy <= nextPoint.getAcc()) { - return MathUtils.lerp(point.getMultiplier(), nextPoint.getMultiplier(), (accuracy - point.getAcc()) / (nextPoint.getAcc() - point.getAcc())); + return MathUtils.lerp( + point.getMultiplier(), nextPoint.getMultiplier(), + (accuracy - point.getAcc()) / (nextPoint.getAcc() - point.getAcc()) + ); } } @@ -157,7 +150,6 @@ public class ScoreSaberPlatform extends Platform { continue; } ScoreSaberAccountToken account = scoreSaberService.getAccount(user); // Get the account from the ScoreSaber API - try (Sender sender = questDBService.getSender()) { sender.table("player") .symbol("platform", this.getPlatform().getPlatformName()) @@ -178,7 +170,7 @@ public class ScoreSaberPlatform extends Platform { @Override public void updateMetrics() { try (Sender sender = questDBService.getSender()) { - TotalScoresResponse totalScores = trackedScoreService.getTotalScores(this.getPlatform()); + TotalScoresResponse totalScores = ScoreService.INSTANCE.getTotalScores(this.getPlatform()); sender.table("metrics") .symbol("platform", this.getPlatform().getPlatformName()) .longColumn("total_scores", totalScores.getTotalScores()) @@ -189,8 +181,7 @@ public class ScoreSaberPlatform extends Platform { @Override public void updateLeaderboards() { - // TODO: PUSH THIS - List scores = this.trackedScoreService.getTrackedScores(this.getPlatform(), true); + List scores = ScoreService.INSTANCE.getRankedScores(this.getPlatform()); Map leaderboards = new HashMap<>(); for (ScoreSaberLeaderboardPageToken rankedLeaderboard : this.scoreSaberService.getRankedLeaderboards()) { for (ScoreSaberLeaderboardToken leaderboard : rankedLeaderboard.getLeaderboards()) { @@ -199,7 +190,7 @@ public class ScoreSaberPlatform extends Platform { } // Add any missing leaderboards - for (TrackedScore score : scores) { + for (Score score : scores) { if (leaderboards.containsKey(score.getLeaderboardId())) { continue; } @@ -218,26 +209,16 @@ public class ScoreSaberPlatform extends Platform { for (Map.Entry leaderboardEntry : leaderboards.entrySet()) { String id = leaderboardEntry.getKey(); ScoreSaberLeaderboardToken leaderboard = leaderboardEntry.getValue(); - - if (finished > 0) { - // Sleep to prevent rate limiting - try { - Thread.sleep(UPDATE_DELAY); - } catch (InterruptedException e) { - log.error("Failed to sleep for rate limit reset", e); - } - } - try { - List toUpdate = scores.stream().filter(score -> { + List toUpdate = scores.stream().filter(score -> { if (!score.getLeaderboardId().equals(id)) { // Check if the leaderboard ID matches return false; } double pp = this.getPp(leaderboard.getStars(), score.getAccuracy()); - return pp != (score.getPp() == null ? 0D : score.getPp()); // Check if the pp has changed + return pp != score.getPp(); // Check if the pp has changed }).toList(); - for (TrackedScore score : toUpdate) { // Update the scores + for (Score score : toUpdate) { // Update the scores if (leaderboard.getStars() == 0) { // The leaderboard was unranked score.setPp(0D); } @@ -246,7 +227,7 @@ public class ScoreSaberPlatform extends Platform { } if (!toUpdate.isEmpty()) { // Save the scores - this.trackedScoreService.updateScores(toUpdate.toArray(TrackedScore[]::new)); + ScoreService.INSTANCE.updateScores(toUpdate.toArray(new Score[0])); } finished++; diff --git a/API/src/main/java/cc/fascinated/repository/couchdb/TrackedScoreRepository.java b/API/src/main/java/cc/fascinated/repository/couchdb/TrackedScoreRepository.java deleted file mode 100644 index 26f7aac..0000000 --- a/API/src/main/java/cc/fascinated/repository/couchdb/TrackedScoreRepository.java +++ /dev/null @@ -1,90 +0,0 @@ -package cc.fascinated.repository.couchdb; - -import cc.fascinated.model.score.TrackedScore; -import jakarta.transaction.Transactional; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.query.Param; - -import java.util.List; - -/** - * @author Fascinated (fascinated7) - */ -public interface TrackedScoreRepository extends CrudRepository { - /** - * Ensures that the deduplication of the scores is done. - */ - @Modifying @Transactional - @Query(value = "ALTER TABLE score DEDUP ENABLE UPSERT KEYS(timestamp, score_id)", nativeQuery = true) - void ensureDeduplication(); - - /** - * Updates the pp of a score. - * - * @param scoreId the ID of the score - * @param pp the new pp of the score - */ - @Modifying @Transactional - @Query(value = "UPDATE score SET pp = :pp WHERE score_id = :scoreId", nativeQuery = true) - void updateScorePp(@Param("scoreId") String scoreId, @Param("pp") double pp); - - /** - * Gets a list of top tracked scores - * sorted by pp from the platform - * - * @param platform the platform to get the scores from - * @param amount the amount of scores to get - * @return the scores - */ - @Query(value = "SELECT * FROM score WHERE platform = :platform AND pp > 0 ORDER BY pp DESC LIMIT :amount", nativeQuery = true) - List findTopRankedScores(@Param("platform") String platform, @Param("amount") int amount); - - /** - * Gets all tracked scores from a platform. - * - * @param platform the platform to get the scores from - * @return the scores - */ - @Query(value = "SELECT * FROM score WHERE platform = :platform", nativeQuery = true) - List findAllByPlatform(String platform); - - /** - * Gets all tracked scores from a platform. - * - * @param platform the platform to get the scores from - * @return the scores - */ - @Query(value = "SELECT * FROM score WHERE platform = :platform AND pp > 0", nativeQuery = true) - List findAllByPlatformRankedOnly(String platform); - - /** - * Gets the total amount of scores - * for a platform. - * - * @param platform the platform to get the scores from - * @return the total amount of scores for the platform - */ - @Query(value = "SELECT COUNT(*) FROM score WHERE platform = :platform", nativeQuery = true) - int countTotalScores(@Param("platform") String platform); - - /** - * Gets the total amount of ranked scores - * for a platform. - * - * @param platform the platform to get the scores from - * @return the total amount of ranked scores for the platform - */ - @Query(value = "SELECT COUNT(*) FROM score WHERE platform = :platform AND pp > 0", nativeQuery = true) - int countTotalRankedScores(@Param("platform") String platform); - - /** - * Gets all scores for a platform. - * - * @param platform the platform to get the scores from - * @return the scores - */ - @Query(value = "SELECT COUNT(*) FROM score WHERE platform = :platform AND pp > :pp", nativeQuery = true) - int getScoreCountOverPpThreshold(@Param("platform") String platform, @Param("pp") double pp); -} \ No newline at end of file diff --git a/API/src/main/java/cc/fascinated/repository/mongo/CounterRepository.java b/API/src/main/java/cc/fascinated/repository/mongo/CounterRepository.java new file mode 100644 index 0000000..7888f40 --- /dev/null +++ b/API/src/main/java/cc/fascinated/repository/mongo/CounterRepository.java @@ -0,0 +1,9 @@ +package cc.fascinated.repository.mongo; + +import cc.fascinated.model.Counter; +import org.springframework.data.mongodb.repository.MongoRepository; + +/** + * @author Fascinated (fascinated7) + */ +public interface CounterRepository extends MongoRepository {} \ No newline at end of file diff --git a/API/src/main/java/cc/fascinated/repository/mongo/ScoreRepository.java b/API/src/main/java/cc/fascinated/repository/mongo/ScoreRepository.java new file mode 100644 index 0000000..48532fa --- /dev/null +++ b/API/src/main/java/cc/fascinated/repository/mongo/ScoreRepository.java @@ -0,0 +1,78 @@ +package cc.fascinated.repository.mongo; + +import cc.fascinated.model.score.Score; +import cc.fascinated.platform.Platform; +import lombok.NonNull; +import org.springframework.data.mongodb.repository.Aggregation; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +/** + * @author Fascinated (fascinated7) + */ +public interface ScoreRepository extends MongoRepository { + + /** + * Gets the top ranked scores from the platform. + * + * @param platform the platform to get the scores from + * @param amount the amount of scores to get + * @return the scores + */ + @Aggregation(pipeline = { + "{ $match: { platform: ?0, pp: { $gt: 0 } } }", + "{ $sort: { pp: -1 } }", + "{ $limit: ?1 }" + }) + List getTopRankedScores(@NonNull Platform.Platforms platform, int amount); + + /** + * Gets all the ranked scores from the platform. + * + * @param platform the platform to get the scores from + * @return the scores + */ + @Aggregation(pipeline = { + "{ $match: { platform: ?0, pp: { $gt: 0 } } }", + "{ $sort: { pp: -1 } }" + }) + List getRankedScores(@NonNull Platform.Platforms platform); + + /** + * Updates a scores pp value. + * + * @param id The id of the score to update + * @param pp The new pp value + */ + @Aggregation(pipeline = { + "{ $match: { _id: ?0 } }", + "{ $set: { pp: ?1 } }" + }) + void updateScorePP(@Param("id") long id, @Param("pp") double pp); + + /** + * Gets the total scores for the platform. + * + * @param platform the platform to get the scores from + * @return the total scores + */ + @Aggregation(pipeline = { + "{ $match: { platform: ?0 } }", + "{ $count: 'total' }" + }) + int getTotalScores(@NonNull Platform.Platforms platform); + + /** + * Gets the total ranked scores for the platform. + * + * @param platform the platform to get the scores from + * @return the total ranked scores + */ + @Aggregation(pipeline = { + "{ $match: { platform: ?0, pp: { $gt: 0 } } }", + "{ $count: 'total' }" + }) + int getTotalRankedScores(@NonNull Platform.Platforms platform); +} diff --git a/API/src/main/java/cc/fascinated/services/CounterService.java b/API/src/main/java/cc/fascinated/services/CounterService.java new file mode 100644 index 0000000..2019939 --- /dev/null +++ b/API/src/main/java/cc/fascinated/services/CounterService.java @@ -0,0 +1,54 @@ +package cc.fascinated.services; + +import cc.fascinated.model.Counter; +import cc.fascinated.repository.mongo.CounterRepository; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +/** + * @author Fascinated (fascinated7) + */ +@Service +public class CounterService { + /** + * The counter repository to use. + */ + private final CounterRepository counterRepository; + + @Autowired + public CounterService(CounterRepository counterRepository) { + this.counterRepository = counterRepository; + } + + /** + * Gets the next number in the sequence. + * + * @param type The type of the counter. + * @return The next number in the sequence. + */ + public long getNext(CounterType type) { + Optional counterOptional = counterRepository.findById(type.getId()); + Counter counter = counterOptional.orElseGet(() -> new Counter(type.getId(), 1)); + + long current = counter.getNext(); + counter.setNext(current + 1); + + counterRepository.save(counter); + return current; + } + + @AllArgsConstructor + @Getter + public enum CounterType { + SCORE("score"); + + /** + * The ID of the counter. + */ + private final String id; + } +} \ No newline at end of file diff --git a/API/src/main/java/cc/fascinated/services/PlatformService.java b/API/src/main/java/cc/fascinated/services/PlatformService.java index 965acb5..55c38df 100644 --- a/API/src/main/java/cc/fascinated/services/PlatformService.java +++ b/API/src/main/java/cc/fascinated/services/PlatformService.java @@ -8,6 +8,7 @@ import lombok.extern.log4j.Log4j2; import org.bson.Document; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.DependsOn; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -20,8 +21,10 @@ import java.util.concurrent.Executors; * @author Fascinated (fascinated7) */ @Service +@DependsOn("mongoService") @Log4j2(topic = "PlatformService") public class PlatformService { + public static PlatformService INSTANCE; private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(1); /** @@ -31,6 +34,7 @@ public class PlatformService { @Autowired public PlatformService(@NonNull ApplicationContext context) { + INSTANCE = this; log.info("Registering platforms..."); registerPlatform(context.getBean(ScoreSaberPlatform.class)); log.info("Loaded %s platforms.".formatted(this.platforms.size())); diff --git a/API/src/main/java/cc/fascinated/services/ScoreService.java b/API/src/main/java/cc/fascinated/services/ScoreService.java new file mode 100644 index 0000000..bc8258f --- /dev/null +++ b/API/src/main/java/cc/fascinated/services/ScoreService.java @@ -0,0 +1,196 @@ +package cc.fascinated.services; + +import cc.fascinated.common.DateUtils; +import cc.fascinated.common.EnumUtils; +import cc.fascinated.common.MathUtils; +import cc.fascinated.model.leaderboard.Leaderboard; +import cc.fascinated.model.score.Score; +import cc.fascinated.model.score.TotalScoresResponse; +import cc.fascinated.model.score.impl.scoresaber.ScoreSaberScore; +import cc.fascinated.model.score.impl.scoresaber.ScoreSaberScoreResponse; +import cc.fascinated.model.token.ScoreSaberLeaderboardToken; +import cc.fascinated.model.token.ScoreSaberPlayerScoreToken; +import cc.fascinated.model.token.ScoreSaberScoreToken; +import cc.fascinated.model.user.User; +import cc.fascinated.platform.Platform; +import cc.fascinated.repository.mongo.ScoreRepository; +import lombok.NonNull; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Fascinated (fascinated7) + */ +@Service +@Log4j2(topic = "Score Service") +public class ScoreService { + public static ScoreService INSTANCE; + + /** + * The counter service to use. + */ + @NonNull + private final CounterService counterService; + + /** + * The user service to use. + */ + @NonNull + private final UserService userService; + + /** + * The ScoreSaber service to use. + */ + @NonNull + private final ScoreSaberService scoreSaberService; + + /** + * The score repository to use. + */ + @NonNull + private final ScoreRepository scoreRepository; + + @Autowired + public ScoreService(@NonNull CounterService counterService, @NonNull UserService userService, @NonNull ScoreSaberService scoreSaberService, + @NonNull ScoreRepository scoreRepository) { + INSTANCE = this; + this.counterService = counterService; + this.userService = userService; + this.scoreSaberService = scoreSaberService; + this.scoreRepository = scoreRepository; + } + + /** + * Gets the top ranked scores from the platform. + * + * @param platform The platform to get the scores from. + * @param amount The amount of scores to get. + * @param scoresOnly Whether to only get the scores. + * @return The scores. + */ + public List getTopRankedScores(@NonNull Platform.Platforms platform, int amount, boolean scoresOnly) { + List trackedScores = scoreRepository.getTopRankedScores(platform, amount); + List scores = new ArrayList<>(); + for (Score score : trackedScores) { + ScoreSaberScore scoreSaberScore = (ScoreSaberScore) score; + User user = scoresOnly ? null : userService.getUser(score.getPlayerId()); + Leaderboard leaderboard = scoresOnly ? null : Leaderboard.getFromScoreSaberToken(scoreSaberService.getLeaderboard(score.getLeaderboardId())); + + scores.add(new ScoreSaberScoreResponse( + score.getId(), + score.getPlayerId(), + score.getPlatform(), + score.getPlatformScoreId(), + score.getLeaderboardId(), + score.getRank(), + score.getAccuracy(), + score.getPp(), + score.getScore(), + score.getModifiers(), + score.getMisses(), + score.getBadCuts(), + score.getTimestamp(), + scoreSaberScore.getWeight(), + scoreSaberScore.getMultiplier(), + scoreSaberScore.getMaxCombo(), + user, + leaderboard + )); + } + + return scores; + } + + /** + * Gets all the ranked scores from the platform. + * + * @param platform The platform to get the scores from. + * @return The scores. + */ + public List getRankedScores(@NonNull Platform.Platforms platform) { + return scoreRepository.getRankedScores(platform); + } + + /** + * Updates a scores pp value. + * + * @param scores The scores to update. + */ + public void updateScores(Score... scores) { + for (Score score : scores) { + scoreRepository.updateScorePP(score.getId(), score.getPp()); + } + } + + public TotalScoresResponse getTotalScores(Platform.Platforms platform) { + return new TotalScoresResponse( + scoreRepository.getTotalScores(platform), + scoreRepository.getTotalRankedScores(platform) + ); + } + + /** + * Tracks a ScoreSaber score. + * + * @param token The token of the score to track. + */ + public void trackScoreSaberScore(ScoreSaberPlayerScoreToken token) { + ScoreSaberLeaderboardToken leaderboard = this.scoreSaberService.getLeaderboard(token.getLeaderboard().getId()); + ScoreSaberScoreToken score = token.getScore(); + User user = userService.getUser(score.getLeaderboardPlayerInfo().getId()); + + double accuracy = leaderboard.getMaxScore() != 0 ? ((double) score.getBaseScore() / leaderboard.getMaxScore()) * 100 : 0; + if (accuracy == 0) { + log.warn("[Scoresaber] Leaderboard '{}' has a max score of 0, unable to calculate accuracy :(", leaderboard.getId()); + } + double pp = score.getPp() != 0 ? PlatformService.INSTANCE.getScoreSaberPlatform().getPp(leaderboard.getStars(), accuracy) : 0; // Recalculate the pp + String[] modifiers = !score.getModifiers().isEmpty() ? score.getModifiers().split(",") : new String[0]; + + ScoreSaberScore scoreSaberScore = new ScoreSaberScore( + counterService.getNext(CounterService.CounterType.SCORE), + user.getSteamId(), + Platform.Platforms.SCORESABER, + score.getId(), + leaderboard.getId(), + score.getRank(), + accuracy, + pp, + score.getBaseScore(), + modifiers, + score.getMissedNotes(), + score.getBadCuts(), + DateUtils.getDateFromString(score.getTimeSet()), + score.getWeight(), + score.getMultiplier(), + score.getMaxCombo() + ); + scoreRepository.save(scoreSaberScore); + this.logScore(Platform.Platforms.SCORESABER, scoreSaberScore, user); + } + + /** + * Logs a score. + * + * @param platform The platform the score was tracked on. + * @param score The score to log. + * @param user The user who set the score. + */ + private void logScore(@NonNull Platform.Platforms platform, @NonNull Score score, @NonNull User user) { + String platformName = EnumUtils.getEnumName(platform); + boolean isRanked = score.getPp() != 0; + log.info("[{}] Tracked{} Score! id: {}, acc: {}%, {} score id: {},{} leaderboard: {}, player: {}", + platformName, + isRanked ? " Ranked" : "", + score.getId(), + MathUtils.format(score.getAccuracy(), 2), + platformName.toLowerCase(), score.getPlatformScoreId(), + isRanked ? " pp: %s,".formatted(score.getPp()) : "", + score.getLeaderboardId(), + user.getUsername() == null ? user.getSteamId() : user.getUsername() + ); + } +} diff --git a/API/src/main/java/cc/fascinated/services/TrackedScoreService.java b/API/src/main/java/cc/fascinated/services/TrackedScoreService.java deleted file mode 100644 index e52330c..0000000 --- a/API/src/main/java/cc/fascinated/services/TrackedScoreService.java +++ /dev/null @@ -1,165 +0,0 @@ -package cc.fascinated.services; - -import cc.fascinated.exception.impl.BadRequestException; -import cc.fascinated.model.leaderboard.Leaderboard; -import cc.fascinated.model.score.ScoreResponse; -import cc.fascinated.model.score.ScoresOverResponse; -import cc.fascinated.model.score.TotalScoresResponse; -import cc.fascinated.model.score.TrackedScore; -import cc.fascinated.model.user.User; -import cc.fascinated.platform.Platform; -import cc.fascinated.repository.couchdb.TrackedScoreRepository; -import lombok.NonNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.LinkedList; -import java.util.List; - -/** - * @author Fascinated (fascinated7) - */ -@Service -public class TrackedScoreService { - /** - * The scores over thresholds. - */ - private static final int[] SCORES_OVER = {1000, 900, 800, 700, 600, 500, 400, 300, 200, 100}; - - /** - * The tracked score repository to use. - */ - @NonNull - private final TrackedScoreRepository trackedScoreRepository; - - /** - * The user service to use. - */ - @NonNull - private final UserService userService; - - /** - * The ScoreSaber service to use. - */ - @NonNull - private final ScoreSaberService scoreSaberService; - - @Autowired - public TrackedScoreService(@NonNull TrackedScoreRepository trackedScoreRepository, @NonNull UserService userService, - @NonNull ScoreSaberService scoreSaberService) { - this.trackedScoreRepository = trackedScoreRepository; - this.userService = userService; - this.scoreSaberService = scoreSaberService; - this.trackedScoreRepository.ensureDeduplication(); - } - - /** - * Gets a list of top tracked scores - * sorted by pp from the platform - * - * @param platform the platform to get the scores from - * @param amount the amount of scores to get - * @return the scores - */ - public List getTopScores(Platform.Platforms platform, int amount) { - List foundScores = trackedScoreRepository.findTopRankedScores(platform.getPlatformName(), amount); - if (foundScores.isEmpty()) { - throw new BadRequestException("No scores found for platform " + platform.getPlatformName()); - } - - List scores = new LinkedList<>(); - for (TrackedScore trackedScore : foundScores) { - User user = this.userService.getUser(trackedScore.getPlayerId()); - Leaderboard leaderboard = null; - switch (platform) { - case SCORESABER -> leaderboard = Leaderboard.getFromScoreSaberToken(this.scoreSaberService.getLeaderboard(trackedScore.getLeaderboardId())); - } - assert leaderboard != null; // This should never be null - - scores.add(new ScoreResponse( - trackedScore.getScoreId(), - trackedScore.getPp(), - trackedScore.getRank(), - trackedScore.getScore(), - trackedScore.getModifiedScore(), - trackedScore.getWeight(), - trackedScore.getModifiers(), - trackedScore.getMultiplier(), - trackedScore.getMissedNotes(), - trackedScore.getBadCuts(), - trackedScore.getMaxCombo(), - trackedScore.getAccuracy(), - user, - leaderboard, - trackedScore.getTimestamp() - )); - } - - return scores; - } - - /** - * Gets the amount of scores over pp thresholds. - * - * @param platform the platform to get the scores from - * @return the scores over pp thresholds - */ - public ScoresOverResponse getScoresOver(Platform.Platforms platform) { - ScoresOverResponse scoresOverResponse = new ScoresOverResponse(); - - for (int i : SCORES_OVER) { - scoresOverResponse.addScores(i, trackedScoreRepository.getScoreCountOverPpThreshold(platform.getPlatformName(), i)); - } - return scoresOverResponse; - } - - /** - * Gets the total amount of scores - * for a platform. - * - * @param platform the platform to get the scores from - * @return the total amount of scores for the platform - */ - public TotalScoresResponse getTotalScores(Platform.Platforms platform) { - return new TotalScoresResponse( - trackedScoreRepository.countTotalScores(platform.getPlatformName()), - trackedScoreRepository.countTotalRankedScores(platform.getPlatformName()) - ); - } - - /** - * Gets a list of tracked scores - * for a platform. - * - * @param platform the platform to get the scores from - * @return the tracked scores - */ - public List getTrackedScores(Platform.Platforms platform, boolean onlyRanked) { - if (onlyRanked) { - return trackedScoreRepository.findAllByPlatformRankedOnly(platform.getPlatformName()); - } - return trackedScoreRepository.findAllByPlatform(platform.getPlatformName()); - } - - /** - * Saves a list of tracked scores. - * - * @param scores the scores to save - */ - public void updateScores(TrackedScore... scores) { - for (TrackedScore score : scores) { - this.trackedScoreRepository.updateScorePp(score.getScoreId(), score.getPp()); - } - } - - /** - * Deletes a list of tracked scores. - * - * @param scores the scores to delete - */ - public void deleteScores(TrackedScore... scores) { - for (TrackedScore score : scores) { - this.trackedScoreRepository.delete(score); - } - } -} diff --git a/API/src/main/java/cc/fascinated/services/UserService.java b/API/src/main/java/cc/fascinated/services/UserService.java index a14b3e4..d8f7921 100644 --- a/API/src/main/java/cc/fascinated/services/UserService.java +++ b/API/src/main/java/cc/fascinated/services/UserService.java @@ -74,7 +74,8 @@ public class UserService { scoresaberAccount == null ? "now" : TimeUtils.format(System.currentTimeMillis() - scoresaberAccount.getLastUpdated().getTime()) ); ScoreSaberAccountToken accountToken = scoreSaberService.getAccount(user); - user.setScoresaberAccount(ScoreSaberAccount.getFromToken(accountToken)); + user.setScoresaberAccount(ScoreSaberAccount.getFromToken(accountToken)); // Update the ScoreSaber account + user.setUsername(accountToken.getName()); // Update the username this.saveUser(user); // Save the user } diff --git a/API/src/main/java/cc/fascinated/websocket/impl/ScoreSaberWebsocket.java b/API/src/main/java/cc/fascinated/websocket/impl/ScoreSaberWebsocket.java index e4a3a72..f23697e 100644 --- a/API/src/main/java/cc/fascinated/websocket/impl/ScoreSaberWebsocket.java +++ b/API/src/main/java/cc/fascinated/websocket/impl/ScoreSaberWebsocket.java @@ -1,17 +1,12 @@ package cc.fascinated.websocket.impl; -import cc.fascinated.common.ScoreSaberUtils; -import cc.fascinated.model.token.ScoreSaberLeaderboardToken; import cc.fascinated.model.token.ScoreSaberPlayerScoreToken; import cc.fascinated.model.token.ScoreSaberScoreToken; import cc.fascinated.model.token.ScoreSaberWebsocketDataToken; -import cc.fascinated.platform.Platform; -import cc.fascinated.services.PlatformService; -import cc.fascinated.services.QuestDBService; +import cc.fascinated.services.ScoreService; import cc.fascinated.services.UserService; import cc.fascinated.websocket.Websocket; import com.fasterxml.jackson.databind.ObjectMapper; -import io.questdb.client.Sender; import lombok.NonNull; import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; @@ -36,23 +31,16 @@ public class ScoreSaberWebsocket extends Websocket { private final UserService userService; /** - * The Influx service to use + * The score service to use */ - private final QuestDBService questDBService; - - /** - * The platform service to use - */ - private final PlatformService platformService; + private final ScoreService scoreService; @Autowired - public ScoreSaberWebsocket(@NonNull ObjectMapper objectMapper, @NonNull UserService userService, @NonNull QuestDBService questDBService, - @NonNull PlatformService platformService) { + public ScoreSaberWebsocket(@NonNull ObjectMapper objectMapper, @NonNull UserService userService, @NonNull ScoreService scoreService) { super("ScoreSaber", "wss://scoresaber.com/ws"); this.objectMapper = objectMapper; this.userService = userService; - this.questDBService = questDBService; - this.platformService = platformService; + this.scoreService = scoreService; } @Override @@ -71,42 +59,12 @@ public class ScoreSaberWebsocket extends Websocket { // Decode the message using Jackson ScoreSaberPlayerScoreToken scoreToken = this.objectMapper.readValue(response.getCommandData().toString(), ScoreSaberPlayerScoreToken.class); ScoreSaberScoreToken score = scoreToken.getScore(); - ScoreSaberLeaderboardToken leaderboard = scoreToken.getLeaderboard(); ScoreSaberScoreToken.LeaderboardPlayerInfo player = score.getLeaderboardPlayerInfo(); // Ensure the player is valid if (!this.userService.isValidSteamId(player.getId())) { return; } - - userService.getUser(player.getId()); - double accuracy = ((double) score.getBaseScore() / leaderboard.getMaxScore()) * 100; - String difficulty = ScoreSaberUtils.parseDifficulty(leaderboard.getDifficulty().getDifficulty()); - double pp = platformService.getScoreSaberPlatform().getPp(leaderboard.getStars(), accuracy); // Recalculate the PP - - try (Sender sender = questDBService.getSender()) { - sender.table("score") - .symbol("platform", Platform.Platforms.SCORESABER.getPlatformName()) - // Player information - .symbol("player_id", player.getId()) - // Score information - .symbol("leaderboard_id", leaderboard.getId()) - .symbol("score_id", score.getId()) - .doubleColumn("pp", pp) - .longColumn("rank", score.getRank()) - .longColumn("score", score.getBaseScore()) - .longColumn("modified_score", score.getModifiedScore()) - .doubleColumn("weight", score.getWeight()) - .stringColumn("modifiers", score.getModifiers()) - .doubleColumn("multiplier", score.getMultiplier()) - .longColumn("missed_notes", score.getMissedNotes()) - .longColumn("bad_cuts", score.getBadCuts()) - .longColumn("max_combo", score.getMaxCombo()) - .doubleColumn("accuracy", accuracy) - .stringColumn("difficulty", difficulty) - .atNow(); - } - log.info("Tracked score for {} with a score of {} and {}pp on {} with a rank of {}", - player.getId(), score.getBaseScore(), score.getPp(), leaderboard.getId(), score.getRank()); + scoreService.trackScoreSaberScore(scoreToken); } }