Updated curve stuff and added ScoreSaber pp calc
This commit is contained in:
parent
f7de7425f9
commit
4de24a34d0
@ -30,9 +30,7 @@ export default async function handler(req, res) {
|
||||
|
||||
const before = Date.now();
|
||||
const data = await fetch(
|
||||
WebsiteTypes.BeatLeader.ApiUrl.MapData.replace("%h", mapHash)
|
||||
.replace("%d", difficulty)
|
||||
.replace("%m", characteristic),
|
||||
WebsiteTypes.BeatLeader.ApiUrl.MapData.replace("%h", mapHash),
|
||||
{
|
||||
headers: {
|
||||
"X-Requested-With": "BeatSaber Overlay",
|
||||
|
70
pages/api/scoresaber/stars.js
Normal file
70
pages/api/scoresaber/stars.js
Normal file
@ -0,0 +1,70 @@
|
||||
import fetch from "node-fetch";
|
||||
import WebsiteTypes from "../../../src/consts/LeaderboardType";
|
||||
import RedisUtils from "../../../src/utils/redisUtils";
|
||||
import { diffToScoreSaberDiff } from "../../../src/utils/scoreSaberUtils";
|
||||
|
||||
const KEY = "SS_MAP_STAR_";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @returns
|
||||
*/
|
||||
export default async function handler(req, res) {
|
||||
const mapHash = req.query.hash.replace("custom_level_", "").toLowerCase();
|
||||
const difficulty = req.query.difficulty.replace(" ", "");
|
||||
const characteristic = req.query.characteristic;
|
||||
|
||||
const key = `${KEY}${difficulty}-${characteristic}-${mapHash}`;
|
||||
const exists = await RedisUtils.exists(key);
|
||||
if (exists) {
|
||||
const data = await RedisUtils.getValue(key);
|
||||
res.setHeader("Cache-Status", "hit");
|
||||
|
||||
return res.status(200).json({
|
||||
status: "OK",
|
||||
stars: Number.parseFloat(data),
|
||||
difficulty: difficulty,
|
||||
});
|
||||
}
|
||||
|
||||
const before = Date.now();
|
||||
const data = await fetch(
|
||||
WebsiteTypes.ScoreSaber.ApiUrl.MapData.replace("%h", mapHash).replace(
|
||||
"%d",
|
||||
diffToScoreSaberDiff(difficulty)
|
||||
),
|
||||
{
|
||||
headers: {
|
||||
"X-Requested-With": "BeatSaber Overlay",
|
||||
},
|
||||
}
|
||||
);
|
||||
if (data.status === 404) {
|
||||
return res.status(404).json({
|
||||
status: 404,
|
||||
message: "Unknown Map Hash",
|
||||
});
|
||||
}
|
||||
const json = await data.json();
|
||||
console.log(json);
|
||||
let starCount = json.stars;
|
||||
if (starCount === undefined) {
|
||||
return res.status(404).json({
|
||||
status: 404,
|
||||
message: "Unknown Map Hash",
|
||||
});
|
||||
}
|
||||
await RedisUtils.setValue(key, starCount);
|
||||
console.log(
|
||||
`[Cache]: Cached SS Star Count for hash ${mapHash} in ${
|
||||
Date.now() - before
|
||||
}ms`
|
||||
);
|
||||
res.setHeader("Cache-Status", "miss");
|
||||
return res.status(200).json({
|
||||
status: "OK",
|
||||
stars: starCount,
|
||||
});
|
||||
}
|
@ -266,16 +266,24 @@ export default class Overlay extends Component {
|
||||
const json = await data.json();
|
||||
this.setState({ beatSaverData: json });
|
||||
|
||||
if (this.state.websiteType == "BeatLeader") {
|
||||
const { characteristic, levelId, difficulty } = songData;
|
||||
let mapHash = levelId.replace("custom_level_", "");
|
||||
const mapStars = await LeaderboardType.BeatLeader.getMapStarCount(
|
||||
const { characteristic, levelId, difficulty } = songData;
|
||||
let mapHash = levelId.replace("custom_level_", "");
|
||||
let mapStars = undefined;
|
||||
if (this.state.websiteType === "BeatLeader") {
|
||||
mapStars = await LeaderboardType.BeatLeader.getMapStarCount(
|
||||
mapHash,
|
||||
difficulty.replace("+", "Plus"),
|
||||
characteristic
|
||||
);
|
||||
this.setState({ mapStarCount: mapStars });
|
||||
}
|
||||
if (this.state.websiteType === "ScoreSaber") {
|
||||
mapStars = await LeaderboardType.ScoreSaber.getMapStarCount(
|
||||
mapHash,
|
||||
difficulty.replace("+", "Plus"),
|
||||
characteristic
|
||||
);
|
||||
}
|
||||
this.setState({ mapStarCount: mapStars });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,6 +4,15 @@ const WebsiteTypes = {
|
||||
PlayerData:
|
||||
process.env.NEXT_PUBLIC_HTTP_PROXY +
|
||||
"/https://scoresaber.com/api/player/%s/basic",
|
||||
MapData:
|
||||
"https://scoresaber.com/api/leaderboard/by-hash/%h/info?difficulty=%d",
|
||||
},
|
||||
async getMapStarCount(mapHash, mapDiff, characteristic) {
|
||||
const data = await fetch(
|
||||
`/api/scoresaber/stars?hash=${mapHash}&difficulty=${mapDiff}&characteristic=${characteristic}`
|
||||
);
|
||||
const json = await data.json();
|
||||
return json.stars || undefined;
|
||||
},
|
||||
},
|
||||
BeatLeader: {
|
||||
@ -20,20 +29,6 @@ const WebsiteTypes = {
|
||||
const json = await data.json();
|
||||
return json.stars || undefined;
|
||||
},
|
||||
curve(acc, stars) {
|
||||
var l = 1 - (0.03 * (stars - 3.0)) / 11.0;
|
||||
var a = 0.96 * l;
|
||||
var f = 1.2 - (0.6 * stars) / 14.0;
|
||||
|
||||
return Math.pow(Math.log10(l / (l - acc)) / Math.log10(l / (l - a)), f);
|
||||
},
|
||||
ppFromAcc(acc, stars) {
|
||||
if (stars === undefined || acc === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const pp = this.curve(acc / 100, stars - 0.5) * (stars + 0.5) * 42;
|
||||
return Number.isNaN(pp) ? undefined : pp;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
18
src/curve/BeatLeaderCurve.js
Normal file
18
src/curve/BeatLeaderCurve.js
Normal file
@ -0,0 +1,18 @@
|
||||
export default class BeatLeaderCurve {
|
||||
static curve(acc, stars) {
|
||||
var l = 1 - (0.03 * (stars - 3.0)) / 11.0;
|
||||
var a = 0.96 * l;
|
||||
var f = 1.2 - (0.6 * stars) / 14.0;
|
||||
|
||||
return Math.pow(Math.log10(l / (l - acc)) / Math.log10(l / (l - a)), f);
|
||||
}
|
||||
|
||||
static getPP(acc, stars) {
|
||||
if (stars === undefined || acc === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const pp =
|
||||
BeatLeaderCurve.curve(acc / 100, stars - 0.5) * (stars + 0.5) * 42;
|
||||
return Number.isNaN(pp) ? undefined : pp;
|
||||
}
|
||||
}
|
82
src/curve/ScoreSaberCurve.js
Normal file
82
src/curve/ScoreSaberCurve.js
Normal file
@ -0,0 +1,82 @@
|
||||
// Yoinked from https://github.com/Shurdoof/pp-calculator/blob/c24b5ca452119339928831d74e6d603fb17fd5ef/src/lib/pp/calculator.ts
|
||||
// Thank for for this I have no fucking idea what the maths is doing but it works!
|
||||
export default class ScoreSaberCurve {
|
||||
static starMultiplier = 42.11;
|
||||
static ppCurve = [
|
||||
[1, 7],
|
||||
[0.999, 5.8],
|
||||
[0.9975, 4.7],
|
||||
[0.995, 3.76],
|
||||
[0.9925, 3.17],
|
||||
[0.99, 2.73],
|
||||
[0.9875, 2.38],
|
||||
[0.985, 2.1],
|
||||
[0.9825, 1.88],
|
||||
[0.98, 1.71],
|
||||
[0.9775, 1.57],
|
||||
[0.975, 1.45],
|
||||
[0.9725, 1.37],
|
||||
[0.97, 1.31],
|
||||
[0.965, 1.2],
|
||||
[0.96, 1.11],
|
||||
[0.955, 1.045],
|
||||
[0.95, 1],
|
||||
[0.94, 0.94],
|
||||
[0.93, 0.885],
|
||||
[0.92, 0.835],
|
||||
[0.91, 0.79],
|
||||
[0.9, 0.75],
|
||||
[0.875, 0.655],
|
||||
[0.85, 0.57],
|
||||
[0.825, 0.51],
|
||||
[0.8, 0.47],
|
||||
[0.75, 0.4],
|
||||
[0.7, 0.34],
|
||||
[0.65, 0.29],
|
||||
[0.6, 0.25],
|
||||
[0.0, 0.0],
|
||||
];
|
||||
|
||||
static clamp(value, min, max) {
|
||||
if (min !== null && value < min) {
|
||||
return min;
|
||||
}
|
||||
|
||||
if (max !== null && value > max) {
|
||||
return max;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static lerp(v0, v1, t) {
|
||||
return v0 + t * (v1 - v0);
|
||||
}
|
||||
|
||||
static calculatePPModifier(c1, c2, acc) {
|
||||
const distance = (c2[0] - acc) / (c2[0] - c1[0]);
|
||||
return ScoreSaberCurve.lerp(c2[1], c1[1], distance);
|
||||
}
|
||||
|
||||
static findPPModifier(acc, curve) {
|
||||
acc = ScoreSaberCurve.clamp(acc, 0, 100) / 100;
|
||||
|
||||
let prev = curve[1];
|
||||
for (const item of curve) {
|
||||
if (item[0] <= acc) {
|
||||
return ScoreSaberCurve.calculatePPModifier(item, prev, acc);
|
||||
}
|
||||
prev = item;
|
||||
}
|
||||
}
|
||||
|
||||
static getPP(acc, stars) {
|
||||
const ppValue = stars * ScoreSaberCurve.starMultiplier;
|
||||
const modifier = ScoreSaberCurve.findPPModifier(
|
||||
acc,
|
||||
ScoreSaberCurve.ppCurve
|
||||
);
|
||||
|
||||
return modifier * ppValue;
|
||||
}
|
||||
}
|
30
src/utils/scoreSaberUtils.js
Normal file
30
src/utils/scoreSaberUtils.js
Normal file
@ -0,0 +1,30 @@
|
||||
function diffToScoreSaberDiff(diff) {
|
||||
console.log(
|
||||
"🚀 ~ file: scoreSaberUtils.js ~ line 2 ~ diffToScoreSaberDiff ~ diff",
|
||||
diff
|
||||
);
|
||||
switch (diff) {
|
||||
case "Easy": {
|
||||
return 1;
|
||||
}
|
||||
case "Normal": {
|
||||
return 3;
|
||||
}
|
||||
case "Hard": {
|
||||
return 5;
|
||||
}
|
||||
case "Expert": {
|
||||
return 7;
|
||||
}
|
||||
case "Expert+": {
|
||||
return 9;
|
||||
}
|
||||
case "ExpertPlus": {
|
||||
return 9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
diffToScoreSaberDiff,
|
||||
};
|
@ -1,11 +1,8 @@
|
||||
import {
|
||||
default as LeaderboardType,
|
||||
default as WebsiteTypes,
|
||||
} from "../consts/LeaderboardType";
|
||||
import { default as LeaderboardType } from "../consts/LeaderboardType";
|
||||
import BeatLeaderCurve from "../curve/BeatLeaderCurve";
|
||||
import ScoreSaberCurve from "../curve/ScoreSaberCurve";
|
||||
|
||||
export default class Utils {
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* Returns the information for the given website type.
|
||||
*
|
||||
@ -35,7 +32,10 @@ export default class Utils {
|
||||
|
||||
static calculatePP(stars, acc, type) {
|
||||
if (type === "BeatLeader") {
|
||||
return WebsiteTypes.BeatLeader.ppFromAcc(acc, stars);
|
||||
return BeatLeaderCurve.getPP(acc, stars);
|
||||
}
|
||||
if (type === "ScoreSaber") {
|
||||
return ScoreSaberCurve.getPP(acc, stars);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
Reference in New Issue
Block a user