@@ -8,7 +8,10 @@ use futures_util::StreamExt;
88use serde:: { Deserialize , Serialize } ;
99use tauri:: { AppHandle , Runtime } ;
1010
11- use crate :: { store, ApiResponse } ;
11+ use crate :: {
12+ commands:: http:: { perform_backend_request, BackendBody } ,
13+ store, ApiResponse ,
14+ } ;
1215
1316#[ derive( Deserialize , Serialize , Debug , Clone ) ]
1417pub struct ApiEmulator {
@@ -26,6 +29,14 @@ pub struct ApiEmulator {
2629 pub zipped : bool ,
2730 pub file_size : i64 ,
2831 pub md5_hash : Option < String > ,
32+ pub version : Option < String > ,
33+ }
34+
35+ #[ derive( Serialize , Debug , Clone ) ]
36+ pub struct ApiEmulatorExtra {
37+ #[ serde( flatten) ]
38+ pub emulator : ApiEmulator ,
39+ pub source_server : String ,
2940}
3041
3142#[ derive( Serialize , Deserialize , Debug , Clone ) ]
@@ -49,6 +60,10 @@ pub struct StoreEmulator {
4960 pub zipped : bool ,
5061 #[ serde( default ) ]
5162 pub file_size : i64 ,
63+ #[ serde( default ) ]
64+ pub version : Option < String > ,
65+ #[ serde( default ) ]
66+ pub source_server : Option < String > ,
5267}
5368
5469#[ tauri:: command]
@@ -76,12 +91,12 @@ pub async fn fetch_server_emulators<R: Runtime>(
7691 platform
7792 ) ;
7893
79- let api_res = crate :: commands :: http :: perform_backend_request (
94+ let api_res = perform_backend_request (
8095 & app,
8196 reqwest:: Method :: GET ,
8297 & api_url,
8398 Some ( & token) ,
84- crate :: commands :: http :: BackendBody :: None ,
99+ BackendBody :: None ,
85100 false ,
86101 )
87102 . await ?
@@ -95,60 +110,80 @@ pub async fn fetch_server_emulators<R: Runtime>(
95110#[ tauri:: command]
96111pub async fn fetch_all_server_emulators < R : Runtime > (
97112 app : AppHandle < R > ,
98- ) -> Result < Vec < ApiEmulator > , String > {
99- let store = store:: get_current_store ( & app) ?;
100-
101- let domain = store
102- . get ( "domain" )
103- . and_then ( |v| v. as_str ( ) . map ( |s| s. to_string ( ) ) )
104- . ok_or ( "Domain not found" ) ?;
105- let token = store
106- . get ( "token" )
107- . and_then ( |v| v. as_str ( ) . map ( |s| s. to_string ( ) ) )
108- . ok_or ( "Token not found" ) ?;
113+ ) -> Result < Vec < ApiEmulatorExtra > , String > {
114+ let global_store = store:: get_global_store ( & app) ?;
115+ let domains: Vec < String > = global_store
116+ . get ( "domains" )
117+ . and_then ( |v| serde_json:: from_value ( v. clone ( ) ) . ok ( ) )
118+ . unwrap_or_default ( ) ;
109119
120+ let mut all_results = Vec :: new ( ) ;
110121 let platform = std:: env:: consts:: OS . to_lowercase ( ) ;
111- let api_url = format ! ( "{}/api/v1/emulators/all" , domain. trim_end_matches( '/' ) ) ;
112122
113- let api_res = crate :: commands:: http:: perform_backend_request (
114- & app,
115- reqwest:: Method :: GET ,
116- & api_url,
117- Some ( & token) ,
118- crate :: commands:: http:: BackendBody :: None ,
119- false ,
120- )
121- . await ?
122- . into_json :: < ApiResponse < Vec < ApiEmulator > > > ( ) ?;
123+ for domain in domains {
124+ let domain_store = match store:: get_domain_store ( & app, & domain) {
125+ Ok ( s) => s,
126+ Err ( _) => continue ,
127+ } ;
123128
124- let emulators = api_res. data . unwrap_or_default ( ) ;
129+ let token = match domain_store
130+ . get ( "token" )
131+ . and_then ( |v| v. as_str ( ) . map ( |s| s. to_string ( ) ) )
132+ {
133+ Some ( t) => t,
134+ None => continue ,
135+ } ;
125136
126- let filtered: Vec < ApiEmulator > = emulators
127- . into_iter ( )
128- . filter ( |e| e. platform . to_lowercase ( ) == platform)
129- . collect ( ) ;
137+ let api_url = format ! ( "{}/api/v1/emulators/all" , domain. trim_end_matches( '/' ) ) ;
138+
139+ if let Ok ( res) = perform_backend_request (
140+ & app,
141+ reqwest:: Method :: GET ,
142+ & api_url,
143+ Some ( & token) ,
144+ BackendBody :: None ,
145+ false ,
146+ )
147+ . await
148+ {
149+ if let Ok ( api_res) = res. into_json :: < ApiResponse < Vec < ApiEmulator > > > ( ) {
150+ if let Some ( emulators) = api_res. data {
151+ for e in emulators {
152+ if e. platform . to_lowercase ( ) == platform {
153+ all_results. push ( ApiEmulatorExtra {
154+ emulator : e,
155+ source_server : domain. clone ( ) ,
156+ } ) ;
157+ }
158+ }
159+ }
160+ }
161+ }
162+ }
130163
131- Ok ( filtered )
164+ Ok ( all_results )
132165}
133166
134167#[ tauri:: command]
135168pub async fn download_emulator < R : Runtime > (
136169 app : AppHandle < R > ,
137170 console : String ,
138171 emulator_id : Option < String > ,
172+ keep_config : Option < bool > ,
173+ source_server : Option < String > ,
139174) -> Result < ( ) , String > {
140- let current_store = store:: get_current_store ( & app) ?;
141175 let global_store = store:: get_global_store ( & app) ?;
176+ let domain = match source_server {
177+ Some ( d) => d,
178+ None => store:: get_current_domain ( & app) . ok_or ( "Domain not found" ) ?,
179+ } ;
142180
143- let domain = current_store
144- . get ( "domain" )
145- . and_then ( |v| v. as_str ( ) . map ( |s| s. to_string ( ) ) )
146- . ok_or ( "Domain not found" ) ?;
147- let token = current_store
181+ let domain_store = store:: get_domain_store ( & app, & domain) ?;
182+ let token = domain_store
148183 . get ( "token" )
149184 . and_then ( |v| v. as_str ( ) . map ( |s| s. to_string ( ) ) )
150185 . ok_or ( "Token not found" ) ?;
151- let storage_path = current_store
186+ let storage_path = domain_store
152187 . get ( "storage_path" )
153188 . and_then ( |v| v. as_str ( ) . map ( |s| s. to_string ( ) ) )
154189 . ok_or ( "Storage path not found" ) ?;
@@ -162,12 +197,12 @@ pub async fn download_emulator<R: Runtime>(
162197 platform
163198 ) ;
164199
165- let api_res = crate :: commands :: http :: perform_backend_request (
200+ let api_res = perform_backend_request (
166201 & app,
167202 reqwest:: Method :: GET ,
168203 & api_url,
169204 Some ( & token) ,
170- crate :: commands :: http :: BackendBody :: None ,
205+ BackendBody :: None ,
171206 false ,
172207 )
173208 . await ?
@@ -207,12 +242,12 @@ pub async fn download_emulator<R: Runtime>(
207242 emulator. binary_path. trim_start_matches( '/' )
208243 ) ;
209244
210- let response = crate :: commands :: http :: perform_backend_request (
245+ let response = perform_backend_request (
211246 & app,
212247 reqwest:: Method :: GET ,
213248 & download_url,
214249 Some ( & token) ,
215- crate :: commands :: http :: BackendBody :: None ,
250+ BackendBody :: None ,
216251 true ,
217252 )
218253 . await ?
@@ -299,12 +334,18 @@ pub async fn download_emulator<R: Runtime>(
299334
300335 if let Some ( existing) = stored_emulators. get_mut ( & server_id) {
301336 existing. binary_path = final_binary_path. to_string_lossy ( ) . to_string ( ) ;
302- existing. run_command = emulator. run_command . clone ( ) ;
303- existing. save_path = emulator. save_path . clone ( ) ;
304- existing. save_extensions = emulator. save_extensions . clone ( ) ;
305- existing. input_config_file = emulator. input_config_file . clone ( ) ;
306- existing. input_mapper = emulator. input_mapper . clone ( ) ;
307- existing. zipped = emulator. zipped ;
337+ let should_keep = keep_config. unwrap_or ( false ) ;
338+
339+ if !should_keep {
340+ existing. run_command = emulator. run_command . clone ( ) ;
341+ existing. save_path = emulator. save_path . clone ( ) ;
342+ existing. save_extensions = emulator. save_extensions . clone ( ) ;
343+ existing. input_config_file = emulator. input_config_file . clone ( ) ;
344+ existing. input_mapper = emulator. input_mapper . clone ( ) ;
345+ existing. zipped = emulator. zipped ;
346+ }
347+ existing. version = emulator. version . clone ( ) ;
348+ existing. source_server = Some ( domain. clone ( ) ) ;
308349
309350 for c in & emulator. consoles {
310351 if !existing. consoles . contains ( c) {
@@ -327,6 +368,8 @@ pub async fn download_emulator<R: Runtime>(
327368 input_mapper : emulator. input_mapper ,
328369 zipped : emulator. zipped ,
329370 file_size : emulator. file_size ,
371+ version : emulator. version ,
372+ source_server : Some ( domain. clone ( ) ) ,
330373 } ,
331374 ) ;
332375 }
@@ -489,3 +532,96 @@ pub async fn migrate_emulator_files<R: Runtime>(app: AppHandle<R>) -> Result<(),
489532
490533 Ok ( ( ) )
491534}
535+
536+ #[ tauri:: command]
537+ pub async fn refresh_emulator_config < R : Runtime > (
538+ app : AppHandle < R > ,
539+ emulator_id : String ,
540+ ) -> Result < ( ) , String > {
541+ let global_store = store:: get_global_store ( & app) ?;
542+
543+ let mut stored_emulators: HashMap < String , StoreEmulator > = global_store
544+ . get ( "emulators" )
545+ . and_then ( |v| serde_json:: from_value ( v. clone ( ) ) . ok ( ) )
546+ . unwrap_or_default ( ) ;
547+
548+ let existing = stored_emulators
549+ . get ( & emulator_id)
550+ . ok_or ( "Emulator not found locally" ) ?;
551+
552+ if emulator_id. starts_with ( "custom-" ) {
553+ return Err ( "Cannot refresh config for custom emulator" . to_string ( ) ) ;
554+ }
555+
556+ let domain = existing
557+ . source_server
558+ . clone ( )
559+ . or_else ( || store:: get_current_domain ( & app) )
560+ . ok_or ( "Source folder not known for this emulator" ) ?;
561+
562+ let domain_store = store:: get_domain_store ( & app, & domain) ?;
563+ let token = domain_store
564+ . get ( "token" )
565+ . and_then ( |v| v. as_str ( ) . map ( |s| s. to_string ( ) ) )
566+ . ok_or_else ( || format ! ( "Login session expired for server {}" , domain) ) ?;
567+
568+ let platform = std:: env:: consts:: OS . to_lowercase ( ) ;
569+ let api_url = format ! ( "{}/api/v1/emulators/all" , domain. trim_end_matches( '/' ) ) ;
570+
571+ let api_res = perform_backend_request (
572+ & app,
573+ reqwest:: Method :: GET ,
574+ & api_url,
575+ Some ( & token) ,
576+ BackendBody :: None ,
577+ false ,
578+ )
579+ . await ?
580+ . into_json :: < ApiResponse < Vec < ApiEmulator > > > ( ) ?;
581+
582+ let emulators = api_res. data . unwrap_or_default ( ) ;
583+ let safe_domain = domain
584+ . replace ( |c : char | !c. is_alphanumeric ( ) , "_" )
585+ . to_lowercase ( ) ;
586+
587+ let mut updated = false ;
588+ for server_emu in emulators {
589+ if server_emu. platform . to_lowercase ( ) != platform {
590+ continue ;
591+ }
592+
593+ let expected_server_id = format ! ( "server-{}-{}" , safe_domain, server_emu. id) ;
594+
595+ if expected_server_id == emulator_id {
596+ if let Some ( existing) = stored_emulators. get_mut ( & emulator_id) {
597+ existing. run_command = server_emu. run_command ;
598+ existing. save_path = server_emu. save_path ;
599+ existing. save_extensions = server_emu. save_extensions ;
600+ existing. input_config_file = server_emu. input_config_file ;
601+ existing. input_mapper = server_emu. input_mapper ;
602+ existing. zipped = server_emu. zipped ;
603+ existing. file_size = server_emu. file_size ;
604+
605+ for c in & server_emu. consoles {
606+ if !existing. consoles . contains ( c) {
607+ existing. consoles . push ( c. clone ( ) ) ;
608+ }
609+ }
610+ updated = true ;
611+ break ;
612+ }
613+ }
614+ }
615+
616+ if !updated {
617+ return Err ( "Server emulator config not found or mismatched." . to_string ( ) ) ;
618+ }
619+
620+ global_store. set (
621+ "emulators" ,
622+ serde_json:: to_value ( stored_emulators) . map_err ( |e| e. to_string ( ) ) ?,
623+ ) ;
624+ global_store. save ( ) . map_err ( |e| e. to_string ( ) ) ?;
625+
626+ Ok ( ( ) )
627+ }
0 commit comments