Skip to content

Commit 4a95289

Browse files
authored
Merge branch 'ebkr:develop' into develop
2 parents 16ae488 + 447b733 commit 4a95289

File tree

1 file changed

+69
-89
lines changed

1 file changed

+69
-89
lines changed

src/r2mm/manager/SettingsDexieStore.ts

Lines changed: 69 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
import Dexie from 'dexie';
2-
import ManagerSettings from '../../r2mm/manager/ManagerSettings';
32
import Game from '../../model/game/Game';
43
import EnumResolver from '../../model/enums/_EnumResolver';
54
import { SortNaming } from '../../model/real_enums/sort/SortNaming';
65
import { SortDirection } from '../../model/real_enums/sort/SortDirection';
76
import { SortLocalDisabledMods } from '../../model/real_enums/sort/SortLocalDisabledMods';
8-
import GameManager from '../../model/game/GameManager';
97
import { StorePlatform } from '../../model/game/StorePlatform';
108
import { GameSelectionViewMode } from '../../model/enums/GameSelectionViewMode';
9+
import GameManager from '../../model/game/GameManager'
1110

1211
export const SETTINGS_DB_NAME = "settings";
1312

1413
export default class SettingsDexieStore extends Dexie {
1514

16-
global: Dexie.Table<SettingsInterface, number>;
17-
gameSpecific: Dexie.Table<SettingsInterface, number>;
1815
activeGame: Game;
16+
games: Dexie.Table<GameSettingsInterface, string>;
17+
global: Dexie.Table<SettingsInterface, number>;
1918

2019
constructor(game: Game) {
2120
super(SETTINGS_DB_NAME);
@@ -24,66 +23,73 @@ export default class SettingsDexieStore extends Dexie {
2423
});
2524

2625
const store = {
27-
value: `++id,settings`
26+
value: `++id,settings`,
27+
games: `identifier,settings`,
2828
} as any;
2929

30-
GameManager.gameList
31-
.forEach(value => {
32-
store[value.settingsIdentifier] = `++id,settings`;
33-
});
30+
// Setup the v3 (>= 75) dexie store.
31+
// This contains the following tables:
32+
// - `games`: (settingsIdentifier,settings) <-- Game-specific settings.
33+
// - `value`: (++id, settings) <-- Global settings.
34+
this.version(75).stores(store).upgrade(async (tx) => {
35+
// Migrate the current game's legacy (v2) table to the v3 schema, if applicable - versions < 75.
36+
//
37+
// v2 (whose types we still utilize) stored game-specific settings in individual tables.
38+
// This worked but required us to bump the Dexie store version every time we added a new game. Yuck!
39+
// To get around this we have moved all game settings into the `games` table, where the key
40+
// is the settings identifier of the game, and the value is a JSON string.
41+
for (const game of GameManager.gameList) {
42+
let gameTable;
43+
44+
try {
45+
gameTable = tx.table(game.settingsIdentifier);
46+
} catch {
47+
continue;
48+
}
3449

35-
// Add all games to store. Borked v2-3 locally
36-
// Increment per game or change to settings.
37-
this.version(74).stores(store);
50+
const legacyEntry = await gameTable.toCollection().first();
51+
if (legacyEntry === undefined) {
52+
continue;
53+
}
54+
55+
// If the legacy game table exists AND it contains a valid settings field, write it to the games table.
56+
// Then clear the contents of the legacy game table.
57+
await this.games.put({ identifier: gameTable.name, settings: legacyEntry.settings });
58+
await gameTable.clear();
59+
}
60+
})
3861

3962
this.activeGame = game;
4063
this.global = this.table("value");
41-
this.gameSpecific = this.table(game.settingsIdentifier);
64+
this.games = this.table("games");
4265
}
4366

4467
public async getLatestGlobal(): Promise<ManagerSettingsInterfaceGlobal_V2> {
45-
return this.global.toArray().then(async result => {
46-
if (result.length > 0) {
47-
const globalEntry = result[result.length - 1];
48-
const parsed = JSON.parse(globalEntry.settings);
49-
if ((parsed as ManagerSettingsInterfaceGlobal_V2).version) {
50-
// Is modern (at least V2).
51-
return parsed;
52-
} else {
53-
// Is legacy.
54-
const legacyToV2 = this.mapLegacyToV2(parsed, this.activeGame);
55-
await this.global.put({ settings: JSON.stringify(legacyToV2.global) });
56-
await this.gameSpecific.put({ settings: JSON.stringify(legacyToV2.gameSpecific) });
57-
return legacyToV2.global;
58-
}
59-
} else {
60-
ManagerSettings.NEEDS_MIGRATION = true;
61-
const obj = this.createNewSettingsInstance();
62-
await this.global.put({ settings: JSON.stringify(obj.global) });
63-
await this.gameSpecific.put({ settings: JSON.stringify(obj.gameSpecific) });
64-
return obj.global;
65-
}
66-
});
68+
const global = await this.global.get(1);
69+
70+
// Create the global settings row if it does not already exist.
71+
if (global === undefined) {
72+
const newSettings = this.createNewSettingsInstance();
73+
await this.global.put({ settings: JSON.stringify(newSettings.global) });
74+
return newSettings.global;
75+
}
76+
77+
// Otherwise parse and return the settings field.
78+
return JSON.parse(global.settings);
6779
}
6880

