diff --git a/jsconfig.json b/jsconfig.json index 0a7a1a2..8ea6492 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "checkJs": true, - "target": "es2021" + "target": "es2021", + "noImplicitAny": false } } diff --git a/main.js b/main.js index 5f03de3..024655d 100644 --- a/main.js +++ b/main.js @@ -2,37 +2,49 @@ // https://github.com/hardcpp/BeatSaberPlus/wiki/%5BEN%5D-Song-Overlay const bspUrl = "ws://localhost:2947/socket"; -const retryMs = 5000; +const retryMs = 10000; let retries = 0; function connect() { console.log(`Connecting to ${bspUrl} (attempt ${retries++})`); const ws = new WebSocket(bspUrl); - ws.onopen = () => console.log("Connection open."); - ws.onmessage = (e) => { - const data = JSON.parse(e.data); - switch (data._type) { - case "event": - switch (data._event) { - case "gameState": - document.body.className = data.gameStateChanged; - break; + ws.onopen = onOpen; + ws.onmessage = onMessage; + ws.onclose = onClose; +} - case "mapInfo": - updateMapInfo(data.mapInfoChanged); - break; - } - break; +function onOpen() { + console.log("Connection open."); + retries = 0; +} - default: - console.log("message", e.data); - break; - } - }; - ws.onclose = (e) => { - console.log(`Connection closed. code: ${e.code}, reason: ${e.reason}, clean: ${e.wasClean}`); - setTimeout(connect, retryMs); - }; +/** @param {MessageEvent} e */ +function onMessage(e) { + /** @type {BeatSaberPlusEvent} */ + const data = JSON.parse(e.data); + switch (data._type) { + case "event": + switch (data._event) { + case "gameState": + document.body.className = data.gameStateChanged; + break; + + case "mapInfo": + updateMapInfo(data.mapInfoChanged); + break; + } + break; + + default: + console.log("message", e.data); + break; + } +} + +/** @param {CloseEvent} e */ +function onClose(e) { + console.log(`Connection closed. code: ${e.code}, reason: ${e.reason}, clean: ${e.wasClean}`); + setTimeout(connect, retryMs); } const cover = document.getElementById("cover"); @@ -56,10 +68,11 @@ function updateMapInfo(data) { mapper.textContent = data.mapper || ""; difficulty.textContent = data.difficulty.replace("Plus", " +") || ""; characteristicIcon.setAttribute("src", `images/characteristic/${data.characteristic}.svg`); - difficultyLabel.textContent = ""; - bsrKey.textContent = data.BSRKey || ""; + difficultyLabel.textContent = ""; // BS+ does not provide label + bsrKey.textContent = data.BSRKey || ""; // Always empty? type.textContent = !custom ? "OST" : data.level_id.endsWith(" WIP") ? "WIP" : ""; + // Fetch extra info from BeatSaver if (custom) { fetch(`https://api.beatsaver.com/maps/hash/${data.level_id.substring(13, 53)}`) .then(response => response.json()) diff --git a/types.d.ts b/types.d.ts index 95da8af..069b57e 100644 --- a/types.d.ts +++ b/types.d.ts @@ -1,20 +1,68 @@ -interface Document { - getElementById(elementId: string): HTMLElement; +// https://github.com/hardcpp/BeatSaberPlus/wiki/%5BEN%5D-Song-Overlay + +interface HandshakeEvent { + "_type": "handshake"; + "protocolVersion": number; + "gameVersion": string; + "playerName": string; + "playerPlatformId": string; } -interface MapInfoChanged { - "level_id": string; - "name": string; - "sub_name": string; - "artist": string; - "mapper": string; - "characteristic": string; - "difficulty": string; - "duration": number; - "BPM": number; - "PP": number; - "BSRKey": string; - "coverRaw": string; - "time": number; - "timeMultiplier": number; +interface GameStateEvent { + "_type": "event"; + "_event": "gameState"; + "gameStateChanged": "Menu" | "Playing"; +} + +interface ResumeEvent { + "_type": "event"; + "_event": "resume"; + "resumeTime": number; +} + +interface PauseEvent { + "_type": "event"; + "_event": "pause"; + "pauseTime": number; +} + +interface MapInfoChangedEvent { + "_type": "event"; + "_event": "mapInfo"; + "mapInfoChanged": { + "level_id": string; + "name": string; + "sub_name": string; + "artist": string; + "mapper": string; + "characteristic": string; + "difficulty": string; + "duration": number; + "BPM": number; + "PP": number; + "BSRKey": string; + "coverRaw": string; + "time": number; + "timeMultiplier": number; + }; +} + +interface ScoreEvent { + "_type": "event"; + "_event": "score"; + "scoreEvent": { + "time": number; + "score": number; + "accuracy": number; + "combo": number; + "missCount": number; + "currentHealth": number; + }; +} + +type BeatSaberPlusEvent = HandshakeEvent | GameStateEvent | ResumeEvent | PauseEvent | MapInfoChangedEvent | ScoreEvent; +type MapInfoChanged = MapInfoChangedEvent["mapInfoChanged"]; + +interface Document { + getElementById(elementId: string): HTMLElement; // Assume non-null }