API: switch to Mongo for score tracking
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Failing after 39s
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Failing after 39s
This commit is contained in:
parent
f68fb48726
commit
8dfdc8c535
@ -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) {
|
||||
|
24
API/src/main/java/cc/fascinated/common/EnumUtils.java
Normal file
24
API/src/main/java/cc/fascinated/common/EnumUtils.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
@ -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<List<?>> getTopScores(@PathVariable String platform) {
|
||||
return ResponseEntity.ok(trackedScoreService.getTopScores(Platform.Platforms.getPlatform(platform), 50));
|
||||
public ResponseEntity<List<?>> 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)));
|
||||
}
|
||||
}
|
||||
|
25
API/src/main/java/cc/fascinated/model/Counter.java
Normal file
25
API/src/main/java/cc/fascinated/model/Counter.java
Normal file
@ -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;
|
||||
}
|
@ -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.
|
||||
|
105
API/src/main/java/cc/fascinated/model/score/Score.java
Normal file
105
API/src/main/java/cc/fascinated/model/score/Score.java
Normal file
@ -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.
|
||||
* <p>
|
||||
* This is an internal ID to avoid clashing with other scores.
|
||||
* This is not the ID of the score on the platform.
|
||||
* </p>
|
||||
*/
|
||||
@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.
|
||||
* <p>
|
||||
* eg: {@link Platform.Platforms#SCORESABER}
|
||||
* </p>
|
||||
*/
|
||||
@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.
|
||||
* <p>
|
||||
* e.g. 500pp
|
||||
* </p>
|
||||
*/
|
||||
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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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<Integer, Integer> 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
33
API/src/main/java/cc/fascinated/model/score/impl/scoresaber/ScoreSaberScoreResponse.java
Normal file
33
API/src/main/java/cc/fascinated/model/score/impl/scoresaber/ScoreSaberScoreResponse.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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<TrackedScore> scores = this.trackedScoreService.getTrackedScores(this.getPlatform(), true);
|
||||
List<Score> scores = ScoreService.INSTANCE.getRankedScores(this.getPlatform());
|
||||
Map<String, ScoreSaberLeaderboardToken> 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<String, ScoreSaberLeaderboardToken> 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<TrackedScore> toUpdate = scores.stream().filter(score -> {
|
||||
List<Score> 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++;
|
||||
|
||||
|
@ -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<TrackedScore, String> {
|
||||
/**
|
||||
* 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<TrackedScore> 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<TrackedScore> 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<TrackedScore> 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);
|
||||
}
|
@ -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<Counter, String> {}
|
@ -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<Score, String> {
|
||||
|
||||
/**
|
||||
* 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<Score> 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<Score> 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);
|
||||
}
|
54
API/src/main/java/cc/fascinated/services/CounterService.java
Normal file
54
API/src/main/java/cc/fascinated/services/CounterService.java
Normal file
@ -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<Counter> 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;
|
||||
}
|
||||
}
|
@ -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()));
|
||||
|
196
API/src/main/java/cc/fascinated/services/ScoreService.java
Normal file
196
API/src/main/java/cc/fascinated/services/ScoreService.java
Normal file
@ -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<ScoreSaberScoreResponse> getTopRankedScores(@NonNull Platform.Platforms platform, int amount, boolean scoresOnly) {
|
||||
List<Score> trackedScores = scoreRepository.getTopRankedScores(platform, amount);
|
||||
List<ScoreSaberScoreResponse> 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<Score> 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()
|
||||
);
|
||||
}
|
||||
}
|
@ -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<ScoreResponse> getTopScores(Platform.Platforms platform, int amount) {
|
||||
List<TrackedScore> foundScores = trackedScoreRepository.findTopRankedScores(platform.getPlatformName(), amount);
|
||||
if (foundScores.isEmpty()) {
|
||||
throw new BadRequestException("No scores found for platform " + platform.getPlatformName());
|
||||
}
|
||||
|
||||
List<ScoreResponse> 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<TrackedScore> 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user