Skip to content

[GOG]: Switch to Galaxy way of loading library #2759

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions public/locales/en/gamepage.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
},
"label": {
"game": {
"not-installable-game": "",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a default text here.

"third-party-game": "Third-Party Game NOT Supported"
},
"launching": "Launching",
Expand Down Expand Up @@ -187,6 +188,8 @@
"downloading": "Downloading",
"extracting": "Extracting",
"gameNotAvailable": "Game not available",
"gog-goodie": "This game doesn't appear to be installable. Check downloadable content on https://gog.com/account",
"goodie": "Not installable",
"hasUpdates": "New Version Available!",
"installed": "Installed",
"installing": "Installing",
Expand Down
4 changes: 4 additions & 0 deletions src/backend/api/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ export const handleRefreshLibrary = (
callback: (event: Electron.IpcRendererEvent, runner: Runner) => void
) => ipcRenderer.on('refreshLibrary', callback)

export const handleGamePush = (
callback: (event: Electron.IpcRendererEvent, game: GameInfo) => void
) => ipcRenderer.on('pushGameToLibrary', callback)

export const removeRecentGame = async (appName: string) =>
ipcRenderer.invoke('removeRecent', appName)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down
51 changes: 38 additions & 13 deletions src/backend/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import Store from 'electron-store'

export default class CacheStore<ValueType, KeyType extends string = string> {
private readonly store: Store
private in_memory_store: Map<string, ValueType>
private using_in_memory: boolean
private current_store: Store | Map<string, ValueType>
private readonly lifespan: number | null

/**
Expand All @@ -16,20 +19,34 @@ export default class CacheStore<ValueType, KeyType extends string = string> {
name: filename,
clearInvalidConfig: true
})
this.in_memory_store = new Map<string, ValueType>()
this.using_in_memory = false
this.current_store = this.store
this.lifespan = max_value_lifespan
}

/**
* Allows to switch over to use in memory store (useful for many read and writes)
* IMPORTANT! after operations run commit() to update file on drive
*/
public use_in_memory() {
// Mirror store to memory map
this.using_in_memory = true
this.in_memory_store = new Map(this.store) as Map<string, ValueType>
this.current_store = this.in_memory_store
}

public get(key: KeyType): ValueType | undefined
public get(key: KeyType, fallback: ValueType): ValueType
public get(key: KeyType, fallback?: ValueType) {
if (!this.store.has(key)) {
if (!this.current_store.has(key)) {
return fallback
}

if (this.lifespan) {
const lastUpdateTimestamp = this.store.get(`__timestamp.${key}`) as
| string
| undefined
const lastUpdateTimestamp = this.current_store.get(
`__timestamp.${key}`
) as string | undefined
if (!lastUpdateTimestamp) {
return fallback
}
Expand All @@ -38,28 +55,36 @@ export default class CacheStore<ValueType, KeyType extends string = string> {
const msSinceUpdate = Date.now() - updateDate.getTime()
const minutesSinceUpdate = msSinceUpdate / 1000 / 60
if (minutesSinceUpdate > this.lifespan) {
this.store.delete(key)
this.store.delete(`__timestamp.${key}`)
this.current_store.delete(key)
this.current_store.delete(`__timestamp.${key}`)
return fallback
}
}

return this.store.get(key) as ValueType
return this.current_store.get(key) as ValueType
}

public set(key: KeyType, value: ValueType) {
this.store.set(key, value)
this.store.set(`__timestamp.${key}`, Date())
this.current_store.set(key, value)
this.current_store.set(`__timestamp.${key}`, Date() as ValueType)
}

public delete(key: KeyType) {
this.store.delete(key)
this.store.delete(`__timestamp.${key}`)
this.current_store.delete(key)
this.current_store.delete(`__timestamp.${key}`)
}

public clear = () => this.store.clear()
public clear = () => this.current_store.clear()

public has(key: string) {
return this.store.has(key)
return this.current_store.has(key)
}

public commit() {
if (this.using_in_memory) {
this.store.store = Object.fromEntries(this.in_memory_store)
this.using_in_memory = false
this.current_store = this.store
}
}
}
5 changes: 1 addition & 4 deletions src/backend/storeManagers/gog/electronStores.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ const configStore = new TypeCheckedStoreBackend('gogConfigStore', {
cwd: 'gog_store'
})

const apiInfoCache = new CacheStore<{
isUpdated: boolean
data: GamesDBData
}>('gog_api_info')
const apiInfoCache = new CacheStore<GamesDBData>('gog_api_info')
const libraryStore = new CacheStore<GameInfo[], 'games'>('gog_library', null)
const syncStore = new TypeCheckedStoreBackend('gogSyncStore', {
cwd: 'gog_store',
Expand Down
5 changes: 3 additions & 2 deletions src/backend/storeManagers/gog/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
createReqsArray,
getGameInfo as getGogLibraryGameInfo,
changeGameInstallPath,
getMetaResponse
getMetaResponse,
getGamesData
} from './library'
import { join } from 'path'
import { GameConfig } from '../../game_config'
Expand Down Expand Up @@ -80,7 +81,7 @@ export async function getExtraInfo(appName: string): Promise<ExtraInfo> {
const extra: ExtraInfo = {
about: gameInfo.extra?.about,
reqs: await createReqsArray(appName, targetPlatform),
storeUrl: gameInfo.store_url
storeUrl: (await getGamesData(appName))?._links.store.href
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this works, nice. But I still think that we should solve promises before defining the values of this object, for both this and the previous key.
It is more readable and safer imo.

}
return extra
}
Expand Down
Loading