Skip to content
This repository has been archived by the owner on Oct 25, 2022. It is now read-only.

Commit

Permalink
feat(core): Always enable save game manager
Browse files Browse the repository at this point in the history
We no longer use our own internal dirty check and now just always install the save manager and save with the game.

The experimental engine state persistence is now always active. Loading is prevented for now.
The legacy storage is always written as well and is still used to restore previous settings.
  • Loading branch information
oliversalzburg committed Oct 17, 2022
1 parent 62cbec5 commit 7078e8a
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 81 deletions.
124 changes: 56 additions & 68 deletions packages/userscript/source/UserScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,6 @@ export class UserScript {
return this._language;
}

/**
* Signals whether the options have been changed since they were last saved.
*/
private _settingsDirty = false;
private _intervalSaveSettings: number | undefined = undefined;

/**
* Stores if we caught the `game/start` signal from the game.
*/
Expand All @@ -90,9 +84,6 @@ export class UserScript {
this._userInterface = new UserInterface(this);
this._userInterface.construct();
this._userInterface.refreshUi();

// Every 30 seconds, check if we need to save our settings.
this._intervalSaveSettings = setInterval(this._checkSettings.bind(this), 30 * 1000);
}

/**
Expand Down Expand Up @@ -146,35 +137,6 @@ export class UserScript {
});
}

/**
* Signal that the settings should be saved again.
*
* @param updater A function that will manipulate the settings before they're saved.
*/
updateSettings(updater?: () => void): void {
cdebug("Settings will be updated.");
if (updater) {
updater();
}
this._settingsDirty = true;
}

private _checkSettings(): void {
if (this._settingsDirty) {
this.saveSettings();
}
}

saveSettings() {
this._settingsDirty = false;

SettingsStorage.setLegacyOptions(this.getLegacyOptions());
cinfo("Kitten Scientists settings (legacy) saved.");

SettingsStorage.setSettings(this.getSettings());
cinfo("Kitten Scientists settings (modern) saved.");
}

