diff --git a/pages/overlay.js b/pages/overlay.js index 1a03128..e67576f 100644 --- a/pages/overlay.js +++ b/pages/overlay.js @@ -3,6 +3,7 @@ import { Component } from "react"; import PlayerStats from "../src/components/PlayerStats"; import ScoreStats from "../src/components/ScoreStats"; import SongInfo from "../src/components/SongInfo"; +import LeaderboardType from "../src/consts/LeaderboardType"; import Utils from "../src/utils/utils"; import styles from "../styles/overlay.module.css"; @@ -26,7 +27,7 @@ export default class Overlay extends Component { showPlayerStats: true, showScore: false, showSongInfo: false, - loadedDuringSong: false, + loadedDuringSong: false, socket: undefined, isVisible: false, @@ -37,6 +38,7 @@ export default class Overlay extends Component { currentScore: 0, percentage: "100.00%", failed: false, + mapStarCount: undefined, leftHand: { averageCut: [15.0], averagePreSwing: [70.0], @@ -175,7 +177,7 @@ export default class Overlay extends Component { const data = await fetch( Utils.getWebsiteApi( id == "test" ? "Test" : this.state.websiteType - ).ApiUrl.replace("%s", id), + ).ApiUrl.PlayerData.replace("%s", id), { headers: { "X-Requested-With": "BeatSaber Overlay", @@ -234,6 +236,7 @@ export default class Overlay extends Component { console.log( "Attempting to re-connect to the HTTP Status socket in 60 seconds." ); + this.resetData(false); this.setState({ isConnectedToSocket: false }); setTimeout(() => this.connectSocket(), 60_000); }); @@ -261,6 +264,17 @@ 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( + mapHash, + difficulty, + characteristic + ); + this.setState({ mapStarCount: mapStars }); + } } /** @@ -291,7 +305,8 @@ export default class Overlay extends Component { currentScore: 0, percentage: "100.00%", isVisible: visible, - loadedDuringSong: loadedDuringSong + loadedDuringSong: loadedDuringSong, + mapStarCount: undefined, }); } @@ -299,15 +314,15 @@ export default class Overlay extends Component { handlers = { hello: (data) => { console.log("Hello from HTTP Status!"); - if (data.status) { - if (this.state.songData === undefined) { - console.log("Going into level during song, resetting data."); - this.resetData(true, true); - this.setState({ songData: data, paused: false }); - if (data.status.beatmap) { - this.setBeatSaver(data.status.beatmap); - } - } + if ( + data.status && + data.status.beatmap && + this.state.songData === undefined + ) { + console.log("Going into level during song, resetting data."); + this.resetData(true, true); + this.setState({ songData: data, paused: false }); + this.setBeatSaver(data.status.beatmap); } }, scoreChanged: (data) => { @@ -438,7 +453,7 @@ export default class Overlay extends Component { countryRank={data.countryRank.toLocaleString()} websiteType={websiteType} avatar={`/api/steamavatar?steamid=${id}`} - loadedDuringSong={this.state.loadedDuringSong} + loadedDuringSong={this.state.loadedDuringSong} /> ) : ( <> diff --git a/src/components/ScoreStats.js b/src/components/ScoreStats.js index 1898fb1..63430da 100644 --- a/src/components/ScoreStats.js +++ b/src/components/ScoreStats.js @@ -1,6 +1,7 @@ import { Component } from "react"; import styles from "../../styles/scoreStats.module.css"; +import Utils from "../utils/utils"; export default class ScoreStats extends Component { constructor(params) { @@ -19,12 +20,18 @@ export default class ScoreStats extends Component { render() { const data = this.props.data; + const currentPP = Utils.calculatePP( + data.mapStarCount, + data.percentage.replace("%", ""), + data.websiteType + ); return (

{data.percentage}

{data.currentScore.toLocaleString()}

+ {currentPP !== undefined ?

{currentPP.toFixed(0)}pp

: null}

Average Cut

diff --git a/src/consts/LeaderboardType.js b/src/consts/LeaderboardType.js index 23b9c2c..27f0eee 100644 --- a/src/consts/LeaderboardType.js +++ b/src/consts/LeaderboardType.js @@ -2,10 +2,46 @@ import Config from "../../config.json"; const WebsiteTypes = { ScoreSaber: { - ApiUrl: Config.proxy_url + "/https://scoresaber.com/api/player/%s/basic", + ApiUrl: { + PlayerData: + Config.proxy_url + "/https://scoresaber.com/api/player/%s/basic", + }, }, BeatLeader: { - ApiUrl: Config.proxy_url + "/https://api.beatleader.xyz/player/%s", + ApiUrl: { + PlayerData: Config.proxy_url + "/https://api.beatleader.xyz/player/%s", + MapData: + Config.proxy_url + + "/https://api.beatleader.xyz/leaderboard/hash/%h/%d/%m", + }, + async getMapStarCount(mapHash, mapDiff, characteristic) { + const data = await fetch( + this.ApiUrl.MapData.replace("%h", mapHash) + .replace("%d", mapDiff.replace("+", "Plus")) + .replace("%m", characteristic), + { + headers: { + "X-Requested-With": "BeatSaber Overlay", + }, + } + ); + const json = await data.json(); + return json.difficulty.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) { + return undefined; + } + const pp = this.curve(acc / 100, stars - 0.5) * (stars + 0.5) * 42; + return pp == NaN ? undefined : pp; + }, }, Test: { ApiUrl: "/api/mockdata", diff --git a/src/utils/utils.js b/src/utils/utils.js index 6f0e83b..4925145 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -1,9 +1,12 @@ import SteamIdCache from "../../src/caches/SteamIdCache"; -import LeaderboardType from "../consts/LeaderboardType"; +import { + default as LeaderboardType, + default as WebsiteTypes, +} from "../consts/LeaderboardType"; const TO_CHECK = [ - LeaderboardType.ScoreSaber.ApiUrl, - LeaderboardType.BeatLeader.ApiUrl, + LeaderboardType.ScoreSaber.ApiUrl.PlayerData, + LeaderboardType.BeatLeader.ApiUrl.PlayerData, ]; export default class Utils { @@ -64,4 +67,11 @@ export default class Utils { const json = await data.json(); return !!json.pp; } + + static calculatePP(stars, acc, type) { + if (type === "BeatLeader") { + return WebsiteTypes.BeatLeader.ppFromAcc(acc, stars); + } + return undefined; + } }