@@ -3,17 +3,18 @@ import { createErrorProxy } from '@shared/Util';
33import { SocketClient } from '@shared/back/SocketClient' ;
44import { BackIn , BackOut } from '@shared/back/types' ;
55import { APP_TITLE } from '@shared/constants' ;
6+ import { num } from '@shared/utils/Coerce' ;
67import { ChildProcess , fork } from 'child_process' ;
78import * as electron from 'electron' ;
89import { BrowserWindow , IpcMainEvent , app , dialog , ipcMain , session , shell } from 'electron' ;
9- import installExtension , { REACT_DEVELOPER_TOOLS , REDUX_DEVTOOLS } from 'electron-devtools-installer' ;
1010import { AppConfigData , AppPreferencesData } from 'flashpoint-launcher' ;
1111import * as fs from 'fs-extra' ;
1212import * as path from 'node:path' ;
1313import { argv } from 'process' ;
1414import { WebSocket } from 'ws' ;
1515import * as Util from './Util' ;
1616import { CustomIPC , WindowIPC } from './constants' ;
17+ import { createHeadlessServer } from './headless' ;
1718import { Init } from './types' ;
1819
1920const TIMEOUT_DELAY = 60_000 ;
@@ -29,11 +30,6 @@ const ALLOWED_HOSTS = [
2930 'cdn.discordapp.com' ,
3031] ;
3132
32- type LaunchOptions = {
33- backend : boolean ;
34- frontend : boolean ;
35- }
36-
3733type MainState = {
3834 window ?: BrowserWindow ;
3935 _installed ?: boolean ;
@@ -53,6 +49,12 @@ type MainState = {
5349 output : string ;
5450}
5551
52+ type LaunchComponents = {
53+ backend : boolean ;
54+ browserFrontend : boolean ;
55+ electronWindow : boolean ;
56+ }
57+
5658export function main ( init : Init ) : void {
5759 const state : MainState = {
5860 window : undefined ,
@@ -70,10 +72,19 @@ export function main(init: Init): void {
7072 output : '' ,
7173 } ;
7274
73- startup ( {
75+ const launchComponents : LaunchComponents = {
7476 backend : ! init . args [ 'host-remote' ] ,
75- frontend : ! init . args [ 'back-only' ] ,
76- } )
77+ browserFrontend : ! ! init . args [ 'browser-mode' ] && ! init . args [ 'back-only' ] ,
78+ electronWindow : ! init . args [ 'browser-mode' ] && ! init . args [ 'back-only' ] ,
79+ } ;
80+
81+ if ( ! launchComponents . backend && ! launchComponents . browserFrontend && ! launchComponents . electronWindow ) {
82+ console . log ( 'Arguments for startup mean no components would run, exiting...' ) ;
83+ process . exit ( ) ;
84+ return ;
85+ }
86+
87+ startup ( launchComponents )
7788 . catch ( ( error ) => {
7889 console . error ( error ) ;
7990 if ( ! Util . isDev ) {
@@ -87,24 +98,9 @@ export function main(init: Init): void {
8798 app . quit ( ) ;
8899 } ) ;
89100
90- // Needed for modern Electron?
91- // This is a workaround to ensure that the extension background workers are started
92- // If you are updating Electron, confirm if this is still needed
93- // https://github.com/electron/electron/issues/41613
94- // function launchExtensionBackgroundWorkers( appSession = session.defaultSession ) {
95- // return Promise.all(
96- // appSession.getAllExtensions().map( async ( extension ) => {
97- // const manifest = extension.manifest;
98- // if ( manifest.manifest_version === 3 && manifest?.background?.service_worker ) {
99- // await appSession.serviceWorkers.startWorkerForScope( extension.url );
100- // }
101- // } )
102- // );
103- // }
104-
105- // -- Functions --
106-
107- async function startup ( opts : LaunchOptions ) {
101+ async function startup ( opts : LaunchComponents ) {
102+ console . log ( JSON . stringify ( opts , undefined , 2 ) ) ;
103+ state . mainFolderPath = Util . getMainFolderPath ( ) ;
108104 // app.disableHardwareAcceleration();
109105
110106 // Single process
@@ -114,14 +110,16 @@ export function main(init: Init): void {
114110 return ;
115111 }
116112
117- // Add app event listener(s)
118- app . once ( 'ready' , onAppReady ) ;
119- app . once ( 'window-all-closed' , onAppWindowAllClosed ) ;
120113 app . once ( 'will-quit' , onAppWillQuit ) ;
114+ app . once ( 'ready' , onAppReady ) ;
121115 app . on ( 'web-contents-created' , onAppWebContentsCreated ) ;
122- app . on ( 'activate' , onAppActivate ) ;
123116 app . on ( 'second-instance' , onAppSecondInstance ) ;
124117 app . on ( 'open-url' , onAppOpenUrl ) ;
118+ app . on ( 'activate' , onAppActivate ) ;
119+ app . commandLine . appendSwitch ( 'ignore-connections-limit' , 'localhost' ) ;
120+
121+ // Prevent closure on Mac after window closes
122+ app . once ( 'window-all-closed' , onAppWindowAllClosed ) ;
125123
126124 // Add IPC event listener(s)
127125 ipcMain . on ( InitRendererChannel , onInit ) ;
@@ -202,29 +200,17 @@ export function main(init: Init): void {
202200 }
203201 } ) ;
204202
205- // Add Socket event listener(s)
206- state . socket . register ( BackOut . QUIT , ( ) => {
207- state . isQuitting = true ;
208- state . socket . allowDeath ( ) ;
209- app . quit ( ) ;
210- } ) ;
211-
212- app . commandLine . appendSwitch ( 'ignore-connections-limit' , 'localhost' ) ;
213-
214- state . mainFolderPath = Util . getMainFolderPath ( ) ;
215-
216203 // ---- Initialize ----
217- // Load custom version text file
218- await fs . promises . readFile ( path . join ( state . mainFolderPath , '.version' ) )
219- . then ( ( data ) => {
220- state . _version = ( data )
221- ? parseInt ( data . toString ( ) . replace ( / [ ^ \d ] / g, '' ) , 10 ) // (Remove all non-numerical characters, then parse it as a string)
222- : - 1 ; // (Version not found error code)
223- } )
224- . catch ( ( ) => { /** No file, ignore */ } ) ;
225204
226205 // Start backend
227206 if ( opts . backend ) {
207+ // Exit Electron app if the backend quits
208+ state . socket . register ( BackOut . QUIT , ( ) => {
209+ state . isQuitting = true ;
210+ state . socket . allowDeath ( ) ;
211+ app . quit ( ) ;
212+ } ) ;
213+
228214 await new Promise < void > ( ( resolve , reject ) => {
229215 // Fork backend, init.rest will contain possible flashpoint:// message
230216 // Increase memory limit in dev instance (mostly for developer page functions)
@@ -310,49 +296,47 @@ export function main(init: Init): void {
310296 // Update flashpoint:// protocol registration state
311297 setProtocolRegistrationState ( state . preferences . registerProtocol ) ;
312298 } ) ;
313- }
314299
315- // Open websocket to backend, for communication between front and back
316- const ws = await timeout < WebSocket > ( new Promise ( ( resolve , reject ) => {
317- const sock = new WebSocket ( state . backHost . href ) ;
318- sock . onclose = ( ) => { reject ( new Error ( 'Failed to authenticate connection to back.' ) ) ; } ;
319- sock . onerror = ( event ) => { reject ( event . error ) ; } ;
320- sock . onopen = ( ) => {
321- sock . onmessage = ( ) => {
322- sock . onclose = noop ;
323- sock . onerror = noop ;
324- resolve ( sock ) ;
300+ // Open websocket to backend, so we can get QUIT events
301+ const ws = await timeout < WebSocket > ( new Promise ( ( resolve , reject ) => {
302+ const sock = new WebSocket ( state . backHost . href ) ;
303+ sock . onclose = ( ) => { reject ( new Error ( 'Failed to authenticate connection to back.' ) ) ; } ;
304+ sock . onerror = ( event ) => { reject ( event . error ) ; } ;
305+ sock . onopen = ( ) => {
306+ sock . onmessage = ( ) => {
307+ sock . onclose = noop ;
308+ sock . onerror = noop ;
309+ resolve ( sock ) ;
310+ } ;
311+ sock . send ( 'flashpoint-launcher' ) ;
325312 } ;
326- sock . send ( 'flashpoint-launcher' ) ;
327- } ;
328- } ) , TIMEOUT_DELAY ) ;
329- state . socket . setSocket ( ws ) ;
330- state . socket . killOnDisconnect = true ;
313+ } ) , TIMEOUT_DELAY ) ;
314+ state . socket . setSocket ( ws ) ;
315+ state . socket . killOnDisconnect = true ;
316+ }
317+
318+ // Start appropriate frontend
319+ if ( opts . browserFrontend ) {
320+ await app . whenReady ( ) ;
321+
322+ // Create headless server
323+ const hostname = init . args [ 'browser-mode-host' ] || 'localhost' ;
324+ const port = init . args [ 'browser-mode-port' ] || 9000 ;
325+ const url = init . args [ 'browser-mode-url' ] || `http://${ hostname } :${ port } /` ;
326+ createHeadlessServer ( hostname , num ( port ) , url ) ;
327+ }
331328
332- // Start frontend
333- if ( opts . frontend ) {
329+ if ( opts . electronWindow ) {
334330 await app . whenReady ( ) ;
335- // Create main window
331+
336332 if ( ! state . window ) {
337333 state . window = createMainWindow ( ) ;
338334 }
339- } else {
340- // Frontend not running, give Backend init message from Main
341- state . socket . send ( BackIn . INIT_LISTEN ) ;
342335 }
343336 }
344337
345338 async function onAppReady ( ) {
346339 // Enable DoH
347- // app.configureHostResolver({
348- // secureDnsMode: 'secure',
349- // secureDnsServers: ['https://cloudflare-dns.com/dns-query', 'https://dns.google/dns-query']
350- // });
351-
352- if ( Util . isDev ) {
353- await installExtension ( REACT_DEVELOPER_TOOLS ) ;
354- await installExtension ( REDUX_DEVTOOLS ) ;
355- }
356340
357341 // Send locale code (if it has no been sent already)
358342 if ( process . platform === 'win32' && ! state . _sentLocaleCode && state . socket . client . socket ) {
@@ -518,22 +502,24 @@ export function main(init: Init): void {
518502 height : height ,
519503 minWidth : 200 ,
520504 minHeight : 200 ,
521- frame : ! state . preferences . useCustomTitlebar ,
505+ frame : true ,
506+ // frame: !state.preferences.useCustomTitlebar,
522507 icon : path . join ( __dirname , '../window/images/icon.png' ) ,
523508 webPreferences : {
524509 preload : path . resolve ( __dirname , 'preload.js' ) ,
525510 nodeIntegration : false ,
526511 contextIsolation : true ,
527512 } ,
528513 } ) ;
514+
529515 // Add protocol report func
530516 ipcMain . on ( WindowIPC . PROTOCOL , ( ) => {
531517 if ( init . protocol ) {
532518 window . webContents . send ( WindowIPC . PROTOCOL , init . protocol ) ;
533519 }
534520 } ) ;
535521 // Remove the menu bar
536- window . setMenu ( null ) ;
522+ // window.setMenu(null);
537523 // and load the index.html of the app.
538524 window . loadFile ( path . join ( __dirname , '../window/renderer.html' ) ) ;
539525 // Open the DevTools. Don't open if using a remote debugger (like vscode)
0 commit comments