Cache and cpu / mem improvements for launcher when idle.
This commit is contained in:
148
ptlinux/ui.py
148
ptlinux/ui.py
@@ -28,11 +28,18 @@ from .news import (
|
||||
fetch_news,
|
||||
fetch_news_image,
|
||||
fetch_official_logo,
|
||||
read_cached_broadcast,
|
||||
read_cached_news,
|
||||
)
|
||||
from .overlay import RuntimeCompatibility, inspect_runtime_compatibility
|
||||
from .paths import AppPaths
|
||||
from .settings import Settings, ensure_settings, save_settings
|
||||
from .self_update import LauncherUpdateCheck, check_for_launcher_update
|
||||
from .self_update import (
|
||||
LauncherUpdateCheck,
|
||||
cache_launcher_update_check,
|
||||
check_for_launcher_update,
|
||||
read_cached_launcher_update_check,
|
||||
)
|
||||
|
||||
|
||||
MAX_NEWS_CARDS = 3
|
||||
@@ -43,6 +50,10 @@ NEWS_IMAGE_HEIGHT = 118
|
||||
LAUNCH_STAGE_TOTAL = 4
|
||||
EVENT_BATCH_SIZE = 48
|
||||
POLL_INTERVAL_MILLISECONDS = 33
|
||||
ACTIVE_POLL_INTERVAL_MILLISECONDS = 75
|
||||
IDLE_POLL_INTERVAL_MILLISECONDS = 500
|
||||
MAX_ACTIVITY_LOG_LINES = 5000
|
||||
ACTIVITY_LOG_TRIM_LINES = 500
|
||||
TRANSFER_RATE_WINDOW_SECONDS = 3.0
|
||||
TRANSFER_RATE_MINIMUM_INTERVAL_SECONDS = 0.2
|
||||
TRANSFER_RATE_IDLE_SECONDS = 2.0
|
||||
@@ -565,6 +576,8 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
preloader_progress: ttk.Progressbar | None = None
|
||||
settings_window: tk.Toplevel | None = None
|
||||
operation_active = False
|
||||
game_window_active = False
|
||||
activity_log_lines = 0
|
||||
operation_started_at: float | None = None
|
||||
operation_stage = 0
|
||||
operation_stage_total = 0
|
||||
@@ -586,8 +599,16 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
)
|
||||
|
||||
def append_log(value: str) -> None:
|
||||
nonlocal activity_log_lines
|
||||
rendered = value.rstrip() + "\n"
|
||||
log.configure(state="normal")
|
||||
log.insert("end", value.rstrip() + "\n")
|
||||
log.insert("end", rendered)
|
||||
activity_log_lines += rendered.count("\n")
|
||||
if activity_log_lines > MAX_ACTIVITY_LOG_LINES:
|
||||
retained_lines = MAX_ACTIVITY_LOG_LINES - ACTIVITY_LOG_TRIM_LINES
|
||||
excess = activity_log_lines - retained_lines
|
||||
log.delete("1.0", f"{excess + 1}.0")
|
||||
activity_log_lines -= excess
|
||||
log.see("end")
|
||||
log.configure(state="disabled")
|
||||
|
||||
@@ -759,7 +780,7 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
restart_on_success: bool = False,
|
||||
game_files_may_change: bool = False,
|
||||
) -> None:
|
||||
nonlocal operation_active
|
||||
nonlocal operation_active, game_window_active
|
||||
nonlocal operation_started_at
|
||||
nonlocal operation_stage
|
||||
nonlocal operation_stage_total
|
||||
@@ -767,6 +788,7 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
nonlocal operation_refreshes_compatibility
|
||||
nonlocal last_elapsed_second
|
||||
operation_active = True
|
||||
game_window_active = False
|
||||
operation_started_at = time.monotonic()
|
||||
operation_stage = 0
|
||||
operation_stage_total = 0
|
||||
@@ -1145,6 +1167,47 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
daemon=True,
|
||||
).start()
|
||||
|
||||
def show_launcher_update_check(
|
||||
check: LauncherUpdateCheck,
|
||||
*,
|
||||
log_reason: bool = True,
|
||||
) -> None:
|
||||
nonlocal launcher_update_check
|
||||
launcher_update_check = check
|
||||
if check.available and check.supported:
|
||||
launcher_update_button.configure(
|
||||
text=f"UPDATE TO {check.latest_version}",
|
||||
state="normal",
|
||||
)
|
||||
elif check.available:
|
||||
launcher_update_button.configure(
|
||||
text="MANUAL UPDATE NEEDED",
|
||||
state="normal",
|
||||
)
|
||||
else:
|
||||
launcher_update_button.configure(
|
||||
text=f"LAUNCHER {__version__}",
|
||||
state="normal",
|
||||
)
|
||||
if log_reason:
|
||||
append_log(check.reason)
|
||||
|
||||
def refresh_launcher_update_check() -> None:
|
||||
def worker() -> None:
|
||||
try:
|
||||
check = check_for_launcher_update()
|
||||
except RuntimeError as update_error:
|
||||
events.put(("launcher_update_error", str(update_error)))
|
||||
else:
|
||||
cache_launcher_update_check(paths.launcher_update_check_cache, check)
|
||||
events.put(("launcher_update_check", check))
|
||||
|
||||
threading.Thread(
|
||||
target=worker,
|
||||
name="ptlinux-self-update",
|
||||
daemon=True,
|
||||
).start()
|
||||
|
||||
def refresh_remote_content() -> None:
|
||||
def broadcast_worker() -> None:
|
||||
events.put(("broadcast", fetch_latest_broadcast(paths.broadcast_cache)))
|
||||
@@ -1160,27 +1223,17 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
else:
|
||||
events.put(("version", snapshot))
|
||||
|
||||
def launcher_update_worker() -> None:
|
||||
try:
|
||||
check = check_for_launcher_update()
|
||||
except RuntimeError as update_error:
|
||||
events.put(("launcher_update_error", str(update_error)))
|
||||
else:
|
||||
events.put(("launcher_update_check", check))
|
||||
|
||||
threading.Thread(target=broadcast_worker, name="ptlinux-broadcast", daemon=True).start()
|
||||
threading.Thread(target=news_worker, name="ptlinux-news", daemon=True).start()
|
||||
threading.Thread(target=version_worker, name="ptlinux-version", daemon=True).start()
|
||||
threading.Thread(
|
||||
target=launcher_update_worker,
|
||||
name="ptlinux-self-update",
|
||||
daemon=True,
|
||||
).start()
|
||||
refresh_launcher_update_check()
|
||||
|
||||
def finish_operation(returncode: int) -> None:
|
||||
nonlocal operation_active, operation_refreshes_compatibility, operation_started_at
|
||||
nonlocal operation_active, game_window_active
|
||||
nonlocal operation_refreshes_compatibility, operation_started_at
|
||||
refresh_compatibility = operation_refreshes_compatibility
|
||||
operation_active = False
|
||||
game_window_active = False
|
||||
operation_refreshes_compatibility = False
|
||||
operation_started_at = None
|
||||
progress_updates.clear()
|
||||
@@ -1200,7 +1253,7 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
progress_text.set("See the activity log for details.")
|
||||
|
||||
def poll_events() -> None:
|
||||
nonlocal launcher_update_check, runtime_compatibility
|
||||
nonlocal game_window_active, launcher_update_check, runtime_compatibility
|
||||
processed = 0
|
||||
try:
|
||||
while processed < EVENT_BATCH_SIZE:
|
||||
@@ -1236,6 +1289,7 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
if step > 0 and total > 0 and isinstance(message, str) and message:
|
||||
set_operation_stage(step, total, message)
|
||||
elif kind in {"game_started", "game_window_ready"}:
|
||||
game_window_active = True
|
||||
progress_bar.stop()
|
||||
status.set("Game client started.")
|
||||
close_game_preloader()
|
||||
@@ -1285,26 +1339,13 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
value,
|
||||
LauncherUpdateCheck,
|
||||
):
|
||||
launcher_update_check = value
|
||||
if value.available and value.supported:
|
||||
launcher_update_button.configure(
|
||||
text=f"UPDATE TO {value.latest_version}",
|
||||
state="normal",
|
||||
)
|
||||
elif value.available:
|
||||
launcher_update_button.configure(
|
||||
text="MANUAL UPDATE NEEDED",
|
||||
state="normal",
|
||||
)
|
||||
else:
|
||||
launcher_update_button.configure(
|
||||
text=f"LAUNCHER {__version__}",
|
||||
state="normal",
|
||||
)
|
||||
append_log(value.reason)
|
||||
show_launcher_update_check(value)
|
||||
elif kind == "launcher_update_error" and isinstance(value, str):
|
||||
launcher_update_check = None
|
||||
launcher_update_button.configure(text="CHECK LAUNCHER", state="normal")
|
||||
if launcher_update_check is None:
|
||||
launcher_update_button.configure(
|
||||
text="CHECK LAUNCHER",
|
||||
state="normal",
|
||||
)
|
||||
append_log(f"Launcher update check unavailable: {value}")
|
||||
except queue.Empty:
|
||||
pass
|
||||
@@ -1315,7 +1356,14 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
if speed_text.get() != "0 B/s" and transfer_rate.is_idle():
|
||||
speed_text.set("0 B/s")
|
||||
update_elapsed_indicator()
|
||||
root.after(POLL_INTERVAL_MILLISECONDS, poll_events)
|
||||
root.after(
|
||||
dashboard_poll_interval(
|
||||
operation_active=operation_active,
|
||||
game_window_active=game_window_active,
|
||||
pending_events=not events.empty(),
|
||||
),
|
||||
poll_events,
|
||||
)
|
||||
|
||||
def command_for(*arguments: str) -> list[str]:
|
||||
return [sys.executable, "-m", "ptlinux", *arguments]
|
||||
@@ -1327,7 +1375,7 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
if required_reason is not None:
|
||||
status.set("Checking for a compatible launcher update…")
|
||||
progress_text.set("Play remains disabled until this release is supported.")
|
||||
refresh_remote_content()
|
||||
refresh_launcher_update_check()
|
||||
return
|
||||
if not check.available:
|
||||
if required_reason is None:
|
||||
@@ -1729,6 +1777,13 @@ def run_dashboard(paths: AppPaths) -> None:
|
||||
|
||||
news_content.bind("<Configure>", update_news_viewport)
|
||||
news_canvas.bind("<Configure>", resize_news_content)
|
||||
show_broadcast(read_cached_broadcast(paths.broadcast_cache))
|
||||
show_news(read_cached_news(paths.news_cache))
|
||||
cached_launcher_update = read_cached_launcher_update_check(
|
||||
paths.launcher_update_check_cache
|
||||
)
|
||||
if cached_launcher_update is not None:
|
||||
show_launcher_update_check(cached_launcher_update, log_reason=False)
|
||||
refresh_primary_action()
|
||||
refresh_runtime_compatibility()
|
||||
poll_events()
|
||||
@@ -1747,6 +1802,21 @@ def _as_int(value: object) -> int:
|
||||
return value if isinstance(value, int) and value >= 0 else 0
|
||||
|
||||
|
||||
def dashboard_poll_interval(
|
||||
*,
|
||||
operation_active: bool,
|
||||
game_window_active: bool,
|
||||
pending_events: bool,
|
||||
) -> int:
|
||||
"""Choose a responsive poll rate while avoiding busy idle/game polling."""
|
||||
|
||||
if pending_events:
|
||||
return POLL_INTERVAL_MILLISECONDS
|
||||
if operation_active and not game_window_active:
|
||||
return ACTIVE_POLL_INTERVAL_MILLISECONDS
|
||||
return IDLE_POLL_INTERVAL_MILLISECONDS
|
||||
|
||||
|
||||
def _format_bytes(value: int) -> str:
|
||||
units = ("B", "KiB", "MiB", "GiB")
|
||||
amount = float(value)
|
||||
|
||||
Reference in New Issue
Block a user