fix(updater): prevent false positive triggers and unwanted app launches
This commit is contained in:
parent
d1a415eb86
commit
1581dcd48b
2 changed files with 42 additions and 36 deletions
|
|
@ -820,6 +820,10 @@ def is_curseforge_running() -> bool:
|
|||
return False
|
||||
|
||||
def is_updater_running() -> bool:
|
||||
"""
|
||||
Checks if a CurseForge or Overwolf updater/installer process is currently running.
|
||||
Strictly scoped to avoid false positives from unrelated apps (Discord, Logitech, etc.).
|
||||
"""
|
||||
if os.name == "nt":
|
||||
try:
|
||||
import ctypes
|
||||
|
|
@ -847,9 +851,9 @@ def is_updater_running() -> bool:
|
|||
if kernel32.Process32First(h_snap, ctypes.byref(pe)):
|
||||
while True:
|
||||
name = pe.szExeFile.decode('mbcs', errors='ignore').lower()
|
||||
if name in ("setup.exe", "installer.exe", "update.exe") or \
|
||||
("curseforge" in name and any(k in name for k in ("update", "setup", "install"))) or \
|
||||
("overwolf" in name and any(k in name for k in ("update", "setup", "install"))):
|
||||
# Strictly match CurseForge or Overwolf installer/updater processes
|
||||
if ('curseforge' in name and any(k in name for k in ('update', 'setup', 'install'))) or \
|
||||
('overwolf' in name and any(k in name for k in ('installer', 'updater', 'setup'))):
|
||||
found = True
|
||||
break
|
||||
if not kernel32.Process32Next(h_snap, ctypes.byref(pe)):
|
||||
|
|
@ -860,22 +864,6 @@ def is_updater_running() -> bool:
|
|||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
out = subprocess.check_output(
|
||||
["tasklist", "/FO", "CSV", "/NH"],
|
||||
text=True, stderr=subprocess.DEVNULL, creationflags=CREATE_NO_WINDOW
|
||||
)
|
||||
for line in out.splitlines():
|
||||
line = line.strip().lower()
|
||||
if not line:
|
||||
continue
|
||||
parts = [p.strip('"') for p in line.split('","')]
|
||||
if parts:
|
||||
pname = parts[0]
|
||||
if pname in ("setup.exe", "installer.exe") or ("curseforge" in pname and "updater" in pname):
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
def is_splash_running() -> bool:
|
||||
|
|
@ -1224,16 +1212,22 @@ def run_watcher_loop():
|
|||
if is_splash_running():
|
||||
return
|
||||
|
||||
needs_patch = not is_curseforge_patched(cf_dir) or is_updater_running()
|
||||
if needs_patch:
|
||||
if now - last_splash_launch < 12.0:
|
||||
# ONLY trigger if CurseForge is genuinely unpatched
|
||||
if not is_curseforge_patched(cf_dir):
|
||||
if now - last_splash_launch < 15.0:
|
||||
return
|
||||
last_splash_launch = now
|
||||
wlog(f"CurseForge update or unpatched state detected ({trigger_reason}). Launching Splash Updater...")
|
||||
wlog(f"CurseForge unpatched state detected ({trigger_reason}). Launching Splash Updater...")
|
||||
ok = launch_splash_updater()
|
||||
if not ok:
|
||||
wlog("Could not launch Splash Updater; falling back to background patch.")
|
||||
settled = wait_for_files_settled(cf_dir, timeout=90.0, log_fn=wlog, intercept_unpatched=True)
|
||||
was_running = is_curseforge_running()
|
||||
was_intercepted = [False]
|
||||
def on_fb_log(msg: str):
|
||||
if "silenc" in msg.lower():
|
||||
was_intercepted[0] = True
|
||||
wlog(msg)
|
||||
settled = wait_for_files_settled(cf_dir, timeout=90.0, log_fn=on_fb_log, intercept_unpatched=True)
|
||||
if settled:
|
||||
kill_curseforge()
|
||||
success = False
|
||||
|
|
@ -1244,7 +1238,8 @@ def run_watcher_loop():
|
|||
time.sleep(2)
|
||||
if success:
|
||||
wlog("★ CurseForge successfully re-patched after update!")
|
||||
subprocess.Popen([str(cf_dir / "CurseForge.exe")], creationflags=CREATE_NO_WINDOW)
|
||||
if was_running or was_intercepted[0]:
|
||||
subprocess.Popen([str(cf_dir / "CurseForge.exe")], creationflags=CREATE_NO_WINDOW)
|
||||
else:
|
||||
wlog("Failed to re-patch CurseForge after update.")
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -642,6 +642,14 @@ class SplashUpdaterWindow:
|
|||
time.sleep(3.0)
|
||||
return
|
||||
|
||||
# Early guard: if no target_pid, CurseForge is already patched, and no updater is running, exit immediately
|
||||
if not self.target_pid and patch_core.is_curseforge_patched(cf_dir) and not patch_core.is_updater_running():
|
||||
self.root.after(0, self.root.destroy)
|
||||
return
|
||||
|
||||
was_running = patch_core.is_curseforge_running() or (self.target_pid is not None)
|
||||
was_intercepted = [False]
|
||||
|
||||
if self.target_pid:
|
||||
self.root.after(0, self.set_status, "Waiting for previous instance to exit...", f"CurseForge PID {self.target_pid}")
|
||||
for _ in range(30):
|
||||
|
|
@ -658,6 +666,7 @@ class SplashUpdaterWindow:
|
|||
elif "settle" in msg.lower() or "mb" in msg.lower():
|
||||
self.root.after(0, self.set_status, "Finalizing update files...", msg)
|
||||
elif "silenc" in msg.lower():
|
||||
was_intercepted[0] = True
|
||||
self.root.after(0, self.set_status, "Silenced vanilla launcher in background...", "Preventing unpatched window display...")
|
||||
|
||||
settled = patch_core.wait_for_files_settled(cf_dir, timeout=90.0, log_fn=on_settle_log, intercept_unpatched=True)
|
||||
|
|
@ -696,11 +705,15 @@ class SplashUpdaterWindow:
|
|||
time.sleep(2.0)
|
||||
|
||||
if success:
|
||||
self.root.after(0, self.set_status, "✓ Complete! Launching BetterCurseForge...", "Enjoy your ad-free experience!", "success")
|
||||
exe = cf_dir / "CurseForge.exe"
|
||||
if exe.exists():
|
||||
creation_flags = 0x00000008 | 0x00000200 if os.name == "nt" else 0 # DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP
|
||||
subprocess.Popen([str(exe)], cwd=str(cf_dir), creationflags=creation_flags)
|
||||
should_launch = was_running or was_intercepted[0] or (self.target_pid is not None)
|
||||
if should_launch:
|
||||
self.root.after(0, self.set_status, "✓ Complete! Launching BetterCurseForge...", "Enjoy your ad-free experience!", "success")
|
||||
exe = cf_dir / "CurseForge.exe"
|
||||
if exe.exists():
|
||||
creation_flags = 0x00000008 | 0x00000200 if os.name == "nt" else 0 # DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP
|
||||
subprocess.Popen([str(exe)], cwd=str(cf_dir), creationflags=creation_flags)
|
||||
else:
|
||||
self.root.after(0, self.set_status, "✓ Complete! BetterCurseForge is updated.", "CurseForge updated in background.", "success")
|
||||
time.sleep(0.9)
|
||||
else:
|
||||
self.root.after(0, self.set_status, "Failed to apply patch.", "Check watcher.log for details.", "error")
|
||||
|
|
@ -723,13 +736,11 @@ if __name__ == "__main__":
|
|||
if arg in ("--watch", "watch"):
|
||||
patch_core.run_watcher_loop()
|
||||
sys.exit(0)
|
||||
elif arg in ("--repatch-splash", "--splash", "splash"):
|
||||
pid = int(sys.argv[2]) if len(sys.argv) > 2 and sys.argv[2].isdigit() else None
|
||||
splash = SplashUpdaterWindow(pid)
|
||||
splash.run()
|
||||
sys.exit(0)
|
||||
elif arg in ("--wait-for-update", "wait-for-update"):
|
||||
elif arg in ("--repatch-splash", "--splash", "splash", "--wait-for-update", "wait-for-update"):
|
||||
pid = int(sys.argv[2]) if len(sys.argv) > 2 and sys.argv[2].isdigit() else None
|
||||
cf = patch_core.get_curseforge_dir()
|
||||
if not pid and cf and patch_core.is_curseforge_patched(cf) and not patch_core.is_updater_running():
|
||||
sys.exit(0)
|
||||
splash = SplashUpdaterWindow(pid)
|
||||
splash.run()
|
||||
sys.exit(0)
|
||||
|
|
|
|||
Loading…
Reference in a new issue