Skip to content
This repository was archived by the owner on Feb 8, 2026. It is now read-only.

Commit 413d98d

Browse files
committed
fix: enhance asset download process with error handling and validation checks
1 parent e18c84f commit 413d98d

2 files changed

Lines changed: 200 additions & 31 deletions

File tree

crates/loader/src/lib.rs

Lines changed: 94 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,31 @@ fn download_asset(client: &Client, asset: &Asset) -> Result<(), Box<dyn std::err
6969
.timeout(Duration::from_secs(30))
7070
.send()?;
7171

72+
// Check if response is successful
73+
if !resp.status().is_success() {
74+
return Err(format!("HTTP error: {}", resp.status()).into());
75+
}
76+
7277
// Create output path and temporary file for safe downloading
7378
let mut out_path = PathBuf::from(DEST_DIR);
7479
out_path.push(&asset.name);
7580

81+
// Ensure parent directory exists
82+
if let Some(parent) = out_path.parent() {
83+
fs::create_dir_all(parent)?;
84+
}
85+
7686
let tmp_path = out_path.with_extension("tmp");
7787
let mut file = fs::File::create(&tmp_path)?;
7888
copy(&mut resp, &mut file)?;
89+
90+
// Verify file was written and has content
91+
let metadata = fs::metadata(&tmp_path)?;
92+
if metadata.len() == 0 {
93+
fs::remove_file(&tmp_path)?;
94+
return Err("Downloaded file is empty".into());
95+
}
96+
7997
// Atomically rename temporary file to final destination
8098
fs::rename(tmp_path, &out_path)?;
8199

