Skip to content
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

[Feat] Add support for Sideloading Browser Apps and Games #2739

Merged
merged 12 commits into from
May 29, 2023
4 changes: 3 additions & 1 deletion public/locales/en/gamepage.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,15 @@
"title": "Title"
},
"info": {
"broser": "BrowserURL",
"exe": "Select Executable",
"image": "App Image",
"title": "Game/App Title"
},
"placeholder": {
"image": "Paste an Image URL here",
"title": "Add a title to your Game/App"
"title": "Add a title to your Game/App",
"url": "Paste the Game URL here"
}
},
"specs": {
Expand Down
1 change: 1 addition & 0 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@
"prefered_language": "2-char code (i.e.: \"en\" or \"fr\")"
},
"platforms": {
"browser": "Browser",
"linux": "Linux",
"mac": "Mac",
"win": "Windows"
Expand Down
4 changes: 2 additions & 2 deletions src/backend/api/downloadmanager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const install = async (args: InstallParams) => {
startTime: 0
}

await ipcRenderer.invoke('addToDMQueue', dmQueueElement)
ipcRenderer.invoke('addToDMQueue', dmQueueElement)
Copy link
Member Author

Choose a reason for hiding this comment

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

not a related change, just a small fix to my previous PR.


// Add Dlcs to the queue
if (Array.isArray(args.installDlcs) && args.installDlcs.length > 0) {
Expand All @@ -28,7 +28,7 @@ export const install = async (args: InstallParams) => {
endTime: 0,
startTime: 0
}
await ipcRenderer.invoke('addToDMQueue', dlcQueueElement)
ipcRenderer.invoke('addToDMQueue', dlcQueueElement)
})
}
}
Expand Down
51 changes: 2 additions & 49 deletions src/backend/storeManagers/sideload/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import {
} from 'common/types'
import { libraryStore } from './electronStores'
import { GameConfig } from '../../game_config'
import { isWindows, isMac, isLinux, icon } from '../../constants'
import { isWindows, isMac, isLinux } from '../../constants'
import { killPattern, shutdownWine } from '../../utils'
import { logInfo, LogPrefix, logWarning } from '../../logger/logger'
import path, { dirname, resolve } from 'path'
import { dirname } from 'path'
import { existsSync, rmSync } from 'graceful-fs'
import i18next from 'i18next'
import {
Expand All @@ -20,11 +20,9 @@ import {
} from '../../shortcuts/shortcuts/shortcuts'
import { notify } from '../../dialog/dialog'
import { sendFrontendMessage } from '../../main_window'
import { app, BrowserWindow } from 'electron'
import { launchGame } from 'backend/storeManagers/storeManagerCommon/games'
import { GOGCloudSavesLocation } from 'common/types/gog'
import { InstallResult, RemoveArgs } from 'common/types/game_manager'
const buildDir = resolve(__dirname, '../../build')

export function getGameInfo(appName: string): GameInfo {
const store = libraryStore.get('games', [])
Expand Down Expand Up @@ -67,51 +65,6 @@ export function isGameAvailable(appName: string): boolean {
return false
}

if (Object.hasOwn(app, 'on'))
app.on('web-contents-created', (_, contents) => {
// Check for a webview
if (contents.getType() === 'webview') {
contents.setWindowOpenHandler(({ url }) => {
const protocol = new URL(url).protocol
if (['https:', 'http:'].includes(protocol)) {
openNewBrowserGameWindow(url)
}
return { action: 'deny' }
})
}
})

const openNewBrowserGameWindow = async (
browserUrl: string
): Promise<boolean> => {
return new Promise((res) => {
const browserGame = new BrowserWindow({
icon: icon,
webPreferences: {
webviewTag: true,
contextIsolation: true,
nodeIntegration: true,
preload: path.join(__dirname, 'preload.js')
}
})

const url = !app.isPackaged
? 'http://localhost:5173?view=BrowserGame&browserUrl=' +
encodeURIComponent(browserUrl)
: `file://${path.join(
buildDir,
'./index.html?view=BrowserGame&browserUrl=' +
encodeURIComponent(browserUrl)
)}`

browserGame.loadURL(url)
setTimeout(() => browserGame.focus(), 200)
browserGame.on('close', () => {
res(true)
})
})
}

export async function launch(
appName: string,
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
Expand Down
35 changes: 17 additions & 18 deletions src/backend/storeManagers/storeManagerCommon/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { GameInfo, GameSettings, Runner } from 'common/types'
import { GameConfig } from '../../game_config'
import { isMac, isLinux, gamesConfigPath, icon } from '../../constants'
import { logInfo, LogPrefix, logWarning } from '../../logger/logger'
import path, { dirname, join, resolve } from 'path'
import { dirname, join } from 'path'
import { appendFileSync, constants as FS_CONSTANTS } from 'graceful-fs'
import i18next from 'i18next'
import {
Expand All @@ -17,9 +17,8 @@ import { access, chmod } from 'fs/promises'
import shlex from 'shlex'
import { showDialogBoxModalAuto } from '../../dialog/dialog'
import { createAbortController } from '../../utils/aborthandler/aborthandler'
import { app, BrowserWindow } from 'electron'
import { BrowserWindow } from 'electron'
import { gameManagerMap } from '../index'
const buildDir = resolve(__dirname, '../../build')

async function getAppSettings(appName: string): Promise<GameSettings> {
return (
Expand All @@ -33,30 +32,30 @@ export function logFileLocation(appName: string) {
}

const openNewBrowserGameWindow = async (
browserUrl: string
browserUrl: string,
abortController: AbortController
): Promise<boolean> => {
const hostname = new URL(browserUrl).hostname

return new Promise((res) => {
const browserGame = new BrowserWindow({
icon: icon,
fullscreen: true,
webPreferences: {
partition: `persist:${hostname}`,
webviewTag: true,
contextIsolation: true,
nodeIntegration: true,
preload: path.join(__dirname, 'preload.js')
nodeIntegration: true
}
})

const url = !app.isPackaged
? 'http://localhost:5173?view=BrowserGame&browserUrl=' +
encodeURIComponent(browserUrl)
: `file://${path.join(
buildDir,
'./index.html?view=BrowserGame&browserUrl=' +
encodeURIComponent(browserUrl)
)}`

browserGame.loadURL(url)
setTimeout(() => browserGame.focus(), 200)
browserGame.loadURL(browserUrl)
browserGame.on('ready-to-show', () => browserGame.show())

abortController.signal.addEventListener('abort', () => {
browserGame.close()
})

browserGame.on('close', () => {
res(true)
})
Expand Down Expand Up @@ -87,7 +86,7 @@ export async function launchGame(
}

if (browserUrl) {
return openNewBrowserGameWindow(browserUrl)
return openNewBrowserGameWindow(browserUrl, createAbortController(appName))
}

const gameSettings = await getAppSettings(appName)
Expand Down
14 changes: 14 additions & 0 deletions src/frontend/components/UI/PlatformFilter/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { faApple, faLinux, faWindows } from '@fortawesome/free-brands-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import ContextProvider from 'frontend/state/ContextProvider'
import './index.css'
import { faGlobe } from '@fortawesome/free-solid-svg-icons'

export default function PlatformFilter() {
const { t } = useTranslation()
Expand Down Expand Up @@ -77,6 +78,19 @@ export default function PlatformFilter() {
/>
</button>
)}
<button
onClick={() => handlePlatformFilter('browser')}
className={cx('FormControl__button', {
active: filterPlatform === 'browser'
})}
title={`${t('header.platform')}: ${t('platforms.browser')}`}
>
<FontAwesomeIcon
className="FormControl__segmentedFaIcon"
icon={faGlobe}
tabIndex={-1}
/>
</button>
</FormControl>
</div>
)
Expand Down
4 changes: 3 additions & 1 deletion src/frontend/screens/Game/GamePage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@ export default React.memo(function GamePage(): JSX.Element | null {
extraInfo?.about?.description ||
t('generic.noDescription', 'No description available')

const showReportIssue = is_installed && installPlatform !== 'Browser'

return (
<div className="gameConfigContainer">
{gameInfo.runner !== 'sideload' && showModal.show && (
Expand Down Expand Up @@ -729,7 +731,7 @@ export default React.memo(function GamePage(): JSX.Element | null {
{getButtonLabel()}
</button>
)}
{is_installed && (
{showReportIssue && (
<span
onClick={() => setIsSettingsModalOpen(true, 'log', gameInfo)}
className="clickable reportProblem"
Expand Down
5 changes: 4 additions & 1 deletion src/frontend/screens/Library/components/GameCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,10 @@ const GameCard = ({
{
label: t('submenu.logs', 'Logs'),
onclick: () => setIsSettingsModalOpen(true, 'log', gameInfo),
show: isInstalled && !isUninstalling
show:
isInstalled &&
!isUninstalling &&
gameInfo.install.platform !== 'Browser'
},
{
// hide
Expand Down