1
1
import Dexie from 'dexie' ;
2
- import ManagerSettings from '../../r2mm/manager/ManagerSettings' ;
3
2
import Game from '../../model/game/Game' ;
4
3
import EnumResolver from '../../model/enums/_EnumResolver' ;
5
4
import { SortNaming } from '../../model/real_enums/sort/SortNaming' ;
6
5
import { SortDirection } from '../../model/real_enums/sort/SortDirection' ;
7
6
import { SortLocalDisabledMods } from '../../model/real_enums/sort/SortLocalDisabledMods' ;
8
- import GameManager from '../../model/game/GameManager' ;
9
7
import { StorePlatform } from '../../model/game/StorePlatform' ;
10
8
import { GameSelectionViewMode } from '../../model/enums/GameSelectionViewMode' ;
9
+ import GameManager from '../../model/game/GameManager'
11
10
12
11
export const SETTINGS_DB_NAME = "settings" ;
13
12
14
13
export default class SettingsDexieStore extends Dexie {
15
14
16
- global : Dexie . Table < SettingsInterface , number > ;
17
- gameSpecific : Dexie . Table < SettingsInterface , number > ;
18
15
activeGame : Game ;
16
+ games : Dexie . Table < GameSettingsInterface , string > ;
17
+ global : Dexie . Table < SettingsInterface , number > ;
19
18
20
19
constructor ( game : Game ) {
21
20
super ( SETTINGS_DB_NAME ) ;
@@ -24,66 +23,73 @@ export default class SettingsDexieStore extends Dexie {
24
23
} ) ;
25
24
26
25
const store = {
27
- value : `++id,settings`
26
+ value : `++id,settings` ,
27
+ games : `identifier,settings` ,
28
28
} as any ;
29
29
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
+ }
34
49
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
+ } )
38
61
39
62
this . activeGame = game ;
40
63
this . global = this . table ( "value" ) ;
41
- this . gameSpecific = this . table ( game . settingsIdentifier ) ;
64
+ this . games = this . table ( "games" ) ;
42
65
}
43
66
44
67
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 ) ;
67
79
}
68
80
69
81
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 ;
87
93
}
88
94
89
95
public async getLatest ( ) : Promise < ManagerSettingsInterfaceHolder > {
@@ -96,7 +102,7 @@ export default class SettingsDexieStore extends Dexie {
96
102
} ;
97
103
} ;
98
104
99
- return await this . transaction ( "rw!" , this . global , this . gameSpecific , get ) ;
105
+ return await this . transaction ( "rw!" , this . global , this . games , get ) ;
100
106
}
101
107
102
108
private createNewSettingsInstance ( ) : ManagerSettingsInterfaceHolder {
@@ -130,57 +136,31 @@ export default class SettingsDexieStore extends Dexie {
130
136
131
137
public async save ( holder : ManagerSettingsInterfaceHolder ) {
132
138
const update = async ( ) => {
139
+ // Update global settings.
133
140
await this . global . toArray ( ) . then ( async result => {
134
141
for ( let settingsInterface of result ) {
135
142
await this . global . update ( settingsInterface . id ! , { settings : JSON . stringify ( holder . global ) } ) ;
136
143
}
137
144
} ) ;
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
- }
144
145
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 ) } ) ;
174
148
}
175
- }
176
149
150
+ await this . transaction ( "rw!" , this . global , this . games , update ) ;
151
+ }
177
152
}
178
153
179
154
interface SettingsInterface {
180
155
id ?: number ;
181
156
settings : string ;
182
157
}
183
158
159
+ interface GameSettingsInterface {
160
+ identifier : string ;
161
+ settings : string ;
162
+ }
163
+
184
164
/**
185
165
* Legacy interface as manager was designed to originally only support Risk of Rain 2.
186
166
* Expanding supported games means that the "riskOfRain2Directory" setting should no longer be global.
0 commit comments