@@ -89,11 +107,28 @@ fn delegate_to_real_loader(lua: State) -> i32 {
89107
let suffix = get_platform_suffix();
90108
let lib_name = format!("{}/gmsv_gmod_integration_{}.dll", DEST_DIR, suffix);
91109

92-
let lib = libloading::Library::new(&lib_name)
93-
.unwrap_or_else(|_| panic!("Cannot load real integration: {}", lib_name));
110+
// Check if file exists before trying to load
111+
if !std::path::Path::new(&lib_name).exists() {
112+
print_log(&format!("Real integration file not found: {}", lib_name));
113+
return 1;
114+
}
115+
116+
let lib = match libloading::Library::new(&lib_name) {
117+
Ok(lib) => lib,
118+
Err(e) => {
119+
print_log(&format!("Failed to load real integration: {}", e));
120+
return 1;
121+
}
122+
};
123+
94124
// Get the gmod13_open function from the real integration
95-
let func: libloading::Symbol<unsafe extern "C" fn(State) -> i32> =
96-
lib.get(b"gmod13_open").expect("symbol not found");
125+
let func: libloading::Symbol<unsafe extern "C" fn(State) -> i32> = match lib.get(b"gmod13_open") {
126+
Ok(func) => func,
127+
Err(e) => {
128+
print_log(&format!("Failed to find gmod13_open symbol: {}", e));
129+
return 1;
130+
}
131+
};
97132

98133
print_log("Delegated to real integration");
99134
func(lua)
@@ -107,7 +142,7 @@ fn gmod13_open(lua: State) -> i32 {
107142
// Ensure destination directory exists
108143
if let Err(e) = fs::create_dir_all(DEST_DIR) {
109144
print_log(&format!("Failed to create directory: {}", e));
110-
return delegate_to_real_loader(lua);
145+
return 1; // Don't delegate if we can't even create directories
111146
}
112147

113148
let mut version_cache = load_loader_version_cache();
@@ -122,17 +157,33 @@ fn gmod13_open(lua: State) -> i32 {
122157
let release: Release = match client
123158
.get(API_LATEST)
124159
.header("User-Agent", "Gmod-Auto-Loader")
160+
.timeout(Duration::from_secs(30))
125161
.send()
126162
.and_then(|r| r.error_for_status())
127163
.and_then(|r| r.json())
128164
{
129165
Ok(r) => r,
130166
Err(e) => {
131167
print_log(&format!("Error fetching release: {}", e));
132-
return delegate_to_real_loader(lua);
168+
// If file exists, still try to delegate
169+
if file_exists {
170+
return delegate_to_real_loader(lua);
171+
}
172+
return 1;
133173
}
134174
};
135175

176+
// Validate release data
177+
if release.tag_name.is_empty() {
178+
print_log("Invalid release: empty tag name");
179+
return delegate_to_real_loader(lua);
180+
}
181+
182+
if release.assets.is_empty() {
183+
print_log("No assets found in release");
184+
return delegate_to_real_loader(lua);
185+
}
186+
136187
// Skip update if version matches and file exists
137188
if let Some(current_version) = &version_cache.gmod_integration_loader {
138189
if current_version == &release.tag_name && file_exists {
@@ -151,20 +202,34 @@ fn gmod13_open(lua: State) -> i32 {
151202

152203
// Download the appropriate binary for current platform
153204
let target_asset = format!("gmsv_gmod_integration_{}.dll", suffix);
205+
let mut found_asset = false;
154206

155207
for asset in &release.assets {
156208
if asset.name == target_asset {
209+
found_asset = true;
157210
if let Err(e) = download_asset(&client, asset) {
158211
print_log(&format!("Failed to download {}: {}", asset.name, e));
212+
// Clean up any partial download
213+
let partial_path = PathBuf::from(DEST_DIR).join(&asset.name);
214+
let _ = fs::remove_file(partial_path);
159215
return delegate_to_real_loader(lua);
160216
}
161217
break;
162218
}
163219
}
164220

221+
if !found_asset {
222+
print_log(&format!("No matching asset found for platform: {}", suffix));
223+
return delegate_to_real_loader(lua);
224+
}
225+
165226
// Update version cache with new version
166227
version_cache.gmod_integration_loader = Some(release.tag_name);
167-
save_loader_version_cache(&version_cache);
228+
if let Err(e) = serde_json::to_string_pretty(&version_cache)
229+
.map_err(|e| e.into())
230+
.and_then(|content| fs::write(VERSION_FILE, content).map_err(|e| e.into())) {
231+
print_log(&format!("Failed to save version cache: {}", e));
232+
}
168233

169234
print_log("Update completed, delegating to real integration");
170235
delegate_to_real_loader(lua)
@@ -177,10 +242,28 @@ fn gmod13_close(lua: State) -> i32 {
177242
let suffix = get_platform_suffix();
178243
let lib_name = format!("{}/gmsv_gmod_integration_{}.dll", DEST_DIR, suffix);
179244

180-
let lib = libloading::Library::new(&lib_name)
181-
.unwrap_or_else(|_| panic!("Cannot load real integration: {}", lib_name));
182-
let func: libloading::Symbol<unsafe extern "C" fn(State) -> i32> =
183-
lib.get(b"gmod13_close").expect("symbol not found");
245+
// Check if file exists before trying to load
246+
if !std::path::Path::new(&lib_name).exists() {
247+
print_log(&format!("Real integration file not found during close: {}", lib_name));
248+
return 0; // Don't fail the close operation
249+
}
250+
251+
let lib = match libloading::Library::new(&lib_name) {
252+
Ok(lib) => lib,
253+
Err(e) => {
254+
print_log(&format!("Failed to load real integration during close: {}", e));
255+
return 0; // Don't fail the close operation
256+
}
257+
};
258+
259+
let func: libloading::Symbol<unsafe extern "C" fn(State) -> i32> = match lib.get(b"gmod13_close") {
260+
Ok(func) => func,
261+
Err(e) => {
262+
print_log(&format!("Failed to find gmod13_close symbol: {}", e));
263+
return 0; // Don't fail the close operation
264+
}
265+
};
266+
184267
func(lua)
185268
}
186269
}

0 commit comments

Comments
 (0)