@@ -22,8 +22,8 @@ use crate::remote::requests::generate_url;
2222use crate :: remote:: utils:: { DROP_CLIENT_ASYNC , DROP_CLIENT_SYNC } ;
2323use log:: { debug, error, info, warn} ;
2424use rayon:: ThreadPoolBuilder ;
25- use std:: collections:: HashMap ;
26- use std:: fs:: { OpenOptions , create_dir_all } ;
25+ use std:: collections:: { HashMap , HashSet } ;
26+ use std:: fs:: { create_dir_all , OpenOptions } ;
2727use std:: path:: { Path , PathBuf } ;
2828use std:: sync:: mpsc:: Sender ;
2929use std:: sync:: { Arc , Mutex } ;
@@ -242,12 +242,8 @@ impl GameDownloadAgent {
242242
243243 let mut buckets = Vec :: new ( ) ;
244244
245- let mut current_bucket = DownloadBucket {
246- game_id : game_id. clone ( ) ,
247- version : self . version . clone ( ) ,
248- drops : Vec :: new ( ) ,
249- } ;
250- let mut current_bucket_size = 0 ;
245+ let mut current_buckets = HashMap :: < String , DownloadBucket > :: new ( ) ;
246+ let mut current_bucket_sizes = HashMap :: < String , usize > :: new ( ) ;
251247
252248 for ( raw_path, chunk) in manifest {
253249 let path = base_path. join ( Path :: new ( & raw_path) ) ;
@@ -282,28 +278,41 @@ impl GameDownloadAgent {
282278
283279 buckets. push ( DownloadBucket {
284280 game_id : game_id. clone ( ) ,
285- version : self . version . clone ( ) ,
281+ version : chunk . version_name . clone ( ) ,
286282 drops : vec ! [ drop] ,
287283 } ) ;
288284
289285 continue ;
290286 }
291287
292- if current_bucket_size + * length >= TARGET_BUCKET_SIZE
288+ let current_bucket_size = current_bucket_sizes
289+ . entry ( chunk. version_name . clone ( ) )
290+ . or_insert_with ( || 0 ) ;
291+ let c_version_name = chunk. version_name . clone ( ) ;
292+ let c_game_id = game_id. clone ( ) ;
293+ let current_bucket = current_buckets
294+ . entry ( chunk. version_name . clone ( ) )
295+ . or_insert_with ( || DownloadBucket {
296+ game_id : c_game_id,
297+ version : c_version_name,
298+ drops : vec ! [ ] ,
299+ } ) ;
300+
301+ if * current_bucket_size + length >= TARGET_BUCKET_SIZE
293302 && !current_bucket. drops . is_empty ( )
294303 {
295304 // Move current bucket into list and make a new one
296- buckets. push ( current_bucket) ;
297- current_bucket = DownloadBucket {
305+ buckets. push ( current_bucket. clone ( ) ) ;
306+ * current_bucket = DownloadBucket {
298307 game_id : game_id. clone ( ) ,
299- version : self . version . clone ( ) ,
300- drops : Vec :: new ( ) ,
308+ version : chunk . version_name . clone ( ) ,
309+ drops : vec ! [ ] ,
301310 } ;
302- current_bucket_size = 0 ;
311+ * current_bucket_size = 0 ;
303312 }
304313
305314 current_bucket. drops . push ( drop) ;
306- current_bucket_size += * length;
315+ * current_bucket_size += * length;
307316 }
308317
309318 #[ cfg( target_os = "linux" ) ]
@@ -312,8 +321,10 @@ impl GameDownloadAgent {
312321 }
313322 }
314323
315- if !current_bucket. drops . is_empty ( ) {
316- buckets. push ( current_bucket) ;
324+ for ( _, bucket) in current_buckets. into_iter ( ) {
325+ if !bucket. drops . is_empty ( ) {
326+ buckets. push ( bucket) ;
327+ }
317328 }
318329
319330 info ! ( "buckets: {}" , buckets. len( ) ) ;
@@ -348,27 +359,46 @@ impl GameDownloadAgent {
348359 . build ( )
349360 . unwrap ( ) ;
350361
362+ let buckets = self . buckets . lock ( ) . unwrap ( ) ;
363+
364+ let mut download_contexts = HashMap :: < String , DownloadContext > :: new ( ) ;
365+
366+ let versions = buckets
367+ . iter ( )
368+ . map ( |e| & e. version )
369+ . collect :: < HashSet < _ > > ( )
370+ . into_iter ( ) . cloned ( )
371+ . collect :: < Vec < String > > ( ) ;
372+
373+ info ! ( "downloading across these versions: {versions:?}" ) ;
374+
351375 let completed_contexts = Arc :: new ( boxcar:: Vec :: new ( ) ) ;
352376 let completed_indexes_loop_arc = completed_contexts. clone ( ) ;
353377
354- let download_context = DROP_CLIENT_SYNC
355- . post ( generate_url ( & [ "/api/v2/client/context" ] , & [ ] ) . unwrap ( ) )
356- . json ( & ManifestBody {
357- game : self . id . clone ( ) ,
358- version : self . version . clone ( ) ,
359- } )
360- . header ( "Authorization" , generate_authorization_header ( ) )
361- . send ( ) ?;
378+ for version in versions {
379+ let download_context = DROP_CLIENT_SYNC
380+ . post ( generate_url ( & [ "/api/v2/client/context" ] , & [ ] ) . unwrap ( ) )
381+ . json ( & ManifestBody {
382+ game : self . id . clone ( ) ,
383+ version : version. clone ( ) ,
384+ } )
385+ . header ( "Authorization" , generate_authorization_header ( ) )
386+ . send ( ) ?;
362387
363- if download_context. status ( ) != 200 {
364- return Err ( RemoteAccessError :: InvalidResponse ( download_context. json ( ) ?) ) ;
365- }
388+ if download_context. status ( ) != 200 {
389+ return Err ( RemoteAccessError :: InvalidResponse ( download_context. json ( ) ?) ) ;
390+ }
366391
367- let download_context = & download_context. json :: < DownloadContext > ( ) ?;
392+ let download_context = download_context. json :: < DownloadContext > ( ) ?;
393+ info ! (
394+ "download context: ({}) {}" ,
395+ & version, download_context. context
396+ ) ;
397+ download_contexts. insert ( version, download_context) ;
398+ }
368399
369- info ! ( "download context: {}" , download_context . context ) ;
400+ let download_contexts = & download_contexts ;
370401
371- let buckets = self . buckets . lock ( ) . unwrap ( ) ;
372402 pool. scope ( |scope| {
373403 let context_map = self . context_map . lock ( ) . unwrap ( ) ;
374404 for ( index, bucket) in buckets. iter ( ) . enumerate ( ) {
@@ -400,6 +430,11 @@ impl GameDownloadAgent {
400430
401431 let sender = self . sender . clone ( ) ;
402432
433+ let download_context = download_contexts
434+ . get ( & bucket. version )
435+ . ok_or ( RemoteAccessError :: CorruptedState )
436+ . unwrap ( ) ;
437+
403438 scope. spawn ( move |_| {
404439 // 3 attempts
405440 for i in 0 ..RETRY_COUNT {
0 commit comments