getLegacyOptions() {
return Options.asLegacyOptions({
bonfire: this.engine.bonfireManager.settings,
Expand All @@ -193,12 +155,12 @@ export class UserScript {
return this.engine.stateSerialize();
}
setSettings(settings: EngineState) {
cinfo("Loading engine state...");
this.engine.stateLoad(settings);
}

installSaveManager() {
cinfo("Replacing internal save management with game manager...");
clearInterval(this._intervalSaveSettings);
cinfo("EXPERIMENTAL: Installing save game manager...");
this.gamePage.managers.push(this.saveManager);
}

Expand All @@ -215,30 +177,51 @@ export class UserScript {

private _saveManager = {
load: (saveData: Record<string, unknown>) => {
cwarn("EXPERIMENTAL: Looking for Kitten Scientists settings in save data...");
if ("ks" in saveData === false) {
return;
}
cwarn("EXPERIMENTAL: Looking for Kitten Scientists engine state in save data...");

const ksData = saveData.ks as { state?: Array<EngineState> };
if ("state" in ksData === false) {
const state = UserScript._tryEngineStateFromSaveData(saveData);
if (!state) {
return;
}

const state = ksData.state;
if (!Array.isArray(state)) {
return;
}

cwarn("EXPERIMENTAL: Loading Kitten Scientists settings from save data...");
this.engine.stateLoad(state[0]);
cwarn(
"EXPERIMENTAL: Found Kitten Scientists engine state in save data. These will NOT be loaded at this time."
);
//this.engine.stateLoad(state);
},
save: (saveData: Record<string, unknown>) => {
cwarn("EXPERIMENTAL: Injecting Kitten Scientists settings into save data...");
cwarn("EXPERIMENTAL: Injecting Kitten Scientists engine state into save data...");
saveData.ks = { state: [this.getSettings()] };

// Also save to legacy storage.
SettingsStorage.setLegacyOptions(this.getLegacyOptions());
cinfo("Kitten Scientists settings (legacy) saved.");
},
};

private static _tryEngineStateFromSaveData(
saveData: Record<string, unknown>
): EngineState | undefined {
if ("ks" in saveData === false) {
cdebug("Failed: `ks` not found in save data.");
return;
}

const ksData = saveData.ks as { state?: Array<EngineState> };
if ("state" in ksData === false) {
cdebug("Failed: `ks.state` not found in save data.");
return;
}

const state = ksData.state;
if (!Array.isArray(state)) {
cdebug("Failed: `ks.state` not `Array`.");
return;
}

return state[0];
}

static async waitForGame(timeout = 30000): Promise<GamePage> {
const signals: Array<Promise<unknown>> = [sleep(2000)];

Expand All @@ -256,24 +239,18 @@ export class UserScript {
"server/load",
(saveData: { ks?: { state?: Array<EngineState> } }) => {
cinfo(
"`server/load` signal caught. Looking for Kitten Scientists engine state in save data..."
"EXPERIMENTAL: `server/load` signal caught. Looking for Kitten Scientists engine state in save data..."
);
if ("ks" in saveData === false) {
return;
}

const ksData = saveData.ks as { state?: Array<EngineState> };
if ("state" in ksData === false) {
return;
}

const state = ksData.state;
if (!Array.isArray(state)) {
const state = UserScript._tryEngineStateFromSaveData(saveData);
if (!state) {
return;
}

cinfo("Using provided save data as seed for next userscript instance.");
UserScript._possibleEngineState = mustExist(mustExist(saveData.ks).state)[0];
cinfo(
"EXPERIMENTAL: Found! Provided save data will be used as seed for next userscript instance."
);
UserScript._possibleEngineState = state;
}
);
}
Expand All @@ -296,16 +273,27 @@ export class UserScript {
return UserScript.waitForGame(timeout - 2000);
}

/**
* Returns an instance of the userscript in our default confiuration.
*
* @returns The default userscript instance.
*/
static getDefaultInstance(): UserScript {
const instance = new UserScript(
mustExist(UserScript.window.gamePage),
mustExist(UserScript.window.$I),
localStorage["com.nuclearunicorn.kittengame.language"] as SupportedLanguages | undefined
);

// We can already attempt to load the possible engine state and see if this produces errors.
// As the startup is orchestrated right now by `index.ts`, if there are legacy options, they
// will be loaded into the instance after we return it from here.
// Thus, legacy options will overrule modern settings, if they are present.
if (!isNil(UserScript._possibleEngineState)) {
instance.setSettings(UserScript._possibleEngineState);
instance.installSaveManager();
}

instance.installSaveManager();
return instance;
}

Expand Down
7 changes: 3 additions & 4 deletions packages/userscript/source/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const devSavegame = KG_SAVEGAME ?? null;
const kittenGame = await UserScript.waitForGame();

const userScript = UserScript.getDefaultInstance();
userScript.installSaveManager();

// For development convenience, load a lategame save to give us more test options.
if (!isNil(devSavegame)) {
Expand All @@ -20,14 +19,14 @@ const devSavegame = KG_SAVEGAME ?? null;
// @ts-expect-error Manipulating global containers is naughty, be we want to expose the script host.
window.kittenScientists = userScript;

cinfo("Looking for legacy settings...");
cinfo("Looking for legacy options...");
const legacySettings = SettingsStorage.getLegacyOptions();

if (!isNil(legacySettings)) {
cinfo("Using restored legacy settings.");
cinfo("Using restored legacy options.");
userScript.loadLegacyOptions(legacySettings);
} else {
cinfo("No legacy settings found. Default settings will be used.");
cinfo("No legacy options found. Default configuration will be used.");
}

userScript.validateGame();
Expand Down
7 changes: 7 additions & 0 deletions packages/userscript/source/settings/SettingsStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ export class SettingsStorage {
static setLegacyOptions(options: LegacyStorage): void {
localStorage["cbc.kitten-scientists"] = JSON.stringify(options);
}

/**
* Persists an engine state to local storage.
*
* @deprecated Hook into the save/load mechanism of Kittens Game instead.
* @param settings The engine state to persist.
*/
static setSettings(settings: EngineState): void {
localStorage["ks.state.0"] = JSON.stringify(settings);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/userscript/source/ui/components/ConsumeButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class ConsumeButton extends UiComponent {
);

if (value !== null) {
host.updateSettings(() => (setting.consume = value));
setting.consume = value;
this.refreshUi();
}

Expand Down
2 changes: 0 additions & 2 deletions packages/userscript/source/ui/components/LimitedButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,9 @@ export class LimitedButton extends UiComponent {
checkbox.on("change", () => {
if (checkbox.is(":checked") && setting.limited === false) {
setting.limited = true;
host.updateSettings();
handler.onLimitedCheck();
} else if (!checkbox.is(":checked") && setting.limited === true) {
setting.limited = false;
host.updateSettings();
handler.onLimitedUnCheck();
}
});
Expand Down
2 changes: 1 addition & 1 deletion packages/userscript/source/ui/components/MaxButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class MaxButton extends UiComponent {
);

if (value !== null) {
host.updateSettings(() => (setting.max = value));
setting.max = value;
this.refreshUi();
}

Expand Down
4 changes: 2 additions & 2 deletions packages/userscript/source/ui/components/SettingListItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ export class SettingListItem<TSetting extends Setting = Setting> extends UiCompo

checkbox.on("change", () => {
if (checkbox.is(":checked") && setting.enabled === false) {
host.updateSettings(() => (setting.enabled = true));
setting.enabled = true;
handler.onCheck();
} else if (!checkbox.is(":checked") && setting.enabled === true) {
host.updateSettings(() => (setting.enabled = false));
setting.enabled = false;
handler.onUnCheck();
}
});
Expand Down
2 changes: 1 addition & 1 deletion packages/userscript/source/ui/components/StockButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class StockButton extends UiComponent {
);

if (value !== null) {
host.updateSettings(() => (setting.stock = value));
setting.stock = value;
this.refreshUi();
}

Expand Down
1 change: 0 additions & 1 deletion packages/userscript/source/ui/components/TriggerButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export class TriggerButton extends UiComponent {

if (value !== null) {
setting.trigger = value;
host.updateSettings();
this.refreshUi();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class TriggerLimitButton extends UiComponent {
);

if (value !== null) {
host.updateSettings(() => (setting.trigger = value));
setting.trigger = value;
this.refreshUi();
}

Expand Down

0 comments on commit 7078e8a

Please sign in to comment.