146 lines
4.2 KiB
JavaScript
146 lines
4.2 KiB
JavaScript
"use strict";
|
|
|
|
(function registerStoragePlugin() {
|
|
const storageKey = "tdkpin.save.v1";
|
|
const encoder = new TextEncoder();
|
|
const decoder = new TextDecoder();
|
|
const highScoreApi = "api/highscores";
|
|
let lastRevision = 0;
|
|
let lastHighScoreRevision = 0;
|
|
let highScoreRequestInFlight = false;
|
|
let highScoreGeneration = 0;
|
|
let highScoreSubmissionWarningShown = false;
|
|
|
|
function browserStorage() {
|
|
try {
|
|
return window.localStorage;
|
|
} catch (_error) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function sendSavedDataToRust() {
|
|
wasm_exports.tdkpin_browser_storage_clear();
|
|
const storage = browserStorage();
|
|
let saved = null;
|
|
try {
|
|
saved = storage?.getItem(storageKey);
|
|
} catch (_error) {
|
|
saved = null;
|
|
}
|
|
if (saved !== null && saved !== undefined) {
|
|
for (const byte of encoder.encode(saved)) {
|
|
wasm_exports.tdkpin_browser_storage_push(byte);
|
|
}
|
|
}
|
|
wasm_exports.tdkpin_browser_storage_finish();
|
|
}
|
|
|
|
function flushRustSave() {
|
|
const revision = wasm_exports.tdkpin_browser_storage_save_revision();
|
|
if (revision === lastRevision) {
|
|
return;
|
|
}
|
|
|
|
const bytes = new Uint8Array(
|
|
wasm_exports.tdkpin_browser_storage_save_length(),
|
|
);
|
|
for (let index = 0; index < bytes.length; index += 1) {
|
|
bytes[index] = wasm_exports.tdkpin_browser_storage_save_byte(index);
|
|
}
|
|
|
|
const storage = browserStorage();
|
|
try {
|
|
storage?.setItem(storageKey, decoder.decode(bytes));
|
|
} catch (_error) {
|
|
// Private browsing or a full quota should not stop the game loop.
|
|
}
|
|
wasm_exports.tdkpin_browser_storage_save_ack();
|
|
lastRevision = revision;
|
|
}
|
|
|
|
function deliverHighScores(json) {
|
|
wasm_exports.tdkpin_browser_high_scores_clear();
|
|
for (const byte of encoder.encode(json)) {
|
|
wasm_exports.tdkpin_browser_high_scores_push(byte);
|
|
}
|
|
wasm_exports.tdkpin_browser_high_scores_finish();
|
|
}
|
|
|
|
async function fetchHighScores() {
|
|
const generation = ++highScoreGeneration;
|
|
try {
|
|
const response = await fetch(highScoreApi, {
|
|
cache: "no-store",
|
|
headers: { Accept: "application/json" },
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`high-score request failed (${response.status})`);
|
|
}
|
|
const json = await response.text();
|
|
if (generation === highScoreGeneration) {
|
|
deliverHighScores(json);
|
|
}
|
|
} catch (error) {
|
|
console.warn("shared high scores unavailable; using local scores", error);
|
|
}
|
|
}
|
|
|
|
async function flushHighScoreSubmission() {
|
|
if (highScoreRequestInFlight) {
|
|
return;
|
|
}
|
|
const revision = wasm_exports.tdkpin_browser_high_score_revision();
|
|
if (revision === lastHighScoreRevision) {
|
|
return;
|
|
}
|
|
const bytes = new Uint8Array(
|
|
wasm_exports.tdkpin_browser_high_score_length(),
|
|
);
|
|
for (let index = 0; index < bytes.length; index += 1) {
|
|
bytes[index] = wasm_exports.tdkpin_browser_high_score_byte(index);
|
|
}
|
|
|
|
highScoreRequestInFlight = true;
|
|
try {
|
|
const response = await fetch(highScoreApi, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: decoder.decode(bytes),
|
|
keepalive: true,
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`high-score submission failed (${response.status})`);
|
|
}
|
|
const json = await response.text();
|
|
highScoreGeneration += 1;
|
|
deliverHighScores(json);
|
|
wasm_exports.tdkpin_browser_high_score_ack(revision);
|
|
lastHighScoreRevision = revision;
|
|
highScoreSubmissionWarningShown = false;
|
|
} catch (error) {
|
|
if (!highScoreSubmissionWarningShown) {
|
|
console.warn("shared high-score submission failed; will retry", error);
|
|
highScoreSubmissionWarningShown = true;
|
|
}
|
|
} finally {
|
|
highScoreRequestInFlight = false;
|
|
}
|
|
}
|
|
|
|
function onInit() {
|
|
sendSavedDataToRust();
|
|
lastRevision = wasm_exports.tdkpin_browser_storage_save_revision();
|
|
void fetchHighScores();
|
|
window.setInterval(flushRustSave, 50);
|
|
window.setInterval(() => void flushHighScoreSubmission(), 50);
|
|
window.addEventListener("beforeunload", flushRustSave);
|
|
}
|
|
|
|
miniquad_add_plugin({
|
|
name: "tdkpin_storage",
|
|
on_init: onInit,
|
|
version: 1,
|
|
});
|
|
})();
|