diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/beatsaber-overlay.iml b/.idea/beatsaber-overlay.iml new file mode 100644 index 0000000..0c8867d --- /dev/null +++ b/.idea/beatsaber-overlay.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/discord.xml b/.idea/discord.xml new file mode 100644 index 0000000..d8e9561 --- /dev/null +++ b/.idea/discord.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..03d9549 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..3ef06f2 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/components/ScoreStats.js b/components/ScoreStats.js new file mode 100644 index 0000000..60b3103 --- /dev/null +++ b/components/ScoreStats.js @@ -0,0 +1,150 @@ +import {Component, useState} from "react"; + +export default class ScoreStats extends Component { + + constructor(params) { + super(params); + + this.state = { + score: 0, + dataSocket: undefined, + isVisible: false, + leftHand: { + averageCut: [15.00], + averagePreSwing: [70.00], + averagePostSwing: [30.00], + }, + rightHand: { + averageCut: [15.00], + averagePreSwing: [70.00], + averagePostSwing: [30.00], + } + } + } + + componentDidMount() { + const socket = new WebSocket('ws://localhost:6557/socket'); + this.setState({ socket: socket }); + socket.addEventListener('error' || 'close', () => { + console.log("Attempting to re-connect to the HTTP Status socket."); + this.setState({ socket: new WebSocket('ws://localhost:6557/socket') }); + }); + socket.addEventListener('message', (message) => { + //console.log("Received message from HTTP Status."); + const json = JSON.parse(message.data); + if (!handlers[json.event]) { + console.log("Unhandled message from HTTP Status. (" + json.event + ")"); + return; + } + handlers[json.event](json || []); + }) + + const handlers = { + "hello": () => { + console.log("Hello from HTTP Status!"); + }, + "scoreChanged": (data) => { + const { status } = data; + this.setState({ score: status.performance.score }) + }, + "noteFullyCut": (data) => { + const { noteCut } = data; + + // Left Saber + if (noteCut.saberType === 'SaberA') { + const data = this.state.leftHand; + if (data.averageCut.includes(15) && data.averageCut.length === 1) { + data.averageCut = []; + } + if (data.averagePreSwing.includes(70) && data.averagePreSwing.length === 1) { + data.averagePreSwing = []; + } + if (data.averagePostSwing.includes(30) && data.averagePostSwing.length === 1) { + data.averagePostSwing = []; + } + data.averagePreSwing.push(noteCut.initialScore > 70 ? 70 : noteCut.initialScore); + data.averagePostSwing.push(noteCut.finalScore - noteCut.initialScore); + data.averageCut.push(noteCut.cutDistanceScore); + this.setState({ leftHand: data }); + } + + // Left Saber + if (noteCut.saberType === 'SaberB') { + const data = this.state.rightHand; + if (data.averageCut.includes(15) && data.averageCut.length === 1) { + data.averageCut = []; + } + if (data.averagePreSwing.includes(70) && data.averagePreSwing.length === 1) { + data.averagePreSwing = []; + } + if (data.averagePostSwing.includes(30) && data.averagePostSwing.length === 1) { + data.averagePostSwing = []; + } + data.averagePreSwing.push(noteCut.initialScore > 70 ? 70 : noteCut.initialScore); + data.averagePostSwing.push(noteCut.finalScore - noteCut.initialScore); + data.averageCut.push(noteCut.cutDistanceScore); + this.setState({ rightHand: data }); + } + }, + "menu": () => { + console.log("Exiting level, resetting data.") + this.setState({ + "leftHand": { + "averageCut": [15.00], + "averagePreSwing": [70.00], + "averagePostSwing": [30.00], + }, + "rightHand": { + "averageCut": [15.00], + "averagePreSwing": [70.00], + "averagePostSwing": [30.00], + }, + isVisible: false + }); + }, + "songStart": () => { + console.log("Going into level, resetting data.") + this.setState({ + "leftHand": { + "averageCut": [15.00], + "averagePreSwing": [70.00], + "averagePostSwing": [30.00], + }, + "rightHand": { + "averageCut": [15.00], + "averagePreSwing": [70.00], + "averagePostSwing": [30.00], + }, + isVisible: true + }); + }, + "noteCut": () => {}, + "noteMissed": () => {}, + "noteSpawned": () => {}, + "bombMissed": () => {}, + "beatmapEvent": () => {} + } + } + + getAverage(values) { + return values.reduce( ( p, c ) => p + c, 0 ) / values.length; + } + + render() { + return
+

{this.state.score.toLocaleString()}

+
+
+

{this.getAverage(this.state.leftHand.averagePreSwing).toFixed(2)}

+

{this.getAverage(this.state.leftHand.averagePostSwing).toFixed(2)}

+

{this.getAverage(this.state.leftHand.averageCut).toFixed(2)}

+
+
+

{this.getAverage(this.state.rightHand.averagePreSwing).toFixed(2)}

+

{this.getAverage(this.state.rightHand.averagePostSwing).toFixed(2)}

+

{this.getAverage(this.state.rightHand.averageCut).toFixed(2)}

+
+
+
+ } +} \ No newline at end of file diff --git a/config.json b/config.json new file mode 100644 index 0000000..a1e7e28 --- /dev/null +++ b/config.json @@ -0,0 +1,7 @@ +{ + "name": "BeatSaber Overlay", + "description": "Simple scoresaber overlay", + "color": "#0EBFE9", + "url": "https://bs-overlay.fascinated.cc", + "proxy_url": "https://bangor375.herokuapp.com" +} \ No newline at end of file diff --git a/pages/_app.js b/pages/_app.js index 4f164e8..0d101f9 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,16 +1,18 @@ import Head from 'next/head' +import Config from '../config.json'; + import '../styles/globals.css' function MyApp({ Component, pageProps }) { return <> - BeatSaber Overlay - - - - - + {Config.name} + + + + + diff --git a/pages/index.js b/pages/index.js index 45f8f7a..08af91c 100644 --- a/pages/index.js +++ b/pages/index.js @@ -2,10 +2,13 @@ import Head from 'next/head' import { Component } from 'react' import Avatar from '../components/Avatar'; import PlayerStats from '../components/PlayerStats'; +import ScoreStats from '../components/ScoreStats'; + +import Config from '../config.json'; // Why do u have to proxy requests... it's so dumb LOL -const API_URL = "https://bangor375.herokuapp.com/https://scoresaber.com/api/player/%s/full"; -const GUTHUB_URL = "https://github.com/RealFascinated/beatsaber-overlay"; +const API_URL = Config.proxy_url + "/https://scoresaber.com/api/player/%s/full"; +const GITHUB_URL = "https://github.com/RealFascinated/beatsaber-overlay"; export default class Home extends Component { @@ -16,7 +19,9 @@ export default class Home extends Component { loading: true, id: undefined, isValidScoresaber: true, - data: undefined + data: undefined, + showPlayerStats: true, + showScore: false } } @@ -25,11 +30,21 @@ export default class Home extends Component { const params = Object.fromEntries(urlSearchParams.entries()); const id = params.id; - if (!id) { // Check if the id pararm is valid + if (!id) { // Check if the id param is valid this.setState({ loading: false, isValidScoresaber: false }); return; } + // Check if the player wants to disable their stats (pp, global pos, etc) + if (params.playerstats === 'false') { + this.setState({ showPlayerStats: false }); + } + + // Check if the player wants to show their current score information on the overlay + if (params.scoreinfo === 'true') { + this.setState({ showScore: true }); + } + await this.updateData(id); setTimeout(async () => { if (!this.state.isValidScoresaber) { @@ -53,7 +68,6 @@ export default class Home extends Component { return; } this.setState({ loading: false, id: id, data: json }); - console.log(json); } render() { @@ -71,27 +85,43 @@ export default class Home extends Component { : !isValidScoresaber ?
-

Provide a valid scoresaber id

-

Example: {document.location.origin}?id=76561198449412074

-
+

This is currently just a simple overlay for OBS displaying ScoreSaber stats.

If you have any suggestions you can message me on discord @ Fascinated#4719

+
+

Provide a valid scoresaber id

+

Example: {document.location.origin}?id=76561198449412074

+

Example with Score Info: {document.location.origin}?id=76561198449412074&scoreinfo=true

+

Example with Score Info and without Player Stats: {document.location.origin}?id=76561198449412074&scoreinfo=true&playerstats=false

+
+
+

Options

+

scoreinfo - Can be "true" if you want to show your current score (needs HTTP Status)

+

playerstats - Can be "false" if you disable showing your stats (pp, global pos, etc)

+

If you use this overlay and like it, don't forget to star the project :3

-

Github link: {GUTHUB_URL}

+

Github link: {GITHUB_URL}

:
-
- - -
+ { this.state.showPlayerStats ? +
+ + +
: + "" + } + { + this.state.showScore ? : + "" + }
} diff --git a/styles/globals.css b/styles/globals.css index af858da..09cf9f0 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -50,8 +50,7 @@ } .player-country p { - margin: 0; - margin-right: 6px; + margin: 0 6px 0 0; } .player-country-icon { @@ -67,4 +66,21 @@ display: block; margin-left: 10px; margin-top: -12px; +} + +.score-stats { + text-align: right; + position:absolute; + top:0; + right:0; + margin-right: 15px; +} + +.score-stats-hands { + display: flex; + margin-top: -30px; +} + +.score-stats-hands div { + padding-left: 10px; } \ No newline at end of file