151 lines
6.6 KiB
Python
151 lines
6.6 KiB
Python
import threading
|
|||
|
|
|
||
|
|
from PySide6.QtCore import QObject, Signal
|
||
|
|
from PySide6.QtWidgets import (
|
||
|
|
QComboBox,
|
||
|
|
QDialog,
|
||
|
|
QDialogButtonBox,
|
||
|
|
QLabel,
|
||
|
|
QListWidget,
|
||
|
|
QPushButton,
|
||
|
|
QVBoxLayout,
|
||
|
|
)
|
||
|
|
|
||
|
|
from manager.game import InstallableJavaRuntime, JavaManager, JavaRuntime
|
||
|
|
|
||
|
|
|
||
|
|
class InstallJavaDialog(QDialog):
|
||
|
|
"""Install and register launcher-managed Mojang Java runtimes."""
|
||
|
|
|
||
|
|
class WorkerSignals(QObject):
|
||
|
|
components_loaded = Signal(object, object)
|
||
|
|
installed = Signal(object, object)
|
||
|
|
|
||
|
|
runtime_installed = Signal(object)
|
||
|
|
|
||
|
|
def __init__(self, app, manifest: dict | None = None, parent=None):
|
||
|
|
super().__init__(parent)
|
||
|
|
self.app = app
|
||
|
|
self.java_manager: JavaManager = app.game_manager.java
|
||
|
|
self.manifest = manifest or {}
|
||
|
|
self.worker_signals = self.WorkerSignals(self)
|
||
|
|
self.worker_signals.components_loaded.connect(self._components_loaded)
|
||
|
|
self.worker_signals.installed.connect(self._installed)
|
||
|
|
|
||
|
|
self.setWindowTitle(self.app.tr_text("Install Java"))
|
||
|
|
self.setMinimumWidth(480)
|
||
|
|
layout = QVBoxLayout(self)
|
||
|
|
self.requirement_label = QLabel(self._requirement_text())
|
||
|
|
self.runtime_list = QListWidget()
|
||
|
|
self.component_dropdown = QComboBox()
|
||
|
|
self.component_dropdown.setEditable(False)
|
||
|
|
self.status_label = QLabel(self.app.tr_text("Loading available Mojang runtimes..."))
|
||
|
|
self.install_button = QPushButton(self.app.tr_text("Install"))
|
||
|
|
buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
|
||
|
|
|
||
|
|
layout.addWidget(self.requirement_label)
|
||
|
|
layout.addWidget(QLabel(self.app.tr_text("Detected Java runtimes")))
|
||
|
|
layout.addWidget(self.runtime_list)
|
||
|
|
layout.addWidget(QLabel(self.app.tr_text("Mojang runtime component")))
|
||
|
|
layout.addWidget(self.component_dropdown)
|
||
|
|
layout.addWidget(self.status_label)
|
||
|
|
layout.addWidget(self.install_button)
|
||
|
|
layout.addWidget(buttons)
|
||
|
|
|
||
|
|
buttons.rejected.connect(self.reject)
|
||
|
|
self.install_button.clicked.connect(self.install_selected)
|
||
|
|
self._refresh_runtime_list()
|
||
|
|
threading.Thread(target=self._load_components, daemon=True).start()
|
||
|
|
|
||
|
|
def _requirement_text(self) -> str:
|
||
|
|
requirement = self.manifest.get("javaVersion") or {}
|
||
|
|
major = requirement.get("majorVersion")
|
||
|
|
component = requirement.get("component")
|
||
|
|
if major:
|
||
|
|
return self.app.tr_text(
|
||
|
|
"This version requires Java {version}.", version=major
|
||
|
|
) + f" ({component or 'component unknown'})"
|
||
|
|
return self.app.tr_text("Choose a Mojang Java runtime component to install.")
|
||
|
|
|
||
|
|
def _refresh_runtime_list(self) -> None:
|
||
|
|
self.runtime_list.clear()
|
||
|
|
for runtime in self.java_manager.list_runtimes():
|
||
|
|
state = "available" if runtime.valid else "missing"
|
||
|
|
self.runtime_list.addItem(
|
||
|
|
f"Java {runtime.major_version} | {runtime.source} | {state}\n{runtime.executable}"
|
||
|
|
)
|
||
|
|
|
||
|
|
def _load_components(self) -> None:
|
||
|
|
try:
|
||
|
|
runtimes = self.java_manager.list_installable_runtimes()
|
||
|
|
self.worker_signals.components_loaded.emit(runtimes, None)
|
||
|
|
except Exception as exc:
|
||
|
|
self.worker_signals.components_loaded.emit([], exc)
|
||
|
|
|
||
|
|
def _components_loaded(self, runtimes: list[InstallableJavaRuntime], error) -> None:
|
||
|
|
requirement = self.manifest.get("javaVersion") or {}
|
||
|
|
required = requirement.get("component")
|
||
|
|
required_major = requirement.get("majorVersion")
|
||
|
|
self.component_dropdown.clear()
|
||
|
|
for runtime in runtimes:
|
||
|
|
major = runtime.major_version
|
||
|
|
if runtime.component == required and required_major is not None:
|
||
|
|
# The Minecraft version manifest is authoritative for its own
|
||
|
|
# runtime requirement if Mojang's version name is unusual.
|
||
|
|
major = int(required_major)
|
||
|
|
label = f"Java {major} | {runtime.component}" if major is not None else runtime.component
|
||
|
|
if runtime.raw_version:
|
||
|
|
label += f" ({runtime.raw_version})"
|
||
|
|
self.component_dropdown.addItem(label, runtime.component)
|
||
|
|
if required:
|
||
|
|
index = self.component_dropdown.findData(required)
|
||
|
|
if index < 0:
|
||
|
|
label = f"Java {required_major} | {required}" if required_major else required
|
||
|
|
self.component_dropdown.addItem(label, required)
|
||
|
|
index = self.component_dropdown.count() - 1
|
||
|
|
self.component_dropdown.setCurrentIndex(index)
|
||
|
|
self.install_button.setEnabled(self.component_dropdown.count() > 0)
|
||
|
|
self.status_label.setText(
|
||
|
|
self.app.tr_text("Unable to load component list: {error}", error=error)
|
||
|
|
if error else self.app.tr_text("Ready to install.")
|
||
|
|
)
|
||
|
|
|
||
|
|
def install_selected(self) -> None:
|
||
|
|
component = self.component_dropdown.currentData()
|
||
|
|
if not component:
|
||
|
|
self.status_label.setText(self.app.tr_text("Select a runtime component."))
|
||
|
|
return
|
||
|
|
self.install_button.setEnabled(False)
|
||
|
|
self.component_dropdown.setEnabled(False)
|
||
|
|
self.status_label.setText(self.app.tr_text("Installing {component}...", component=component))
|
||
|
|
callback, task_id = self.app.game_manager.manifest.create_download_progress_callback(
|
||
|
|
self.app.tr_text("Installing Java ({component})", component=component)
|
||
|
|
)
|
||
|
|
|
||
|
|
def worker():
|
||
|
|
try:
|
||
|
|
runtime = self.java_manager.install_runtime(
|
||
|
|
component,
|
||
|
|
progress_callback=callback,
|
||
|
|
redownload_callback=self.app.game_manager.game_file.redownload_files,
|
||
|
|
)
|
||
|
|
self.app.widget_manager.download_signal.finished.emit(task_id)
|
||
|
|
self.worker_signals.installed.emit(runtime, None)
|
||
|
|
except Exception as exc:
|
||
|
|
self.app.widget_manager.download_signal.failed.emit(task_id, str(exc))
|
||
|
|
self.worker_signals.installed.emit(None, exc)
|
||
|
|
|
||
|
|
threading.Thread(target=worker, daemon=True).start()
|
||
|
|
|
||
|
|
def _installed(self, runtime: JavaRuntime | None, error) -> None:
|
||
|
|
self.install_button.setEnabled(True)
|
||
|
|
self.component_dropdown.setEnabled(True)
|
||
|
|
if error:
|
||
|
|
self.status_label.setText(self.app.tr_text("Installation failed: {error}", error=error))
|
||
|
|
return
|
||
|
|
self._refresh_runtime_list()
|
||
|
|
self.status_label.setText(self.app.tr_text(
|
||
|
|
"Java {version} installed successfully.", version=runtime.major_version
|
||
|
|
))
|
||
|
|
self.runtime_installed.emit(runtime)
|