UNPKG

@kitten-science/kitten-scientists

Version:

Add-on for the wonderful incremental browser game: https://kittensgame.com/web/

93 lines 4.5 kB
import { sleep } from "@oliversalzburg/js-utils/async/async.js"; import { isNil, mustExist } from "@oliversalzburg/js-utils/data/nil.js"; import { cl } from "./tools/Log.js"; export const FallbackLocale = "en-US"; // How long to wait for KG to load, in milliseconds. const TIMEOUT_DEFAULT = 2 * 60 * 1000; // Allows the user to define a timeout override in their browser's web storage. // This allows users to extend the timeout period, in case their local configuration // requires it. const TIMEOUT_OVERRIDE = "localStorage" in globalThis && !isNil(localStorage["ks.timeout"]) ? Number(localStorage["ks.timeout"]) : undefined; export class UserScriptLoader { /** * Stores if we caught the `game/start` signal from the game. */ _gameStartSignal; _gameStartSignalResolver; _possibleEngineState; static tryEngineStateFromSaveData(saveDataKey, saveData) { const saveDataProxy = saveData; if (!(saveDataKey in saveDataProxy)) { console.debug(...cl(`Failed: \`${saveDataKey}\` not found in save data.`)); return undefined; } const ksData = saveDataProxy.ks; if (!("state" in ksData)) { console.debug(...cl(`Failed: \`${saveDataKey}.state\` not found in save data.`)); return undefined; } const state = ksData.state; if (!Array.isArray(state)) { console.debug(...cl(`Failed: \`${saveDataKey}.state\` not \`Array\`.`)); return undefined; } return state[0]; } async waitForGame(UserScript, saveDataKey, timeout = TIMEOUT_OVERRIDE ?? TIMEOUT_DEFAULT) { if (UserScriptLoader._isGameLoaded()) { const game = mustExist(UserScriptLoader.window.game); const i18nEngine = mustExist(UserScriptLoader.window.$I); const gameLanguage = localStorage["com.nuclearunicorn.kittengame.language"]; return new UserScript(game, i18nEngine, gameLanguage, this._possibleEngineState); } const signals = [sleep(2000)]; if (isNil(this._gameStartSignal) && typeof UserScriptLoader.window.dojo !== "undefined") { this._gameStartSignal = new Promise(resolve => { this._gameStartSignalResolver = resolve; }); const subGameStart = UserScriptLoader.window.dojo.subscribe("game/start", () => { console.debug(...cl(`'game/start' signal caught. Fast-tracking script load for '${saveDataKey}'...`)); mustExist(this._gameStartSignalResolver)(true); UserScriptLoader.window.dojo.unsubscribe(subGameStart); }); if (saveDataKey !== undefined) { const subServerLoad = UserScriptLoader.window.dojo.subscribe("server/load", (saveData) => { console.info(...cl(`'server/load' signal caught. Looking for script state with key '${saveDataKey}' in save data...`)); const state = UserScriptLoader.tryEngineStateFromSaveData(saveDataKey, saveData); if (!state) { console.info(...cl(`The Kittens Game save data did not contain a script state for '${saveDataKey}'.`)); return; } console.info(...cl(`Found key '${saveDataKey}'! Provided save data will be used as seed for next script instance.`)); this._possibleEngineState = state; UserScriptLoader.window.dojo.unsubscribe(subServerLoad); }); } } if (!isNil(this._gameStartSignal)) { signals.push(this._gameStartSignal); } if (timeout < 0) { throw new Error("Unable to find game. Giving up. Maybe the game is not exported at `window.game`?"); } console.debug(...cl(`Waiting for game... (timeout: ${Math.round(timeout / 1000)}s)`)); await Promise.race(signals); return this.waitForGame(UserScript, saveDataKey, timeout - 2000); } static _isGameLoaded() { return (!isNil(UserScriptLoader.window.game) && (UserScriptLoader.window.document.getElementById("game")?.checkVisibility() ?? false) && !isNil(UserScriptLoader.window.$I)); } static get window() { try { return mustExist(unsafeWindow); } catch (_error) { return window; } } } //# sourceMappingURL=UserScriptLoader.js.map