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