From bca85fbdc1028618984268b68c600b7837e01651 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Wed, 12 Nov 2025 22:11:29 +0100 Subject: [PATCH] always a size --- crates/lanspread-db/src/db.rs | 9 +- crates/lanspread-peer/src/lib.rs | 130 ++++++++++++----------------- crates/lanspread-server/src/req.rs | 13 ++- 3 files changed, 68 insertions(+), 84 deletions(-) diff --git a/crates/lanspread-db/src/db.rs b/crates/lanspread-db/src/db.rs index 4582332..8068039 100644 --- a/crates/lanspread-db/src/db.rs +++ b/crates/lanspread-db/src/db.rs @@ -179,8 +179,7 @@ pub struct GameFileDescription { pub game_id: String, pub relative_path: String, pub is_dir: bool, - #[serde(default)] - pub size: Option, + pub size: u64, } impl GameFileDescription { @@ -190,8 +189,8 @@ impl GameFileDescription { } #[must_use] - pub fn file_size(&self) -> Option { - if self.is_dir { None } else { self.size } + pub fn file_size(&self) -> u64 { + if self.is_dir { 0 } else { self.size } } } @@ -204,7 +203,7 @@ impl fmt::Debug for GameFileDescription { self.game_id, if self.is_dir { 'D' } else { 'F' }, self.relative_path, - self.size.unwrap_or_default(), + self.size, ) } } diff --git a/crates/lanspread-peer/src/lib.rs b/crates/lanspread-peer/src/lib.rs index fbb0c98..a2a7e7a 100644 --- a/crates/lanspread-peer/src/lib.rs +++ b/crates/lanspread-peer/src/lib.rs @@ -391,7 +391,7 @@ impl PeerGameDB { .and_then(|p| p.files.get(game_id)) && let Some(file_desc) = files .iter() - .find(|f| f.relative_path == relative_path && f.size == Some(size)) + .find(|f| f.relative_path == relative_path && f.size == size) { return Some(file_desc.clone()); } @@ -427,9 +427,8 @@ fn collect_file_sizes( for (peer_addr, files) in game_files { let mut peer_file_sizes = HashMap::new(); for file in files { - if !file.is_dir - && let Some(size) = file.size - { + if !file.is_dir { + let size = file.size; file_size_map .entry(file.relative_path.clone()) .or_default() @@ -587,23 +586,22 @@ async fn prepare_game_storage( .open(&validated_path) .await?; - // Pre-allocate the file if we have size information - if let Some(size) = desc.size { - if let Err(e) = file.set_len(size).await { - log::warn!( - "Failed to pre-allocate file {} (size: {}): {}", - desc.relative_path, - size, - e - ); - // Continue without pre-allocation - the file will grow as chunks are written - } else { - log::debug!( - "Pre-allocated file {} with {} bytes", - desc.relative_path, - size - ); - } + // Pre-allocate the file with the expected size + let size = desc.size; + if let Err(e) = file.set_len(size).await { + log::warn!( + "Failed to pre-allocate file {} (size: {}): {}", + desc.relative_path, + size, + e + ); + // Continue without pre-allocation - the file will grow as chunks are written + } else { + log::debug!( + "Pre-allocated file {} with {} bytes", + desc.relative_path, + size + ); } } } @@ -622,42 +620,33 @@ fn build_peer_plans( let mut peer_index = 0usize; for desc in file_descs.iter().filter(|d| !d.is_dir) { - if let Some(size) = desc.file_size() { - if size == 0 { - let peer = peers[peer_index % peers.len()]; - peer_index += 1; - plans.entry(peer).or_default().chunks.push(DownloadChunk { - relative_path: desc.relative_path.clone(), - offset: 0, - length: 0, - retry_count: 0, - last_peer: Some(peer), - }); - continue; - } - - let mut offset = 0u64; - while offset < size { - let length = std::cmp::min(CHUNK_SIZE, size - offset); - let peer = peers[peer_index % peers.len()]; - peer_index += 1; - plans.entry(peer).or_default().chunks.push(DownloadChunk { - relative_path: desc.relative_path.clone(), - offset, - length, - retry_count: 0, - last_peer: Some(peer), - }); - offset += length; - } - } else { + let size = desc.file_size(); + if size == 0 { let peer = peers[peer_index % peers.len()]; peer_index += 1; - plans - .entry(peer) - .or_default() - .whole_files - .push(desc.clone()); + plans.entry(peer).or_default().chunks.push(DownloadChunk { + relative_path: desc.relative_path.clone(), + offset: 0, + length: 0, + retry_count: 0, + last_peer: Some(peer), + }); + continue; + } + + let mut offset = 0u64; + while offset < size { + let length = std::cmp::min(CHUNK_SIZE, size - offset); + let peer = peers[peer_index % peers.len()]; + peer_index += 1; + plans.entry(peer).or_default().chunks.push(DownloadChunk { + relative_path: desc.relative_path.clone(), + offset, + length, + retry_count: 0, + last_peer: Some(peer), + }); + offset += length; } } @@ -673,24 +662,13 @@ async fn download_chunk( let stream = conn.open_bidirectional_stream().await?; let (mut rx, mut tx) = stream.split(); - if chunk.length == 0 { - // fall back to whole file download when size is unknown - let request = Request::GetGameFileData(GameFileDescription { - game_id: game_id.to_string(), - relative_path: chunk.relative_path.clone(), - is_dir: false, - size: None, - }); - tx.write_all(&request.encode()).await?; - } else { - let request = Request::GetGameFileChunk { - game_id: game_id.to_string(), - relative_path: chunk.relative_path.clone(), - offset: chunk.offset, - length: chunk.length, - }; - tx.write_all(&request.encode()).await?; - } + let request = Request::GetGameFileChunk { + game_id: game_id.to_string(), + relative_path: chunk.relative_path.clone(), + offset: chunk.offset, + length: chunk.length, + }; + tx.write_all(&request.encode()).await?; tx.close().await?; @@ -1905,13 +1883,13 @@ async fn get_game_file_descriptions( let is_dir = entry.file_type().is_dir(); let size = if is_dir { - None + 0 } else { match tokio::fs::metadata(entry.path()).await { - Ok(metadata) => Some(metadata.len()), + Ok(metadata) => metadata.len(), Err(e) => { log::error!("Failed to read metadata for {relative_path}: {e}"); - None + eyre::bail!("Failed to read metadata for {relative_path}: {e}"); } } }; diff --git a/crates/lanspread-server/src/req.rs b/crates/lanspread-server/src/req.rs index 95dfb76..841e598 100644 --- a/crates/lanspread-server/src/req.rs +++ b/crates/lanspread-server/src/req.rs @@ -80,16 +80,23 @@ impl RequestHandler { Some(relative_path) => { let is_dir = entry.file_type().is_dir(); let size = if is_dir { - None + 0 } else { match entry.metadata() { - Ok(metadata) => Some(metadata.len()), + Ok(metadata) => metadata.len(), Err(e) => { tracing::error!( "Failed to read metadata for {}: {e}", relative_path ); - None + // Return early since we can't proceed without file size + return Response::InvalidRequest( + format!("Failed to read metadata for {relative_path}") + .as_bytes() + .to_vec() + .into(), + "File size unavailable".to_string(), + ); } } };