improve score fetching and add a curve version to scores
All checks were successful
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m24s
All checks were successful
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m24s
This commit is contained in:
parent
e5fbb3d44c
commit
f5b8aa82c7
@ -1,5 +1,8 @@
|
|||||||
package cc.fascinated.backend.common;
|
package cc.fascinated.backend.common;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
public class MathUtils {
|
public class MathUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package cc.fascinated.backend.common;
|
package cc.fascinated.backend.common;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
public class Timer {
|
public class Timer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,6 +10,11 @@ public class Leaderboard {
|
|||||||
*/
|
*/
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version of the leaderboard.
|
||||||
|
*/
|
||||||
|
private int curveVersion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The curve of the leaderboard.
|
* The curve of the leaderboard.
|
||||||
*/
|
*/
|
||||||
|
@ -24,7 +24,7 @@ public class ScoreSaberLeaderboard extends Leaderboard {
|
|||||||
private final double weightCoefficient = 0.965;
|
private final double weightCoefficient = 0.965;
|
||||||
|
|
||||||
public ScoreSaberLeaderboard() {
|
public ScoreSaberLeaderboard() {
|
||||||
super("ScoreSaber", new LeaderboardCurvePoint[] {
|
super("ScoreSaber", 1, new LeaderboardCurvePoint[] {
|
||||||
new LeaderboardCurvePoint(1.0, 5.367394282890631),
|
new LeaderboardCurvePoint(1.0, 5.367394282890631),
|
||||||
new LeaderboardCurvePoint(0.9995, 5.019543595874787),
|
new LeaderboardCurvePoint(0.9995, 5.019543595874787),
|
||||||
new LeaderboardCurvePoint(0.999, 4.715470646416203),
|
new LeaderboardCurvePoint(0.999, 4.715470646416203),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package cc.fascinated.backend.model.score;
|
package cc.fascinated.backend.model.score;
|
||||||
|
|
||||||
import cc.fascinated.backend.common.DateUtils;
|
import cc.fascinated.backend.common.DateUtils;
|
||||||
|
import cc.fascinated.backend.leaderboard.impl.ScoreSaberLeaderboard;
|
||||||
import cc.fascinated.backend.model.token.ScoreSaberLeaderboardToken;
|
import cc.fascinated.backend.model.token.ScoreSaberLeaderboardToken;
|
||||||
import cc.fascinated.backend.model.token.ScoreSaberPlayerScoreToken;
|
import cc.fascinated.backend.model.token.ScoreSaberPlayerScoreToken;
|
||||||
import cc.fascinated.backend.model.token.ScoreSaberScoreToken;
|
import cc.fascinated.backend.model.token.ScoreSaberScoreToken;
|
||||||
@ -8,8 +9,6 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.springframework.data.annotation.Id;
|
import org.springframework.data.annotation.Id;
|
||||||
import org.springframework.data.mongodb.core.index.CompoundIndex;
|
|
||||||
import org.springframework.data.mongodb.core.index.Indexed;
|
|
||||||
import org.springframework.data.mongodb.core.mapping.Document;
|
import org.springframework.data.mongodb.core.mapping.Document;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -20,7 +19,7 @@ import java.util.List;
|
|||||||
* A score for an account.
|
* A score for an account.
|
||||||
*/
|
*/
|
||||||
@Document
|
@Document
|
||||||
@AllArgsConstructor @Getter @Setter
|
@Getter @Setter
|
||||||
public class Score {
|
public class Score {
|
||||||
/**
|
/**
|
||||||
* The id for this score.
|
* The id for this score.
|
||||||
@ -128,11 +127,45 @@ public class Score {
|
|||||||
*/
|
*/
|
||||||
private String leaderboardId;
|
private String leaderboardId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The curve version for this score.
|
||||||
|
*/
|
||||||
|
private Integer curveVersion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The difficulty this score was set on.
|
* The difficulty this score was set on.
|
||||||
*/
|
*/
|
||||||
private Difficulty difficulty;
|
private Difficulty difficulty;
|
||||||
|
|
||||||
|
public Score(String id, int rank, int baseScore, int modifiedScore, Double pp, int weight, List<String> modifiers,
|
||||||
|
int multiplier, int badCuts, int missedNotes, int maxCombo, boolean fullCombo, int hmd, Date timeSet,
|
||||||
|
boolean hasReplay, String deviceHmd, String deviceControllerLeft, String deviceControllerRight,
|
||||||
|
List<Score> previousScores, String accountId, String leaderboardId, Integer curveVersion, Difficulty difficulty) {
|
||||||
|
this.id = id;
|
||||||
|
this.rank = rank;
|
||||||
|
this.baseScore = baseScore;
|
||||||
|
this.modifiedScore = modifiedScore;
|
||||||
|
this.pp = pp;
|
||||||
|
this.weight = weight;
|
||||||
|
this.modifiers = modifiers;
|
||||||
|
this.multiplier = multiplier;
|
||||||
|
this.badCuts = badCuts;
|
||||||
|
this.missedNotes = missedNotes;
|
||||||
|
this.maxCombo = maxCombo;
|
||||||
|
this.fullCombo = fullCombo;
|
||||||
|
this.hmd = hmd;
|
||||||
|
this.timeSet = timeSet;
|
||||||
|
this.hasReplay = hasReplay;
|
||||||
|
this.deviceHmd = deviceHmd;
|
||||||
|
this.deviceControllerLeft = deviceControllerLeft;
|
||||||
|
this.deviceControllerRight = deviceControllerRight;
|
||||||
|
this.previousScores = previousScores;
|
||||||
|
this.accountId = accountId;
|
||||||
|
this.leaderboardId = leaderboardId;
|
||||||
|
this.curveVersion = curveVersion == null ? ScoreSaberLeaderboard.INSTANCE.getCurveVersion() : curveVersion;
|
||||||
|
this.difficulty = difficulty;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a score from the given token.
|
* Gets a score from the given token.
|
||||||
*
|
*
|
||||||
@ -171,6 +204,7 @@ public class Score {
|
|||||||
new ArrayList<>(),
|
new ArrayList<>(),
|
||||||
playerId,
|
playerId,
|
||||||
leaderboard.getId(),
|
leaderboard.getId(),
|
||||||
|
ScoreSaberLeaderboard.INSTANCE.getCurveVersion(), // Get the curve version from the leaderboard.
|
||||||
Difficulty.fromId(leaderboard.getDifficulty().getDifficulty())
|
Difficulty.fromId(leaderboard.getDifficulty().getDifficulty())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import lombok.extern.log4j.Log4j2;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -120,33 +120,6 @@ public class ScoreSaberService extends TextWebSocketHandler {
|
|||||||
return scores;
|
return scores;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch the scores until the specified score id.
|
|
||||||
*
|
|
||||||
* @param account The account.
|
|
||||||
* @param scoreUntil The score to fetch until.
|
|
||||||
* @return The scores.
|
|
||||||
*/
|
|
||||||
public List<ScoreSaberPlayerScoreToken> getScoreUntil(Account account, Score scoreUntil) {
|
|
||||||
List<ScoreSaberPlayerScoreToken> scores = new ArrayList<>();
|
|
||||||
int page = 1;
|
|
||||||
do {
|
|
||||||
ScoreSaberScoresPageToken pageToken = getPageScores(account, page);
|
|
||||||
for (ScoreSaberPlayerScoreToken score : pageToken.getPlayerScores()) {
|
|
||||||
// If the score isn't the same as the scoreUntil, add it to the list.
|
|
||||||
if (!DateUtils.getDateFromString(score.getScore().getTimeSet()).equals(scoreUntil.getTimeSet())) {
|
|
||||||
scores.add(score);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (score.getScore().getId().equals(scoreUntil.getId())) {
|
|
||||||
// If the current score matches the specified scoreUntil, stop fetching.
|
|
||||||
return scores;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
page++;
|
|
||||||
} while (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the scores for the account.
|
* Fetches the scores for the account.
|
||||||
*
|
*
|
||||||
@ -165,9 +138,11 @@ public class ScoreSaberService extends TextWebSocketHandler {
|
|||||||
List<Leaderboard> leaderboardToSave = new ArrayList<>();
|
List<Leaderboard> leaderboardToSave = new ArrayList<>();
|
||||||
|
|
||||||
for (ScoreSaberScoresPageToken page : scoresPageTokens) {
|
for (ScoreSaberScoresPageToken page : scoresPageTokens) {
|
||||||
for (ScoreSaberPlayerScoreToken score : page.getPlayerScores()) {
|
for (ScoreSaberPlayerScoreToken scoreToken : page.getPlayerScores()) {
|
||||||
newScores.add(Score.fromToken(id, score));
|
Score score = Score.fromToken(id, scoreToken);
|
||||||
leaderboardToSave.add(Leaderboard.fromToken(score.getLeaderboard()));
|
newScores.add(score);
|
||||||
|
scores.add(score);
|
||||||
|
leaderboardToSave.add(Leaderboard.fromToken(scoreToken.getLeaderboard()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,33 +160,39 @@ public class ScoreSaberService extends TextWebSocketHandler {
|
|||||||
|
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
log.info("Fetching new scores for account '{}'.", id);
|
log.info("Fetching new scores for account '{}'.", id);
|
||||||
Score latestScore = scoreRepository.getScoresSortedByNewest(id).get(0);
|
|
||||||
|
|
||||||
List<ScoreSaberPlayerScoreToken> newScores = this.getScoreUntil(account, latestScore);
|
int page = 1; // The current page to search for scores.
|
||||||
if (newScores.isEmpty()) {
|
boolean done = false; // Whether we are done fetching scores.
|
||||||
log.info("No new scores found for account '{}'.", id);
|
List<ScoreSaberPlayerScoreToken> newScores = new ArrayList<>();
|
||||||
return;
|
do {
|
||||||
}
|
// This will keep fetching score pages until it finds a score that already exists.
|
||||||
|
ScoreSaberScoresPageToken pageScores = getPageScores(account, page);
|
||||||
int newScoreCount = 0;
|
for (ScoreSaberPlayerScoreToken score : pageScores.getPlayerScores()) {
|
||||||
for (ScoreSaberPlayerScoreToken newScore : newScores) {
|
boolean exists = scores.stream().anyMatch(s -> s.getId().equals(score.getScore().getId()));
|
||||||
if (saveScore(account, newScore)) {
|
if (!exists) {
|
||||||
newScoreCount++;
|
newScores.add(score);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
done = true;
|
||||||
}
|
}
|
||||||
|
page++;
|
||||||
|
} while (!done);
|
||||||
|
|
||||||
|
// Save the new scores.
|
||||||
|
for (ScoreSaberPlayerScoreToken score : newScores) {
|
||||||
|
saveScore(account, score);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Found {} new scores for account '{}'. (took: {}ms)", newScoreCount, id, System.currentTimeMillis() - start);
|
log.info("Found {} new scores for account '{}'. (took: {}ms)", newScores.size(), id, System.currentTimeMillis() - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the score for the account.
|
* Saves the score for the account.
|
||||||
*
|
*
|
||||||
* @param account The account.
|
* @param account The account.
|
||||||
* @param score The score to save.
|
* @param score The score to save.
|
||||||
* @return Whether the score was saved.
|
|
||||||
*/
|
*/
|
||||||
private boolean saveScore(Account account, ScoreSaberPlayerScoreToken score) {
|
private void saveScore(Account account, ScoreSaberPlayerScoreToken score) {
|
||||||
boolean didSave = false;
|
|
||||||
Leaderboard newScoreLeaderboard = Leaderboard.fromToken(score.getLeaderboard());
|
Leaderboard newScoreLeaderboard = Leaderboard.fromToken(score.getLeaderboard());
|
||||||
Score oldScore = scoreRepository.findById(score.getScore().getId()).orElse(null);
|
Score oldScore = scoreRepository.findById(score.getScore().getId()).orElse(null);
|
||||||
|
|
||||||
@ -227,19 +208,16 @@ public class ScoreSaberService extends TextWebSocketHandler {
|
|||||||
|
|
||||||
scoreRepository.delete(oldScore); // Delete the old score.
|
scoreRepository.delete(oldScore); // Delete the old score.
|
||||||
scoreRepository.save(scoreSet); // Save the new score.
|
scoreRepository.save(scoreSet); // Save the new score.
|
||||||
didSave = true;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The score is new
|
// The score is new
|
||||||
scoreRepository.save(Score.fromToken(account.getId(), score)); // Save the new score.
|
scoreRepository.save(Score.fromToken(account.getId(), score)); // Save the new score.
|
||||||
didSave = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the leaderboard doesn't already exist.
|
// Check if the leaderboard doesn't already exist.
|
||||||
if (leaderboardRepository.findById(newScoreLeaderboard.getId()).isEmpty()) {
|
if (leaderboardRepository.findById(newScoreLeaderboard.getId()).isEmpty()) {
|
||||||
leaderboardRepository.save(newScoreLeaderboard); // Save the leaderboard.
|
leaderboardRepository.save(newScoreLeaderboard); // Save the leaderboard.
|
||||||
}
|
}
|
||||||
return didSave;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -276,9 +254,21 @@ public class ScoreSaberService extends TextWebSocketHandler {
|
|||||||
*/
|
*/
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private void connectWebSocket() {
|
private void connectWebSocket() {
|
||||||
|
log.info("Connecting to the ScoreSaber WSS.");
|
||||||
new StandardWebSocketClient().execute(this, "wss://scoresaber.com/ws").get();
|
new StandardWebSocketClient().execute(this, "wss://scoresaber.com/ws").get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterConnectionEstablished(@NonNull WebSocketSession session) {
|
||||||
|
log.info("Connected to the ScoreSaber WSS.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterConnectionClosed(@NonNull WebSocketSession session, @NonNull CloseStatus status) {
|
||||||
|
log.info("Disconnected from the ScoreSaber WSS.");
|
||||||
|
connectWebSocket(); // Reconnect to the WebSocket.
|
||||||
|
}
|
||||||
|
|
||||||
@Override @SneakyThrows
|
@Override @SneakyThrows
|
||||||
protected void handleTextMessage(@NonNull WebSocketSession session, @NonNull TextMessage message) {
|
protected void handleTextMessage(@NonNull WebSocketSession session, @NonNull TextMessage message) {
|
||||||
// Ignore the connection message.
|
// Ignore the connection message.
|
||||||
@ -310,10 +300,4 @@ public class ScoreSaberService extends TextWebSocketHandler {
|
|||||||
log.error("An error occurred while handling the message.", ex);
|
log.error("An error occurred while handling the message.", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterConnectionClosed(@NonNull WebSocketSession session, @NonNull CloseStatus status) {
|
|
||||||
log.info("Disconnected from the ScoreSaber WSS.");
|
|
||||||
connectWebSocket(); // Reconnect to the WebSocket.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user