6981
public async getLatestGameSpecific(): Promise<ManagerSettingsInterfaceGame_V2> {
70-
return this.gameSpecific.toArray().then(async result => {
71-
if (result.length > 0) {
72-
const globalEntry = result[result.length - 1];
73-
const parsed = JSON.parse(globalEntry.settings);
74-
if ((parsed as ManagerSettingsInterfaceGame_V2).version === 2) {
75-
// Is modern (at least V2).
76-
return parsed;
77-
} else {
78-
// Placeholder for future migration
79-
return;
80-
}
81-
} else {
82-
const obj = this.createNewSettingsInstance();
83-
await this.gameSpecific.put({ settings: JSON.stringify(obj.gameSpecific) });
84-
return obj.gameSpecific;
85-
}
86-
});
82+
const identifier = this.activeGame.settingsIdentifier;
83+
const game = await this.games.get(identifier);
84+
85+
if (game !== undefined) {
86+
return JSON.parse(game.settings);
87+
}
88+
89+
let newGame = this.createNewSettingsInstance();
90+
await this.games.put({ identifier: identifier, settings: JSON.stringify(newGame.gameSpecific) });
91+
92+
return newGame.gameSpecific;
8793
}
8894

8995
public async getLatest(): Promise<ManagerSettingsInterfaceHolder> {
@@ -96,7 +102,7 @@ export default class SettingsDexieStore extends Dexie {
96102
};
97103
};
98104

99-
return await this.transaction("rw!", this.global, this.gameSpecific, get);
105+
return await this.transaction("rw!", this.global, this.games, get);
100106
}
101107

102108
private createNewSettingsInstance(): ManagerSettingsInterfaceHolder {
@@ -130,57 +136,31 @@ export default class SettingsDexieStore extends Dexie {
130136

131137
public async save(holder: ManagerSettingsInterfaceHolder) {
132138
const update = async () => {
139+
// Update global settings.
133140
await this.global.toArray().then(async result => {
134141
for (let settingsInterface of result) {
135142
await this.global.update(settingsInterface.id!, {settings: JSON.stringify(holder.global)});
136143
}
137144
});
138-
await this.gameSpecific.toArray().then(async result => {
139-
for (let settingsInterface of result) {
140-
await this.gameSpecific.update(settingsInterface.id!, {settings: JSON.stringify(holder.gameSpecific)});
141-
}
142-
});
143-
}
144145

145-
await this.transaction("rw!", this.global, this.gameSpecific, update);
146-
}
147-
148-
private mapLegacyToV2(itf: ManagerSettingsInterface_Legacy, game: Game): ManagerSettingsInterfaceHolder {
149-
return {
150-
global: {
151-
darkTheme: itf.darkTheme,
152-
dataDirectory: itf.dataDirectory,
153-
expandedCards: itf.expandedCards,
154-
funkyModeEnabled: itf.funkyModeEnabled,
155-
ignoreCache: itf.ignoreCache,
156-
steamDirectory: itf.steamDirectory,
157-
lastSelectedGame: null,
158-
version: 2,
159-
favouriteGames: [],
160-
defaultGame: undefined,
161-
defaultStore: undefined,
162-
gameSelectionViewMode: GameSelectionViewMode.CARD
163-
},
164-
gameSpecific: {
165-
version: 2,
166-
gameDirectory: game.displayName === "Risk of Rain 2" ? itf.riskOfRain2Directory : null,
167-
installedDisablePosition: itf.installedDisablePosition,
168-
installedSortBy: itf.installedDisablePosition,
169-
installedSortDirection: itf.installedSortDirection,
170-
lastSelectedProfile: itf.lastSelectedProfile,
171-
launchParameters: itf.launchParameters,
172-
linkedFiles: itf.linkedFiles
173-
}
146+
// Update the active game's settings.
147+
await this.games.put({ identifier: this.activeGame.settingsIdentifier, settings: JSON.stringify(holder.gameSpecific) });
174148
}
175-
}
176149

150+
await this.transaction("rw!", this.global, this.games, update);
151+
}
177152
}
178153

179154
interface SettingsInterface {
180155
id?: number;
181156
settings: string;
182157
}
183158

159+
interface GameSettingsInterface {
160+
identifier: string;
161+
settings: string;
162+
}
163+
184164
/**
185165
* Legacy interface as manager was designed to originally only support Risk of Rain 2.
186166
* Expanding supported games means that the "riskOfRain2Directory" setting should no longer be global.

0 commit comments

Comments
 (0)