@@ -92,6 +92,9 @@ interface IProvider {
9292
9393const gameInfoProviders : IProvider [ ] = [ ] ;
9494
95+ // Add this at the top of the file with the other module-level variables
96+ let isRefreshingGameList = false ;
97+
9598function refreshGameInfo ( store : Redux . Store < IState > , gameId : string ) : Promise < void > {
9699 interface IKeyProvider {
97100 [ key : string ] : { priority : number , provider : string } ;
@@ -239,9 +242,19 @@ function browseGameLocation(api: IExtensionApi, gameId: string): Promise<void> {
239242 const state : IState = api . store . getState ( ) ;
240243
241244 if ( gameById ( state , gameId ) === undefined ) {
242- return api . showDialog ( 'question' , 'Game support not installed' , {
243- text : 'Support for this game is provided through an extension. '
244- + 'Please click "Manage" to install the extension and set it up.' ,
245+ // Find the extension to get the game name
246+ const extension = state . session . extensions . available . find ( ext =>
247+ ( ext ?. gameId === gameId ) || ( ext . name === gameId ) ) ;
248+
249+ // Get the game name for better user messaging
250+ const gameName = extension ?. gameName || extension ?. name ?. replace ( / ^ G a m e : / , '' ) || 'this game' ;
251+
252+ return api . showDialog ( 'question' , 'Game Support Not Installed' , {
253+ text : 'Support for {{gameName}} is provided through a community extension that is not included with the main Vortex application. '
254+ + 'To manage mods for {{gameName}}, you need to download and install this extension.' ,
255+ parameters : {
256+ gameName
257+ }
245258 } , [
246259 { label : 'Close' } ,
247260 ] )
@@ -857,10 +870,169 @@ function init(context: IExtensionContext): boolean {
857870 . catch ( err => callback ( err ) ) ;
858871 } ) ;
859872
860- events . on ( 'refresh-game-list' , ( ) => {
873+ events . on ( 'refresh-game-list' , ( forceFullDiscovery ?: boolean ) => {
861874 // Refresh the known games list when game extensions are installed
862- $ . gameModeManager . refreshKnownGames ( ) ;
863- log ( 'info' , 'Game list refreshed after extension installation' ) ;
875+ if ( forceFullDiscovery ) {
876+ // Force a complete refresh including re-reading game extensions
877+ // Use the extension manager's exposed update function if available
878+ if ( context . api . ext [ 'updateExtensions' ] ) {
879+ // Add a flag to prevent infinite loop
880+ if ( isRefreshingGameList ) {
881+ // Already refreshing, skip to prevent infinite loop
882+ return ;
883+ }
884+
885+ // Set the flag to indicate we're refreshing
886+ isRefreshingGameList = true ;
887+
888+ context . api . ext [ 'updateExtensions' ] ( false )
889+ . then ( ( ) => {
890+ // After updating extensions, we need to ensure game extensions are properly registered
891+ // Re-initialize the extension games list to capture any newly installed game extensions
892+ $ . extensionGames = [ ] ;
893+
894+ // Get the current state and re-process all installed game extensions
895+ const state = context . api . getState ( ) ;
896+ const installedExtensions = state . session . extensions . installed ;
897+
898+ // Process each installed game extension to ensure it's registered
899+ return Promise . map ( Object . keys ( installedExtensions ) , extId => {
900+ const ext = installedExtensions [ extId ] ;
901+ if ( ext . type === 'game' && ext . path ) {
902+ try {
903+ // Try to load and register the game extension
904+ const indexPath = path . join ( ext . path , 'index.js' ) ;
905+ return fs . statAsync ( indexPath )
906+ . then ( ( ) => {
907+ const extensionModule = require ( indexPath ) ;
908+ if ( typeof extensionModule . default === 'function' ) {
909+ // Create a minimal context for the extension to register its game
910+ const contextProxy = new Proxy ( {
911+ api : context . api ,
912+ registerGame : ( game : IGame ) => {
913+ // Register the game in our extension games list
914+ game . extensionPath = ext . path ;
915+ $ . extensionGames . push ( game ) ;
916+ } ,
917+ registerGameStub : ( game : IGame , extInfo : IExtensionDownloadInfo ) => {
918+ // Handle game stubs if needed
919+ $ . extensionStubs . push ( { ext : extInfo , game } ) ;
920+ }
921+ } , {
922+ get : ( target , prop ) => {
923+ // Provide stub functions for other register methods
924+ if ( prop === 'registerGame' || prop === 'registerGameStub' ) {
925+ return target [ prop ] ;
926+ } else if ( typeof prop === 'string' && prop . startsWith ( 'register' ) ) {
927+ return ( ) => { } ; // Stub for other register methods
928+ }
929+ return target [ prop ] ;
930+ }
931+ } ) ;
932+
933+ // Call the extension's init function
934+ extensionModule . default ( contextProxy ) ;
935+ }
936+ return Promise . resolve ( ) ;
937+ } )
938+ . catch ( ( ) => {
939+ // File doesn't exist or other error, skip this extension
940+ return Promise . resolve ( ) ;
941+ } ) ;
942+ } catch ( err ) {
943+ log ( 'warn' , 'Failed to load game extension' , { extension : ext . name , error : err . message } ) ;
944+ return Promise . resolve ( ) ;
945+ }
946+ }
947+ return Promise . resolve ( ) ;
948+ } )
949+ . then ( ( ) => {
950+ // Refresh the known games with the updated extension games list
951+ const gamesStored : IGameStored [ ] = $ . extensionGames
952+ . map ( game => ( {
953+ name : game . name ,
954+ shortName : game . shortName ,
955+ id : game . id ,
956+ logo : game . logo ,
957+ extensionPath : game . extensionPath ,
958+ contributed : game . contributed ,
959+ final : game . final ,
960+ version : game . version ,
961+ executable : game . executable ?.( ) ,
962+ requiredFiles : game . requiredFiles ,
963+ environment : game . environment ,
964+ details : game . details ,
965+ } ) )
966+ . filter ( game =>
967+ ( game . executable !== undefined ) &&
968+ ( game . requiredFiles !== undefined ) &&
969+ ( game . name !== undefined )
970+ ) ;
971+ context . api . store . dispatch ( setKnownGames ( gamesStored ) ) ;
972+
973+ // Also run quick discovery to find the game paths
974+ return $ . gameModeManager . startQuickDiscovery ( undefined , false ) ;
975+ } ) ;
976+ } )
977+ . then ( ( ) => {
978+ log ( 'info' , 'Game list fully refreshed after extension installation' ) ;
979+ // Clear the flag when done
980+ isRefreshingGameList = false ;
981+ } )
982+ . catch ( err => {
983+ log ( 'error' , 'Failed to refresh game list' , err . message ) ;
984+ // Clear the flag on error
985+ isRefreshingGameList = false ;
986+ } ) ;
987+ } else {
988+ // Fallback if the update function is not available
989+ const gamesStored : IGameStored [ ] = $ . extensionGames
990+ . map ( game => ( {
991+ name : game . name ,
992+ shortName : game . shortName ,
993+ id : game . id ,
994+ logo : game . logo ,
995+ extensionPath : game . extensionPath ,
996+ contributed : game . contributed ,
997+ final : game . final ,
998+ version : game . version ,
999+ executable : game . executable ?.( ) ,
1000+ requiredFiles : game . requiredFiles ,
1001+ environment : game . environment ,
1002+ details : game . details ,
1003+ } ) )
1004+ . filter ( game =>
1005+ ( game . executable !== undefined ) &&
1006+ ( game . requiredFiles !== undefined ) &&
1007+ ( game . name !== undefined )
1008+ ) ;
1009+ context . api . store . dispatch ( setKnownGames ( gamesStored ) ) ;
1010+ log ( 'info' , 'Game list refreshed after extension installation' ) ;
1011+ }
1012+ } else {
1013+ const gamesStored : IGameStored [ ] = $ . extensionGames
1014+ . map ( game => ( {
1015+ name : game . name ,
1016+ shortName : game . shortName ,
1017+ id : game . id ,
1018+ logo : game . logo ,
1019+ extensionPath : game . extensionPath ,
1020+ contributed : game . contributed ,
1021+ final : game . final ,
1022+ version : game . version ,
1023+ executable : game . executable ?.( ) ,
1024+ requiredFiles : game . requiredFiles ,
1025+ environment : game . environment ,
1026+ details : game . details ,
1027+ } ) )
1028+ . filter ( game =>
1029+ ( game . executable !== undefined ) &&
1030+ ( game . requiredFiles !== undefined ) &&
1031+ ( game . name !== undefined )
1032+ ) ;
1033+ context . api . store . dispatch ( setKnownGames ( gamesStored ) ) ;
1034+ log ( 'info' , 'Game list refreshed after extension installation' ) ;
1035+ }
8641036 } ) ;
8651037
8661038 const changeGameMode = ( oldGameId : string , newGameId : string ,
0 commit comments