Skip to content

Commit

Permalink
[Feat] Add support for Sideloading Browser Apps and Games (#2739)
Browse files Browse the repository at this point in the history
* [Feat] Add Support for Browser Apps and Games

* i18n: labels

* feat: add browser button filter

* fix: merge

* chore: code cleanup

* fix: editing

* fix: macOS sideload icon

* fix: pr comments

* ui: hide logs buttons for web apps

* fix: dm dlc queue

* chore: test electron-security

* Revert "chore: test electron-security"

This reverts commit 9cd4755.
  • Loading branch information
flavioislima committed May 29, 2023
1 parent 413a28d commit dfb78b8
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 98 deletions.
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)

// 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

0 comments on commit dfb78b8

Please sign in to comment.