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
|
return False
|
||||||
|
|
||||||
def is_updater_running() -> bool:
|
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":
|
if os.name == "nt":
|
||||||
try:
|
try:
|
||||||
import ctypes
|
import ctypes
|
||||||
|
|
@ -847,9 +851,9 @@ def is_updater_running() -> bool:
|
||||||
if kernel32.Process32First(h_snap, ctypes.byref(pe)):
|
if kernel32.Process32First(h_snap, ctypes.byref(pe)):
|
||||||
while True:
|
while True:
|
||||||
name = pe.szExeFile.decode('mbcs', errors='ignore').lower()
|
name = pe.szExeFile.decode('mbcs', errors='ignore').lower()
|
||||||
if name in ("setup.exe", "installer.exe", "update.exe") or \
|
# Strictly match CurseForge or Overwolf installer/updater processes
|
||||||
("curseforge" in name and any(k in name for k in ("update", "setup", "install"))) or \
|
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 ("update", "setup", "install"))):
|
('overwolf' in name and any(k in name for k in ('installer', 'updater', 'setup'))):
|
||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
if not kernel32.Process32Next(h_snap, ctypes.byref(pe)):
|
if not kernel32.Process32Next(h_snap, ctypes.byref(pe)):
|
||||||
|
|
@ -860,22 +864,6 @@ def is_updater_running() -> bool:
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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
|
return False
|
||||||
|
|
||||||
def is_splash_running() -> bool:
|
def is_splash_running() -> bool:
|
||||||
|
|
@ -1224,16 +1212,22 @@ def run_watcher_loop():
|
||||||
if is_splash_running():
|
if is_splash_running():
|
||||||
return
|
return
|
||||||
|
|
||||||
needs_patch = not is_curseforge_patched(cf_dir) or is_updater_running()
|
# ONLY trigger if CurseForge is genuinely unpatched
|
||||||
if needs_patch:
|
if not is_curseforge_patched(cf_dir):
|
||||||
if now - last_splash_launch < 12.0:
|
if now - last_splash_launch < 15.0:
|
||||||
return
|
return
|
||||||
last_splash_launch = now
|
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()
|
ok = launch_splash_updater()
|
||||||
if not ok:
|
if not ok:
|
||||||
wlog("Could not launch Splash Updater; falling back to background patch.")
|
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:
|
if settled:
|
||||||
kill_curseforge()
|
kill_curseforge()
|
||||||
success = False
|
success = False
|
||||||
|
|
@ -1244,7 +1238,8 @@ def run_watcher_loop():
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
if success:
|
if success:
|
||||||
wlog("★ CurseForge successfully re-patched after update!")
|
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:
|
else:
|
||||||
wlog("Failed to re-patch CurseForge after update.")
|
wlog("Failed to re-patch CurseForge after update.")
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -642,6 +642,14 @@ class SplashUpdaterWindow:
|
||||||
time.sleep(3.0)
|
time.sleep(3.0)
|
||||||
return
|
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:
|
if self.target_pid:
|
||||||
self.root.after(0, self.set_status, "Waiting for previous instance to exit...", f"CurseForge PID {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):
|
for _ in range(30):
|
||||||
|
|
@ -658,6 +666,7 @@ class SplashUpdaterWindow:
|
||||||
elif "settle" in msg.lower() or "mb" in msg.lower():
|
elif "settle" in msg.lower() or "mb" in msg.lower():
|
||||||
self.root.after(0, self.set_status, "Finalizing update files...", msg)
|
self.root.after(0, self.set_status, "Finalizing update files...", msg)
|
||||||
elif "silenc" in msg.lower():
|
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...")
|
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)
|
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)
|
time.sleep(2.0)
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
self.root.after(0, self.set_status, "✓ Complete! Launching BetterCurseForge...", "Enjoy your ad-free experience!", "success")
|
should_launch = was_running or was_intercepted[0] or (self.target_pid is not None)
|
||||||
exe = cf_dir / "CurseForge.exe"
|
if should_launch:
|
||||||
if exe.exists():
|
self.root.after(0, self.set_status, "✓ Complete! Launching BetterCurseForge...", "Enjoy your ad-free experience!", "success")
|
||||||
creation_flags = 0x00000008 | 0x00000200 if os.name == "nt" else 0 # DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP
|
exe = cf_dir / "CurseForge.exe"
|
||||||
subprocess.Popen([str(exe)], cwd=str(cf_dir), creationflags=creation_flags)
|
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)
|
time.sleep(0.9)
|
||||||
else:
|
else:
|
||||||
self.root.after(0, self.set_status, "Failed to apply patch.", "Check watcher.log for details.", "error")
|
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"):
|
if arg in ("--watch", "watch"):
|
||||||
patch_core.run_watcher_loop()
|
patch_core.run_watcher_loop()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif arg in ("--repatch-splash", "--splash", "splash"):
|
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
|
|
||||||
splash = SplashUpdaterWindow(pid)
|
|
||||||
splash.run()
|
|
||||||
sys.exit(0)
|
|
||||||
elif arg in ("--wait-for-update", "wait-for-update"):
|
|
||||||
pid = int(sys.argv[2]) if len(sys.argv) > 2 and sys.argv[2].isdigit() else None
|
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 = SplashUpdaterWindow(pid)
|
||||||
splash.run()
|
splash.run()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue