always a size
This commit is contained in:
@@ -179,8 +179,7 @@ pub struct GameFileDescription {
|
|||||||
pub game_id: String,
|
pub game_id: String,
|
||||||
pub relative_path: String,
|
pub relative_path: String,
|
||||||
pub is_dir: bool,
|
pub is_dir: bool,
|
||||||
#[serde(default)]
|
pub size: u64,
|
||||||
pub size: Option<u64>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameFileDescription {
|
impl GameFileDescription {
|
||||||
@@ -190,8 +189,8 @@ impl GameFileDescription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn file_size(&self) -> Option<u64> {
|
pub fn file_size(&self) -> u64 {
|
||||||
if self.is_dir { None } else { self.size }
|
if self.is_dir { 0 } else { self.size }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +203,7 @@ impl fmt::Debug for GameFileDescription {
|
|||||||
self.game_id,
|
self.game_id,
|
||||||
if self.is_dir { 'D' } else { 'F' },
|
if self.is_dir { 'D' } else { 'F' },
|
||||||
self.relative_path,
|
self.relative_path,
|
||||||
self.size.unwrap_or_default(),
|
self.size,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -391,7 +391,7 @@ impl PeerGameDB {
|
|||||||
.and_then(|p| p.files.get(game_id))
|
.and_then(|p| p.files.get(game_id))
|
||||||
&& let Some(file_desc) = files
|
&& let Some(file_desc) = files
|
||||||
.iter()
|
.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());
|
return Some(file_desc.clone());
|
||||||
}
|
}
|
||||||
@@ -427,9 +427,8 @@ fn collect_file_sizes(
|
|||||||
for (peer_addr, files) in game_files {
|
for (peer_addr, files) in game_files {
|
||||||
let mut peer_file_sizes = HashMap::new();
|
let mut peer_file_sizes = HashMap::new();
|
||||||
for file in files {
|
for file in files {
|
||||||
if !file.is_dir
|
if !file.is_dir {
|
||||||
&& let Some(size) = file.size
|
let size = file.size;
|
||||||
{
|
|
||||||
file_size_map
|
file_size_map
|
||||||
.entry(file.relative_path.clone())
|
.entry(file.relative_path.clone())
|
||||||
.or_default()
|
.or_default()
|
||||||
@@ -587,23 +586,22 @@ async fn prepare_game_storage(
|
|||||||
.open(&validated_path)
|
.open(&validated_path)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Pre-allocate the file if we have size information
|
// Pre-allocate the file with the expected size
|
||||||
if let Some(size) = desc.size {
|
let size = desc.size;
|
||||||
if let Err(e) = file.set_len(size).await {
|
if let Err(e) = file.set_len(size).await {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"Failed to pre-allocate file {} (size: {}): {}",
|
"Failed to pre-allocate file {} (size: {}): {}",
|
||||||
desc.relative_path,
|
desc.relative_path,
|
||||||
size,
|
size,
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
// Continue without pre-allocation - the file will grow as chunks are written
|
// Continue without pre-allocation - the file will grow as chunks are written
|
||||||
} else {
|
} else {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Pre-allocated file {} with {} bytes",
|
"Pre-allocated file {} with {} bytes",
|
||||||
desc.relative_path,
|
desc.relative_path,
|
||||||
size
|
size
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -622,42 +620,33 @@ fn build_peer_plans(
|
|||||||
let mut peer_index = 0usize;
|
let mut peer_index = 0usize;
|
||||||
|
|
||||||
for desc in file_descs.iter().filter(|d| !d.is_dir) {
|
for desc in file_descs.iter().filter(|d| !d.is_dir) {
|
||||||
if let Some(size) = desc.file_size() {
|
let size = desc.file_size();
|
||||||
if size == 0 {
|
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 peer = peers[peer_index % peers.len()];
|
let peer = peers[peer_index % peers.len()];
|
||||||
peer_index += 1;
|
peer_index += 1;
|
||||||
plans
|
plans.entry(peer).or_default().chunks.push(DownloadChunk {
|
||||||
.entry(peer)
|
relative_path: desc.relative_path.clone(),
|
||||||
.or_default()
|
offset: 0,
|
||||||
.whole_files
|
length: 0,
|
||||||
.push(desc.clone());
|
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 stream = conn.open_bidirectional_stream().await?;
|
||||||
let (mut rx, mut tx) = stream.split();
|
let (mut rx, mut tx) = stream.split();
|
||||||
|
|
||||||
if chunk.length == 0 {
|
let request = Request::GetGameFileChunk {
|
||||||
// fall back to whole file download when size is unknown
|
game_id: game_id.to_string(),
|
||||||
let request = Request::GetGameFileData(GameFileDescription {
|
relative_path: chunk.relative_path.clone(),
|
||||||
game_id: game_id.to_string(),
|
offset: chunk.offset,
|
||||||
relative_path: chunk.relative_path.clone(),
|
length: chunk.length,
|
||||||
is_dir: false,
|
};
|
||||||
size: None,
|
tx.write_all(&request.encode()).await?;
|
||||||
});
|
|
||||||
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?;
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.close().await?;
|
tx.close().await?;
|
||||||
|
|
||||||
@@ -1905,13 +1883,13 @@ async fn get_game_file_descriptions(
|
|||||||
|
|
||||||
let is_dir = entry.file_type().is_dir();
|
let is_dir = entry.file_type().is_dir();
|
||||||
let size = if is_dir {
|
let size = if is_dir {
|
||||||
None
|
0
|
||||||
} else {
|
} else {
|
||||||
match tokio::fs::metadata(entry.path()).await {
|
match tokio::fs::metadata(entry.path()).await {
|
||||||
Ok(metadata) => Some(metadata.len()),
|
Ok(metadata) => metadata.len(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Failed to read metadata for {relative_path}: {e}");
|
log::error!("Failed to read metadata for {relative_path}: {e}");
|
||||||
None
|
eyre::bail!("Failed to read metadata for {relative_path}: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -80,16 +80,23 @@ impl RequestHandler {
|
|||||||
Some(relative_path) => {
|
Some(relative_path) => {
|
||||||
let is_dir = entry.file_type().is_dir();
|
let is_dir = entry.file_type().is_dir();
|
||||||
let size = if is_dir {
|
let size = if is_dir {
|
||||||
None
|
0
|
||||||
} else {
|
} else {
|
||||||
match entry.metadata() {
|
match entry.metadata() {
|
||||||
Ok(metadata) => Some(metadata.len()),
|
Ok(metadata) => metadata.len(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
"Failed to read metadata for {}: {e}",
|
"Failed to read metadata for {}: {e}",
|
||||||
relative_path
|
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(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user