Add BeatLeader score modifier support
This commit is contained in:
parent
9150fc4dd6
commit
2e06ac16ac
@ -22,7 +22,6 @@ Need help? Feel free to message me at: Fascinated#4719
|
|||||||
|
|
||||||
## Todo
|
## Todo
|
||||||
|
|
||||||
- Make BeatLeader pp count check for modifiers
|
|
||||||
- Add toggle for showing pp
|
- Add toggle for showing pp
|
||||||
- Change the song time to a circular style in the song art
|
- Change the song time to a circular style in the song art
|
||||||
|
|
||||||
|
@ -12,9 +12,9 @@ const nextConfig = {
|
|||||||
"avatars.akamai.steamstatic.com",
|
"avatars.akamai.steamstatic.com",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
experimental: {
|
// experimental: {
|
||||||
optimizeCss: true,
|
// optimizeCss: true,
|
||||||
},
|
// },
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = nextConfig;
|
module.exports = nextConfig;
|
||||||
|
@ -9,12 +9,12 @@ const LeaderboardType = {
|
|||||||
MapData:
|
MapData:
|
||||||
"https://scoresaber.com/api/leaderboard/by-hash/%h/info?difficulty=%d",
|
"https://scoresaber.com/api/leaderboard/by-hash/%h/info?difficulty=%d",
|
||||||
},
|
},
|
||||||
async getMapStarCount(mapHash, mapDiff, characteristic) {
|
async getMapLeaderboardData(mapHash, mapDiff, characteristic) {
|
||||||
const data = await fetch(
|
const data = await fetch(
|
||||||
`/api/scoresaber/stars?hash=${mapHash}&difficulty=${mapDiff}&characteristic=${characteristic}`
|
`/api/scoresaber/data?hash=${mapHash}&difficulty=${mapDiff}&characteristic=${characteristic}`
|
||||||
);
|
);
|
||||||
const json = await data.json();
|
const json = await data.json();
|
||||||
return json.stars || undefined;
|
return json || undefined;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
BeatLeader: {
|
BeatLeader: {
|
||||||
@ -23,12 +23,12 @@ const LeaderboardType = {
|
|||||||
env(VARS.HTTP_PROXY) + "/https://api.beatleader.xyz/player/%s",
|
env(VARS.HTTP_PROXY) + "/https://api.beatleader.xyz/player/%s",
|
||||||
MapData: "https://api.beatleader.xyz/map/hash/%h",
|
MapData: "https://api.beatleader.xyz/map/hash/%h",
|
||||||
},
|
},
|
||||||
async getMapStarCount(mapHash, mapDiff, characteristic) {
|
async getMapLeaderboardData(mapHash, mapDiff, characteristic) {
|
||||||
const data = await fetch(
|
const data = await fetch(
|
||||||
`/api/beatleader/stars?hash=${mapHash}&difficulty=${mapDiff}&characteristic=${characteristic}`
|
`/api/beatleader/data?hash=${mapHash}&difficulty=${mapDiff}&characteristic=${characteristic}`
|
||||||
);
|
);
|
||||||
const json = await data.json();
|
const json = await data.json();
|
||||||
return json.stars || undefined;
|
return json || undefined;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -2,7 +2,6 @@ function curve(acc, stars) {
|
|||||||
var l = 1 - (0.03 * (stars - 3.0)) / 11.0;
|
var l = 1 - (0.03 * (stars - 3.0)) / 11.0;
|
||||||
var a = 0.96 * l;
|
var a = 0.96 * l;
|
||||||
var f = 1.2 - (0.6 * stars) / 14.0;
|
var f = 1.2 - (0.6 * stars) / 14.0;
|
||||||
|
|
||||||
return Math.pow(Math.log10(l / (l - acc)) / Math.log10(l / (l - a)), f);
|
return Math.pow(Math.log10(l / (l - acc)) / Math.log10(l / (l - a)), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +77,7 @@ const handlers: any = {
|
|||||||
resetCutState();
|
resetCutState();
|
||||||
state.setInSong(true);
|
state.setInSong(true);
|
||||||
state.setCombo(data.status.performance.combo);
|
state.setCombo(data.status.performance.combo);
|
||||||
|
state.setModifiers(data.status.mod);
|
||||||
useDataStore.setState({ loadedDuringSong: true });
|
useDataStore.setState({ loadedDuringSong: true });
|
||||||
|
|
||||||
const { score, relativeScore } = data.status.performance;
|
const { score, relativeScore } = data.status.performance;
|
||||||
@ -114,15 +115,9 @@ const handlers: any = {
|
|||||||
} = data.status.beatmap;
|
} = data.status.beatmap;
|
||||||
|
|
||||||
state.reset();
|
state.reset();
|
||||||
cutData.saberA = {
|
resetCutState();
|
||||||
count: [0, 0, 0],
|
|
||||||
totalScore: [0, 0, 0],
|
|
||||||
};
|
|
||||||
cutData.saberB = {
|
|
||||||
count: [0, 0, 0],
|
|
||||||
totalScore: [0, 0, 0],
|
|
||||||
};
|
|
||||||
state.setInSong(true);
|
state.setInSong(true);
|
||||||
|
state.setModifiers(data.status.mod);
|
||||||
state.updateMapData(
|
state.updateMapData(
|
||||||
getMapHashFromLevelId(levelId),
|
getMapHashFromLevelId(levelId),
|
||||||
difficultyEnum,
|
difficultyEnum,
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
import WebsiteTypes from "../../../../src/consts/LeaderboardType";
|
import WebsiteTypes from "../../../consts/LeaderboardType";
|
||||||
import {
|
import { getValue, setValue, valueExists } from "../../../utils/redisUtils";
|
||||||
getValue,
|
|
||||||
setValue,
|
|
||||||
valueExists,
|
|
||||||
} from "../../../../src/utils/redisUtils";
|
|
||||||
|
|
||||||
const KEY = "BL_MAP_STAR_";
|
const KEY = "BL_MAP_DATA_";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -29,12 +25,14 @@ export default async function handler(req, res) {
|
|||||||
const exists = await valueExists(key);
|
const exists = await valueExists(key);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
const data = await getValue(key);
|
const data = await getValue(key);
|
||||||
|
const json = JSON.parse(data);
|
||||||
res.setHeader("Cache-Status", "hit");
|
res.setHeader("Cache-Status", "hit");
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
status: "OK",
|
status: "OK",
|
||||||
stars: Number.parseFloat(data),
|
|
||||||
difficulty: difficulty,
|
difficulty: difficulty,
|
||||||
|
stars: json.stars,
|
||||||
|
modifiers: json.modifiers,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,12 +53,14 @@ export default async function handler(req, res) {
|
|||||||
}
|
}
|
||||||
const json = await data.json();
|
const json = await data.json();
|
||||||
let starCount = undefined;
|
let starCount = undefined;
|
||||||
|
let modifiers = undefined;
|
||||||
for (const diff of json.difficulties) {
|
for (const diff of json.difficulties) {
|
||||||
if (
|
if (
|
||||||
diff.difficultyName === difficulty &&
|
diff.difficultyName === difficulty &&
|
||||||
diff.modeName === characteristic
|
diff.modeName === characteristic
|
||||||
) {
|
) {
|
||||||
starCount = diff.stars;
|
starCount = diff.stars;
|
||||||
|
modifiers = diff.modifierValues;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (starCount === undefined) {
|
if (starCount === undefined) {
|
||||||
@ -69,7 +69,13 @@ export default async function handler(req, res) {
|
|||||||
message: "Unknown Map Hash",
|
message: "Unknown Map Hash",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await setValue(key, starCount);
|
await setValue(
|
||||||
|
key,
|
||||||
|
JSON.stringify({
|
||||||
|
stars: starCount,
|
||||||
|
modifiers: modifiers,
|
||||||
|
})
|
||||||
|
);
|
||||||
console.log(
|
console.log(
|
||||||
`[Cache]: Cached BL Star Count for hash ${mapHash} in ${
|
`[Cache]: Cached BL Star Count for hash ${mapHash} in ${
|
||||||
Date.now() - before
|
Date.now() - before
|
||||||
@ -78,6 +84,8 @@ export default async function handler(req, res) {
|
|||||||
res.setHeader("Cache-Status", "miss");
|
res.setHeader("Cache-Status", "miss");
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
status: "OK",
|
status: "OK",
|
||||||
|
difficulty: difficulty,
|
||||||
stars: starCount,
|
stars: starCount,
|
||||||
|
modifiers: modifiers,
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -1,11 +1,7 @@
|
|||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
import WebsiteTypes from "../../../../src/consts/LeaderboardType";
|
import WebsiteTypes from "../../../consts/LeaderboardType";
|
||||||
import {
|
import { getValue, setValue, valueExists } from "../../../utils/redisUtils";
|
||||||
getValue,
|
import { diffToScoreSaberDiff } from "../../../utils/scoreSaberUtils";
|
||||||
setValue,
|
|
||||||
valueExists,
|
|
||||||
} from "../../../../src/utils/redisUtils";
|
|
||||||
import { diffToScoreSaberDiff } from "../../../../src/utils/scoreSaberUtils";
|
|
||||||
|
|
||||||
const KEY = "SS_MAP_STAR_";
|
const KEY = "SS_MAP_STAR_";
|
||||||
|
|
||||||
@ -34,8 +30,8 @@ export default async function handler(req, res) {
|
|||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
status: "OK",
|
status: "OK",
|
||||||
stars: Number.parseFloat(data),
|
|
||||||
difficulty: difficulty,
|
difficulty: difficulty,
|
||||||
|
stars: Number.parseFloat(data),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,6 +70,7 @@ export default async function handler(req, res) {
|
|||||||
res.setHeader("Cache-Status", "miss");
|
res.setHeader("Cache-Status", "miss");
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
status: "OK",
|
status: "OK",
|
||||||
|
difficulty: difficulty,
|
||||||
stars: starCount,
|
stars: starCount,
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -12,7 +12,11 @@ interface SongDataState {
|
|||||||
songSubTitle: string;
|
songSubTitle: string;
|
||||||
songLength: number;
|
songLength: number;
|
||||||
songDifficulty: string;
|
songDifficulty: string;
|
||||||
mapStarCount: number;
|
songModifiers: Object;
|
||||||
|
mapLeaderboardData: {
|
||||||
|
stars: Number;
|
||||||
|
modifiers: Object;
|
||||||
|
};
|
||||||
mapArt: string;
|
mapArt: string;
|
||||||
bsr: string;
|
bsr: string;
|
||||||
|
|
||||||
@ -50,6 +54,7 @@ interface SongDataState {
|
|||||||
setPp: (percent: number) => void;
|
setPp: (percent: number) => void;
|
||||||
setInSong: (isInSong: boolean) => void;
|
setInSong: (isInSong: boolean) => void;
|
||||||
setSaberData: (saberType: string, cutData: any) => void;
|
setSaberData: (saberType: string, cutData: any) => void;
|
||||||
|
setModifiers: (modifiers: Map<string, object>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSongDataStore = create<SongDataState>()((set) => ({
|
export const useSongDataStore = create<SongDataState>()((set) => ({
|
||||||
@ -60,7 +65,11 @@ export const useSongDataStore = create<SongDataState>()((set) => ({
|
|||||||
songSubTitle: "",
|
songSubTitle: "",
|
||||||
songLength: 0,
|
songLength: 0,
|
||||||
songDifficulty: "",
|
songDifficulty: "",
|
||||||
mapStarCount: 0,
|
songModifiers: {},
|
||||||
|
mapLeaderboardData: {
|
||||||
|
stars: 0,
|
||||||
|
modifiers: {},
|
||||||
|
},
|
||||||
mapArt: "",
|
mapArt: "",
|
||||||
bsr: "",
|
bsr: "",
|
||||||
|
|
||||||
@ -93,11 +102,10 @@ export const useSongDataStore = create<SongDataState>()((set) => ({
|
|||||||
let hasError = false;
|
let hasError = false;
|
||||||
const leaderboardType = useSettingsStore.getState().leaderboardType;
|
const leaderboardType = useSettingsStore.getState().leaderboardType;
|
||||||
|
|
||||||
const mapStars = await Utils.getWebsiteApi(leaderboardType).getMapStarCount(
|
const mapLeaderboardData = await Utils.getWebsiteApi(
|
||||||
mapHash,
|
leaderboardType
|
||||||
mapDiff,
|
).getMapLeaderboardData(mapHash, mapDiff, characteristic);
|
||||||
characteristic
|
console.log(mapLeaderboardData);
|
||||||
);
|
|
||||||
|
|
||||||
const mapData = await axios.get(
|
const mapData = await axios.get(
|
||||||
`${env("SITE_URL")}/api/beatsaver/map?hash=${mapHash}`
|
`${env("SITE_URL")}/api/beatsaver/map?hash=${mapHash}`
|
||||||
@ -110,7 +118,7 @@ export const useSongDataStore = create<SongDataState>()((set) => ({
|
|||||||
set({
|
set({
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
hasError: hasError,
|
hasError: hasError,
|
||||||
mapStarCount: mapStars,
|
mapLeaderboardData: mapLeaderboardData,
|
||||||
bsr: bsr,
|
bsr: bsr,
|
||||||
mapArt: mapArt,
|
mapArt: mapArt,
|
||||||
songDifficulty: mapDiff,
|
songDifficulty: mapDiff,
|
||||||
@ -150,7 +158,7 @@ export const useSongDataStore = create<SongDataState>()((set) => ({
|
|||||||
|
|
||||||
setPp: (percent: number) => {
|
setPp: (percent: number) => {
|
||||||
const leaderboardType = useSettingsStore.getState().leaderboardType;
|
const leaderboardType = useSettingsStore.getState().leaderboardType;
|
||||||
const mapStarCount = useSongDataStore.getState().mapStarCount;
|
const mapStarCount = useSongDataStore.getState().mapLeaderboardData.stars;
|
||||||
|
|
||||||
let pp = Utils.calculatePP(mapStarCount, percent, leaderboardType);
|
let pp = Utils.calculatePP(mapStarCount, percent, leaderboardType);
|
||||||
if (pp === undefined) {
|
if (pp === undefined) {
|
||||||
@ -163,6 +171,10 @@ export const useSongDataStore = create<SongDataState>()((set) => ({
|
|||||||
set({ inSong: isInSong });
|
set({ inSong: isInSong });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setModifiers(modifiers: Map<string, object>) {
|
||||||
|
set({ songModifiers: modifiers });
|
||||||
|
},
|
||||||
|
|
||||||
reset: () =>
|
reset: () =>
|
||||||
set({
|
set({
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
@ -170,7 +182,11 @@ export const useSongDataStore = create<SongDataState>()((set) => ({
|
|||||||
songTitle: "",
|
songTitle: "",
|
||||||
songSubTitle: "",
|
songSubTitle: "",
|
||||||
songDifficulty: "",
|
songDifficulty: "",
|
||||||
mapStarCount: 0,
|
songModifiers: {},
|
||||||
|
mapLeaderboardData: {
|
||||||
|
stars: 0,
|
||||||
|
modifiers: {},
|
||||||
|
},
|
||||||
mapArt: "",
|
mapArt: "",
|
||||||
bsr: "",
|
bsr: "",
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { default as LeaderboardType } from "../consts/LeaderboardType";
|
import { default as LeaderboardType } from "../consts/LeaderboardType";
|
||||||
import { getBeatLeaderPP } from "../curve/BeatLeaderCurve";
|
import { getBeatLeaderPP } from "../curve/BeatLeaderCurve";
|
||||||
import { getScoreSaberPP } from "../curve/ScoreSaberCurve";
|
import { getScoreSaberPP } from "../curve/ScoreSaberCurve";
|
||||||
|
import { useSongDataStore } from "../store/songDataStore";
|
||||||
|
|
||||||
export default class Utils {
|
export default class Utils {
|
||||||
/**
|
/**
|
||||||
@ -35,7 +36,7 @@ export default class Utils {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (type === "BeatLeader") {
|
if (type === "BeatLeader") {
|
||||||
return getBeatLeaderPP(acc, stars);
|
return getBeatLeaderPP(acc, stars) * (1 + this.calculateModifierBonus());
|
||||||
}
|
}
|
||||||
if (type === "ScoreSaber") {
|
if (type === "ScoreSaber") {
|
||||||
return getScoreSaberPP(acc, stars);
|
return getScoreSaberPP(acc, stars);
|
||||||
@ -43,6 +44,47 @@ export default class Utils {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static calculateModifierBonus() {
|
||||||
|
const songMods = useSongDataStore.getState().songModifiers;
|
||||||
|
const modifierMulipliers =
|
||||||
|
useSongDataStore.getState().mapLeaderboardData.modifiers;
|
||||||
|
let bonus = 0;
|
||||||
|
|
||||||
|
if (
|
||||||
|
songMods.noFail == true &&
|
||||||
|
modifierMulipliers.nf < 0 &&
|
||||||
|
useSongDataStore.getState().failed
|
||||||
|
) {
|
||||||
|
bonus -= modifierMulipliers.nf;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (songMods.songSpeed != "Normal") {
|
||||||
|
if (songMods.songSpeed == "FasterSong" && modifierMulipliers.fs > 0) {
|
||||||
|
bonus += modifierMulipliers.fs;
|
||||||
|
}
|
||||||
|
if (songMods.songSpeed == "SuperFast" && modifierMulipliers.sf > 0) {
|
||||||
|
bonus += modifierMulipliers.sf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (songMods.disappearingArrows == true && modifierMulipliers.da > 0) {
|
||||||
|
bonus += modifierMulipliers.da;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (songMods.ghostNotes == true && modifierMulipliers.gn > 0) {
|
||||||
|
toAdd += modifierMulipliers.gn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (songMods.noArrows == true && modifierMulipliers.na < 0) {
|
||||||
|
bonus -= modifierMulipliers.na;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (songMods.noBombs == true && modifierMulipliers.nb < 0) {
|
||||||
|
bonus -= modifierMulipliers.nb;
|
||||||
|
}
|
||||||
|
return bonus;
|
||||||
|
}
|
||||||
|
|
||||||
static base64ToArrayBuffer(base64) {
|
static base64ToArrayBuffer(base64) {
|
||||||
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
|
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user