Skip to content

Commit 605a3f1

Browse files
authored
Add files via upload
1 parent f342984 commit 605a3f1

12 files changed

Lines changed: 123 additions & 174 deletions

File tree

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "zgamelib"
3-
version = "0.4.1"
3+
version = "0.5.0"
44
description = "ZGameLib - Personal Game Library"
55
authors = ["TheHolyOneZ"]
66
license = "MIT"

src-tauri/src/commands/games.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,17 @@ pub fn toggle_favorite(state: State<DbState>, id: String) -> Result<bool, String
8282
Ok(game.is_favorite)
8383
}
8484

85-
// Notes
85+
#[tauri::command]
86+
pub fn check_exe_health(state: State<DbState>, id: String) -> Result<bool, String> {
87+
let conn = state.0.lock().map_err(|e| e.to_string())?;
88+
let game = queries::get_game_by_id(&conn, &id)
89+
.map_err(|e| e.to_string())?
90+
.ok_or("Game not found")?;
91+
match &game.exe_path {
92+
Some(p) if !p.is_empty() => Ok(std::path::Path::new(p).exists()),
93+
_ => Ok(false),
94+
}
95+
}
8696

8797
#[tauri::command]
8898
pub fn get_notes(state: State<DbState>, game_id: String) -> Result<Vec<Note>, String> {
@@ -108,7 +118,6 @@ pub fn create_note(state: State<DbState>, game_id: String, content: String) -> R
108118
#[tauri::command]
109119
pub fn update_note(state: State<DbState>, id: String, content: String) -> Result<Note, String> {
110120
let conn = state.0.lock().map_err(|e| e.to_string())?;
111-
// fetch existing note
112121
let mut stmt = conn.prepare("SELECT id, game_id, content, created_at, updated_at FROM notes WHERE id=?1")
113122
.map_err(|e| e.to_string())?;
114123
let mut rows = stmt.query_map(rusqlite::params![id], |row| {

src-tauri/src/commands/launcher.rs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ impl ActivePids {
1212
}
1313
}
1414

15-
// ── Windows-only process helpers (no extra crates needed) ─────────────────
16-
1715
#[cfg(windows)]
1816
use std::os::windows::process::CommandExt;
1917
#[cfg(windows)]
@@ -50,12 +48,9 @@ fn is_pid_running(pid: u32) -> bool {
5048
.output()
5149
.map(|o| String::from_utf8_lossy(&o.stdout).to_string())
5250
.unwrap_or_default();
53-
// Output has a data row if running; "INFO: No tasks..." if not
5451
out.contains('"')
5552
}
5653

57-
// ── Session-end helper: update DB + emit + restore window ─────────────────
58-
5954
fn finish_session(app: &AppHandle, game_id: &str, elapsed_mins: i64) {
6055
let now = Utc::now().to_rfc3339();
6156
{
@@ -69,18 +64,14 @@ fn finish_session(app: &AppHandle, game_id: &str, elapsed_mins: i64) {
6964
}
7065
}
7166
let _ = app.emit("game-session-ended", game_id);
72-
// Restore window after game exits
7367
if let Some(win) = app.get_webview_window("main") {
7468
let _ = win.unminimize();
7569
let _ = win.set_focus();
7670
}
7771
}
7872

79-
// ── Commands ──────────────────────────────────────────────────────────────
80-
8173
#[tauri::command]
8274
pub fn launch_game(app: AppHandle, state: State<DbState>, id: String) -> Result<(), String> {
83-
// Phase 1: gather data, release mutex
8475
let (exe_path, minimize) = {
8576
let conn = state.0.lock().map_err(|e| e.to_string())?;
8677
let game = queries::get_game_by_id(&conn, &id)
@@ -92,12 +83,10 @@ pub fn launch_game(app: AppHandle, state: State<DbState>, id: String) -> Result<
9283
(exe, min)
9384
};
9485

95-
// Phase 2: spawn process
9686
let child = std::process::Command::new(&exe_path)
9787
.spawn()
9888
.map_err(|e| format!("Failed to launch: {}", e))?;
9989

100-
// Record last_played immediately
10190
let now = Utc::now().to_rfc3339();
10291
{
10392
let conn = state.0.lock().map_err(|e| e.to_string())?;
@@ -110,16 +99,13 @@ pub fn launch_game(app: AppHandle, state: State<DbState>, id: String) -> Result<
11099
if minimize {
111100
let app_min = app.clone();
112101
std::thread::spawn(move || {
113-
// Short delay so the game window can appear and grab focus first,
114-
// preventing Windows from immediately re-focusing ZGameLib
115102
std::thread::sleep(std::time::Duration::from_millis(400));
116103
if let Some(win) = app_min.get_webview_window("main") {
117104
let _ = win.minimize();
118105
}
119106
});
120107
}
121108

122-
// Phase 3: background thread — wait for exit, record playtime
123109
let app_clone = app.clone();
124110
let game_id = id.clone();
125111
let start = std::time::Instant::now();
@@ -142,7 +128,6 @@ pub fn launch_steam_game(
142128
app_id: String,
143129
game_id: String,
144130
) -> Result<(), String> {
145-
// Get exe_path for process monitoring + minimize setting
146131
let (exe_path, minimize) = {
147132
let conn = state.0.lock().map_err(|e| e.to_string())?;
148133
let exe = queries::get_game_by_id(&conn, &game_id)
@@ -153,11 +138,9 @@ pub fn launch_steam_game(
153138
(exe, min)
154139
};
155140

156-
// Launch via Steam URI
157141
open::that(format!("steam://run/{}", app_id))
158142
.map_err(|e| format!("Failed to launch Steam game: {}", e))?;
159143

160-
// Record last_played immediately
161144
let now = Utc::now().to_rfc3339();
162145
{
163146
let conn = state.0.lock().map_err(|e| e.to_string())?;
@@ -177,7 +160,6 @@ pub fn launch_steam_game(
177160
});
178161
}
179162

180-
// Background: find game process by exe name, wait for exit, record playtime
181163
if let Some(exe) = exe_path {
182164
let app_clone = app.clone();
183165
let gid = game_id.clone();
@@ -195,7 +177,6 @@ pub fn launch_steam_game(
195177

196178
let start = std::time::Instant::now();
197179

198-
// Wait up to 120 s for process to appear (Steam can be slow to launch)
199180
let mut pid: Option<u32> = None;
200181
for _ in 0..120 {
201182
if let Some(p) = find_pid_by_exe_name(&exe_name) {
@@ -212,7 +193,6 @@ pub fn launch_steam_game(
212193
return;
213194
}
214195
}
215-
// Poll every 5 s until the process disappears
216196
loop {
217197
std::thread::sleep(std::time::Duration::from_secs(5));
218198
if !is_pid_running(pid) { break; }
@@ -236,7 +216,6 @@ pub fn launch_epic_game(
236216
app_name: String,
237217
game_id: String,
238218
) -> Result<(), String> {
239-
// Get exe_path for process monitoring + minimize setting
240219
let (exe_path, minimize) = {
241220
let conn = state.0.lock().map_err(|e| e.to_string())?;
242221
let exe = queries::get_game_by_id(&conn, &game_id)
@@ -247,15 +226,13 @@ pub fn launch_epic_game(
247226
(exe, min)
248227
};
249228

250-
// Launch via Epic Games Launcher URI
251229
let uri = format!(
252230
"com.epicgames.launcher://apps/{}?action=launch&silent=true",
253231
app_name
254232
);
255233
open::that(&uri)
256234
.map_err(|e| format!("Failed to launch Epic game: {}", e))?;
257235

258-
// Record last_played immediately
259236
let now = Utc::now().to_rfc3339();
260237
{
261238
let conn = state.0.lock().map_err(|e| e.to_string())?;
@@ -275,7 +252,6 @@ pub fn launch_epic_game(
275252
});
276253
}
277254

278-
// Background: find game process by exe name, wait for exit, record playtime
279255
if let Some(exe) = exe_path {
280256
let app_clone = app.clone();
281257
let gid = game_id.clone();
@@ -293,7 +269,6 @@ pub fn launch_epic_game(
293269

294270
let start = std::time::Instant::now();
295271

296-
// Wait up to 180 s for process to appear (Epic launcher can be slow)
297272
let mut pid: Option<u32> = None;
298273
for _ in 0..180 {
299274
if let Some(p) = find_pid_by_exe_name(&exe_name) {
@@ -310,7 +285,6 @@ pub fn launch_epic_game(
310285
return;
311286
}
312287
}
313-
// Poll every 5 s until the process disappears
314288
loop {
315289
std::thread::sleep(std::time::Duration::from_secs(5));
316290
if !is_pid_running(pid) { break; }

src-tauri/src/commands/modloader.rs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ use std::io::Read;
33
use std::path::{Path, PathBuf};
44
use tauri::{AppHandle, Emitter};
55

6-
// ── helpers ────────────────────────────────────────────────────────────────
7-
86
fn p(install_dir: &str, sub: &str) -> PathBuf {
97
Path::new(install_dir).join(sub)
108
}
@@ -53,6 +51,7 @@ fn fetch_json(url: &str) -> Result<serde_json::Value, String> {
5351
let mut body = String::new();
5452
ureq::get(url)
5553
.set("User-Agent", "ZGameLib")
54+
.timeout(std::time::Duration::from_secs(15))
5655
.call()
5756
.map_err(|e| e.to_string())?
5857
.into_reader()
@@ -65,6 +64,7 @@ fn download_bytes(url: &str) -> Result<Vec<u8>, String> {
6564
let mut bytes = Vec::new();
6665
ureq::get(url)
6766
.set("User-Agent", "ZGameLib")
67+
.timeout(std::time::Duration::from_secs(15))
6868
.call()
6969
.map_err(|e| e.to_string())?
7070
.into_reader()
@@ -73,8 +73,6 @@ fn download_bytes(url: &str) -> Result<Vec<u8>, String> {
7373
Ok(bytes)
7474
}
7575

76-
// ── types ──────────────────────────────────────────────────────────────────
77-
7876
#[derive(Debug, serde::Serialize, Clone)]
7977
pub struct ModLoaderStatus {
8078
pub bepinex_installed: bool,
@@ -90,14 +88,11 @@ pub struct ModInfo {
9088
pub size_bytes: u64,
9189
}
9290

93-
// ── commands ───────────────────────────────────────────────────────────────
94-
9591
#[tauri::command]
9692
pub fn check_modloader_status(install_dir: String) -> Result<ModLoaderStatus, String> {
9793
let bepinex = is_bepinex_installed(&install_dir);
9894
let melonloader = is_melonloader_installed(&install_dir);
9995

100-
// If both somehow installed, prefer BepInEx for the mods folder
10196
let (mods_folder, mods) = if bepinex {
10297
let dir = bepinex_plugins_dir(&install_dir);
10398
(Some(dir.to_string_lossy().to_string()), list_mods(&dir))
@@ -152,12 +147,10 @@ pub fn install_bepinex(app: AppHandle, install_dir: String) -> Result<(), String
152147

153148
#[tauri::command]
154149
pub fn uninstall_bepinex(install_dir: String) -> Result<(), String> {
155-
// Remove BepInEx folder
156150
let bepinex_dir = p(&install_dir, "BepInEx");
157151
if bepinex_dir.exists() {
158152
fs::remove_dir_all(&bepinex_dir).map_err(|e| e.to_string())?;
159153
}
160-
// Remove hook DLLs and config files BepInEx places at game root
161154
for file in &["winhttp.dll", "doorstop_config.ini", ".doorstop_version", "changelog.txt"] {
162155
let path = p(&install_dir, file);
163156
if path.exists() { let _ = fs::remove_file(path); }
@@ -169,12 +162,9 @@ pub fn uninstall_bepinex(install_dir: String) -> Result<(), String> {
169162
pub fn install_melonloader(app: AppHandle, install_dir: String) -> Result<(), String> {
170163
emit_log(&app, "info", "Fetching MelonLoader releases from GitHub...");
171164

172-
// Use /releases list — MelonLoader ships as pre-releases so /latest often misses them
173165
let releases = fetch_json("https://api.github.com/repos/LavaGang/MelonLoader/releases?per_page=10")?;
174166
let releases_arr = releases.as_array().ok_or("Unexpected response from GitHub")?;
175167

176-
// Find the first non-draft release that has MelonLoader.x64.zip
177-
// We use the zip directly — the GUI installer has no reliable silent CLI mode
178168
let (asset_url, version) = releases_arr.iter()
179169
.filter(|r| !r["draft"].as_bool().unwrap_or(false))
180170
.find_map(|r| {
@@ -207,8 +197,6 @@ pub fn install_melonloader(app: AppHandle, install_dir: String) -> Result<(), St
207197
}
208198
}
209199

210-
// Pre-create Mods/ and Plugins/ — MelonLoader creates these on first game launch
211-
// but we want them visible immediately in the panel
212200
fs::create_dir_all(melonloader_mods_dir(&install_dir)).map_err(|e| e.to_string())?;
213201
fs::create_dir_all(p(&install_dir, "Plugins")).map_err(|e| e.to_string())?;
214202

@@ -218,12 +206,10 @@ pub fn install_melonloader(app: AppHandle, install_dir: String) -> Result<(), St
218206

219207
#[tauri::command]
220208
pub fn uninstall_melonloader(install_dir: String) -> Result<(), String> {
221-
// Remove MelonLoader folder
222209
let ml_dir = p(&install_dir, "MelonLoader");
223210
if ml_dir.exists() {
224211
fs::remove_dir_all(&ml_dir).map_err(|e| e.to_string())?;
225212
}
226-
// Remove hook files MelonLoader places at game root
227213
for file in &["version.dll", "dobby.dll"] {
228214
let path = p(&install_dir, file);
229215
if path.exists() { let _ = fs::remove_file(path); }

0 commit comments

Comments
 (0)