Now vanilla (or with Fabric loader) profile creation is implemented.
This commit is contained in:
@@ -1 +1,3 @@
|
|||||||
# TestLauncher
|
# TestLauncher
|
||||||
|
|
||||||
|
# There's no stable branch because this project is still under development.
|
||||||
@@ -13,6 +13,9 @@ FABRIC_META_LOADER_VERSION_ENDPOINT_V2 = "https://meta.fabricmc.net/v2/versions/
|
|||||||
FABRIC_META_LOADER_MANIFEST_ENDPOINT_V2 = "https://meta.fabricmc.net/v2/versions/loader/{}/{}/profile/json"
|
FABRIC_META_LOADER_MANIFEST_ENDPOINT_V2 = "https://meta.fabricmc.net/v2/versions/loader/{}/{}/profile/json"
|
||||||
FABRIC_MAVEN_URL = "https://maven.fabricmc.net/"
|
FABRIC_MAVEN_URL = "https://maven.fabricmc.net/"
|
||||||
|
|
||||||
|
def get_full_version_string(version: str, loader_version: str) -> str:
|
||||||
|
return f"fabric-loader-{loader_version}-{version}"
|
||||||
|
|
||||||
def fetch_fabric_version_list(version_manifest_url: str=FABRIC_META_VERSION_ENDPOINT_V2, timeout: int=5) -> list[dict]:
|
def fetch_fabric_version_list(version_manifest_url: str=FABRIC_META_VERSION_ENDPOINT_V2, timeout: int=5) -> list[dict]:
|
||||||
try:
|
try:
|
||||||
r = requests.get(version_manifest_url, timeout=timeout)
|
r = requests.get(version_manifest_url, timeout=timeout)
|
||||||
@@ -66,6 +69,7 @@ def get_version_support_loader_list(target_version: str, loader_version_url: str
|
|||||||
-> list[dict]:
|
-> list[dict]:
|
||||||
try:
|
try:
|
||||||
r = requests.get(loader_version_url.format(target_version), timeout=timeout)
|
r = requests.get(loader_version_url.format(target_version), timeout=timeout)
|
||||||
|
r.raise_for_status()
|
||||||
except requests.exceptions.Timeout:
|
except requests.exceptions.Timeout:
|
||||||
raise VersionManifestFetchException(
|
raise VersionManifestFetchException(
|
||||||
"Timeout while fetch from {}".format(loader_version_url)
|
"Timeout while fetch from {}".format(loader_version_url)
|
||||||
@@ -75,9 +79,15 @@ def get_version_support_loader_list(target_version: str, loader_version_url: str
|
|||||||
"Connection error while fetch from {}: {}".format(loader_version_url, e)
|
"Connection error while fetch from {}: {}".format(loader_version_url, e)
|
||||||
)
|
)
|
||||||
except requests.exceptions.HTTPError as e:
|
except requests.exceptions.HTTPError as e:
|
||||||
raise VersionManifestFetchException(
|
if e.response.status_code == 404 or e.response.status_code == 400:
|
||||||
"HTTP error while fetch from {}: HTTP Status: {}".format(loader_version_url, e.response.status_code)
|
raise VersionManifestFetchException(
|
||||||
)
|
"HTTP error while fetch from {}: {}".format(loader_version_url, e),
|
||||||
|
user_message="Target version {} no fabric version found".format(target_version)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise VersionManifestFetchException(
|
||||||
|
"HTTP error while fetch from {}: HTTP Status: {}".format(loader_version_url, e.response.status_code)
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise VersionManifestFetchException(
|
raise VersionManifestFetchException(
|
||||||
"Unknown error while fetch from {}: {}".format(loader_version_url, e)
|
"Unknown error while fetch from {}: {}".format(loader_version_url, e)
|
||||||
@@ -102,7 +112,8 @@ def get_version_support_loader_list(target_version: str, loader_version_url: str
|
|||||||
|
|
||||||
if not loaders:
|
if not loaders:
|
||||||
raise VersionManifestException(
|
raise VersionManifestException(
|
||||||
"No available loader found in {}".format(loader_version_url)
|
"No available loader found in {}".format(loader_version_url),
|
||||||
|
user_message="Target version {} no fabric version found".format(target_version)
|
||||||
)
|
)
|
||||||
|
|
||||||
return loaders
|
return loaders
|
||||||
@@ -121,6 +132,7 @@ def get_loader_manifest_from_loader_data(target_version: str, loader_data: dict,
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
r = requests.get(url, timeout=5)
|
r = requests.get(url, timeout=5)
|
||||||
|
r.raise_for_status()
|
||||||
except requests.exceptions.Timeout:
|
except requests.exceptions.Timeout:
|
||||||
raise VersionManifestFetchException(
|
raise VersionManifestFetchException(
|
||||||
"Timeout while fetch from {}".format(url)
|
"Timeout while fetch from {}".format(url)
|
||||||
@@ -150,6 +162,3 @@ def get_loader_manifest_from_loader_data(target_version: str, loader_data: dict,
|
|||||||
"Unknown error while fetch from {}: {}".format(url, e)
|
"Unknown error while fetch from {}: {}".format(url, e)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -400,6 +400,48 @@ def get_core_file_download_url_and_hash(version_manifest: dict, is_server: bool=
|
|||||||
|
|
||||||
return download_url, sha1
|
return download_url, sha1
|
||||||
|
|
||||||
|
def save_version_manifest(manifest: dict, dest: Path, sha1: str | None=None) -> dict:
|
||||||
|
"""
|
||||||
|
Save version manifest with hash verify
|
||||||
|
:param manifest:
|
||||||
|
:param dest:
|
||||||
|
:param sha1:
|
||||||
|
:return:
|
||||||
|
data: dict
|
||||||
|
"""
|
||||||
|
file = FileObject(
|
||||||
|
path=dest,
|
||||||
|
sha1=sha1,
|
||||||
|
)
|
||||||
|
|
||||||
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
method = "wb" if isinstance(manifest, bytes) else "w"
|
||||||
|
with dest.open(method) as f:
|
||||||
|
if isinstance(manifest, bytes):
|
||||||
|
f.write(manifest)
|
||||||
|
elif isinstance(manifest, dict):
|
||||||
|
json.dump(manifest, f)
|
||||||
|
|
||||||
|
if sha1 is not None and not file.verify_sha1(sha1):
|
||||||
|
raise HashMismatchException(
|
||||||
|
"sha1",
|
||||||
|
file.sha1,
|
||||||
|
sha1,
|
||||||
|
file.posix_path
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(manifest, bytes):
|
||||||
|
# Convert the data back to dict
|
||||||
|
return json.loads(manifest.decode("utf-8"))
|
||||||
|
else:
|
||||||
|
return manifest
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(e)
|
||||||
|
raise VersionManifestSaveException(
|
||||||
|
"Error saving version manifest:\n{}".format(e)
|
||||||
|
)
|
||||||
|
|
||||||
def fetch_and_save_version_manifest(dest: Path, target_version: str=None, root_manifest=None) -> dict | None:
|
def fetch_and_save_version_manifest(dest: Path, target_version: str=None, root_manifest=None) -> dict | None:
|
||||||
"""
|
"""
|
||||||
@@ -423,39 +465,7 @@ def fetch_and_save_version_manifest(dest: Path, target_version: str=None, root_m
|
|||||||
data = root_manifest
|
data = root_manifest
|
||||||
sha1 = None
|
sha1 = None
|
||||||
|
|
||||||
file = FileObject(
|
return save_version_manifest(data, dest, sha1)
|
||||||
path=dest,
|
|
||||||
sha1=sha1,
|
|
||||||
)
|
|
||||||
|
|
||||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
try:
|
|
||||||
method = "wb" if isinstance(data, bytes) else "w"
|
|
||||||
with dest.open(method) as f:
|
|
||||||
if isinstance(data, bytes):
|
|
||||||
f.write(data)
|
|
||||||
elif isinstance(data, dict):
|
|
||||||
json.dump(data, f)
|
|
||||||
|
|
||||||
if sha1 is not None and not file.verify_sha1(sha1):
|
|
||||||
raise HashMismatchException(
|
|
||||||
"sha1",
|
|
||||||
file.sha1,
|
|
||||||
sha1,
|
|
||||||
file.posix_path
|
|
||||||
)
|
|
||||||
|
|
||||||
if isinstance(data, bytes):
|
|
||||||
# Convert the data back to dict
|
|
||||||
return json.loads(data.decode("utf-8"))
|
|
||||||
else:
|
|
||||||
return data
|
|
||||||
except Exception as e:
|
|
||||||
logger.exception(e)
|
|
||||||
raise VersionManifestSaveException(
|
|
||||||
"Error saving version manifest:\n{}".format(e)
|
|
||||||
)
|
|
||||||
|
|
||||||
def download_core_file(manifest: dict, destination: Path, is_server=False, raise_for_null_hash=False,
|
def download_core_file(manifest: dict, destination: Path, is_server=False, raise_for_null_hash=False,
|
||||||
progress_callback: Callable[[str, float], None]=None) -> FileObject:
|
progress_callback: Callable[[str, float], None]=None) -> FileObject:
|
||||||
|
|||||||
@@ -136,16 +136,15 @@ def find_java_installations(extra_install_patterns: list | None=None) -> tuple[l
|
|||||||
try:
|
try:
|
||||||
ver, raw = get_java_version(converted_path)
|
ver, raw = get_java_version(converted_path)
|
||||||
except JavaRuntimeParseException:
|
except JavaRuntimeParseException:
|
||||||
|
|
||||||
logger.debug(f"Unable to parse java version: {converted_path}")
|
logger.debug(f"Unable to parse java version: {converted_path}")
|
||||||
except JavaRuntimeException:
|
except JavaRuntimeException:
|
||||||
logger.debug(f"Unable to run java executable: {converted_path}")
|
logger.debug(f"Unable to run java executable: {converted_path}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Unhandled exception: {e} (For path: {converted_path})")
|
logger.exception(f"Unhandled exception: {e} (For path: {converted_path})")
|
||||||
finally:
|
|
||||||
if not ver:
|
if not ver:
|
||||||
errors.append(converted_path)
|
errors.append(converted_path)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.debug(f"Detected java version {ver} for path: {converted_path}")
|
logger.debug(f"Detected java version {ver} for path: {converted_path}")
|
||||||
|
|
||||||
|
|||||||
+71
-2
@@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
from collections.abc import Callable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from PySide6.QtCore import QObject
|
from PySide6.QtCore import QObject
|
||||||
@@ -8,7 +9,8 @@ from PySide6.QtCore import QObject
|
|||||||
from core_lib.common.common import FileObject
|
from core_lib.common.common import FileObject
|
||||||
from core_lib.common.exception import VersionManifestFetchException, VersionManifestSaveException, \
|
from core_lib.common.exception import VersionManifestFetchException, VersionManifestSaveException, \
|
||||||
NoSpecifiedVersionKeyException
|
NoSpecifiedVersionKeyException
|
||||||
from core_lib.game.version_info import fetch_and_save_version_manifest, get_specific_version_manifest_url_and_hash
|
from core_lib.game.version_info import fetch_and_save_version_manifest, get_specific_version_manifest_url_and_hash, \
|
||||||
|
save_version_manifest
|
||||||
|
|
||||||
|
|
||||||
class ManifestManager:
|
class ManifestManager:
|
||||||
@@ -59,6 +61,52 @@ class ManifestManager:
|
|||||||
|
|
||||||
return True if data else False, data
|
return True if data else False, data
|
||||||
|
|
||||||
|
def _cache_custom_version_manifest(self, version_id: str, fetch_manifest_handler: Callable[[str], tuple[dict, str | None]]):
|
||||||
|
data = None
|
||||||
|
manifest_sha1 = None
|
||||||
|
path = self.get_specific_version_manifest_path(version_id)
|
||||||
|
|
||||||
|
# Fetch data
|
||||||
|
try:
|
||||||
|
data, manifest_sha1 = fetch_manifest_handler(version_id)
|
||||||
|
except VersionManifestFetchException as e:
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
e.user_message,
|
||||||
|
)
|
||||||
|
except VersionManifestSaveException as e:
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
e.user_message,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.widget_mgr.signal.show_and_print_error_message.emit(
|
||||||
|
{
|
||||||
|
"error": traceback.format_exception(e),
|
||||||
|
"title": "Cache Error",
|
||||||
|
"message": "An unexpected error occurred while caching version manifest."
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Save data
|
||||||
|
if data:
|
||||||
|
try:
|
||||||
|
save_version_manifest(data, path, sha1=manifest_sha1)
|
||||||
|
except VersionManifestSaveException as e:
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
e.user_message,
|
||||||
|
)
|
||||||
|
return False, None
|
||||||
|
except Exception as e:
|
||||||
|
self.widget_mgr.signal.show_and_print_error_message.emit(
|
||||||
|
{
|
||||||
|
"error": traceback.format_exception(e),
|
||||||
|
"title": "Cache Error",
|
||||||
|
"message": "An unexpected error occurred while caching version manifest."
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
return True if data else False, data
|
||||||
|
|
||||||
def _read_cached_version_manifest(self, specified_ver: str = None) -> dict | None:
|
def _read_cached_version_manifest(self, specified_ver: str = None) -> dict | None:
|
||||||
path = self.switch_between_specific_path(specified_ver)
|
path = self.switch_between_specific_path(specified_ver)
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
@@ -80,7 +128,8 @@ class ManifestManager:
|
|||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_cached_version_manifest(self, specified_ver: str = None, max_age: int = 3600) -> dict | None:
|
def get_cached_version_manifest(self, specified_ver: str = None,
|
||||||
|
max_age: int = 3600) -> dict | None:
|
||||||
path = self.switch_between_specific_path(specified_ver)
|
path = self.switch_between_specific_path(specified_ver)
|
||||||
file = FileObject(path)
|
file = FileObject(path)
|
||||||
sha1 = None
|
sha1 = None
|
||||||
@@ -122,6 +171,26 @@ class ManifestManager:
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def get_cached_custom_version_manifest(self, specified_ver: str,
|
||||||
|
fetch_manifest_handler: Callable[[str], tuple[dict, str | None]],
|
||||||
|
max_age=1260000) -> dict | None:
|
||||||
|
path = self.get_specific_version_manifest_path(specified_ver)
|
||||||
|
file = FileObject(path)
|
||||||
|
|
||||||
|
if file.exists:
|
||||||
|
age = time.time() - path.stat().st_mtime
|
||||||
|
|
||||||
|
# Check file stat
|
||||||
|
if age < max_age:
|
||||||
|
return self._read_cached_version_manifest(specified_ver)
|
||||||
|
|
||||||
|
result, data = self._cache_custom_version_manifest(specified_ver, fetch_manifest_handler=fetch_manifest_handler)
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
def create_download_progress_callback(self, progress_title: str):
|
def create_download_progress_callback(self, progress_title: str):
|
||||||
task_id = uuid4().hex
|
task_id = uuid4().hex
|
||||||
signals = self.widget_mgr.download_signal
|
signals = self.widget_mgr.download_signal
|
||||||
|
|||||||
+528
-78
@@ -4,23 +4,16 @@ from pathlib import Path
|
|||||||
|
|
||||||
from PySide6.QtCore import QSize, Qt, QObject, Signal, Slot
|
from PySide6.QtCore import QSize, Qt, QObject, Signal, Slot
|
||||||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QComboBox, QListWidget, QPushButton, QLabel, QHBoxLayout, \
|
from PySide6.QtWidgets import QWidget, QVBoxLayout, QComboBox, QListWidget, QPushButton, QLabel, QHBoxLayout, \
|
||||||
QSizePolicy
|
QSizePolicy, QListWidgetItem, QInputDialog
|
||||||
|
|
||||||
|
from core_lib.common.exception import VersionManifestFetchException, VersionManifestException
|
||||||
|
from core_lib.game.mod import fabric
|
||||||
|
from core_lib.game.mod.mod_core import find_useful_part_in_mod_version_manifest
|
||||||
from core_lib.game.version_info import get_all_versions, get_available_version_types
|
from core_lib.game.version_info import get_all_versions, get_available_version_types
|
||||||
from manager.game import ManifestManager, GameManager
|
from manager.game import ManifestManager, GameManager
|
||||||
|
from manager.profile import CreateProfileRequest
|
||||||
from manager.widgets import SignalTransferItem, WidgetManager
|
from manager.widgets import SignalTransferItem, WidgetManager
|
||||||
|
|
||||||
LOADER_LIST = {
|
|
||||||
"fabric": {
|
|
||||||
"type": "modded",
|
|
||||||
"loadVersionMethod": None,
|
|
||||||
},
|
|
||||||
"Don't Install": {
|
|
||||||
"type": "vanilla",
|
|
||||||
"loadVersionMethod": "official",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CreateProfilePage(QWidget):
|
class CreateProfilePage(QWidget):
|
||||||
def __init__(self, app, parent=None):
|
def __init__(self, app, parent=None):
|
||||||
super(CreateProfilePage, self).__init__(parent)
|
super(CreateProfilePage, self).__init__(parent)
|
||||||
@@ -73,8 +66,8 @@ class CreateProfilePage(QWidget):
|
|||||||
self.version_list.setObjectName("ListWidget")
|
self.version_list.setObjectName("ListWidget")
|
||||||
|
|
||||||
# Button
|
# Button
|
||||||
self.create_profile = QPushButton("Create Profile")
|
self.create_profile_button = QPushButton("Create Profile")
|
||||||
self.create_profile.setObjectName("Button")
|
self.create_profile_button.setObjectName("Button")
|
||||||
|
|
||||||
self.refresh_button = QPushButton("Refresh")
|
self.refresh_button = QPushButton("Refresh")
|
||||||
self.refresh_button.setObjectName("Button")
|
self.refresh_button.setObjectName("Button")
|
||||||
@@ -85,7 +78,7 @@ class CreateProfilePage(QWidget):
|
|||||||
self.operate_layout.addWidget(self.version_list_type_label)
|
self.operate_layout.addWidget(self.version_list_type_label)
|
||||||
self.operate_layout.addStretch()
|
self.operate_layout.addStretch()
|
||||||
self.operate_layout.addWidget(self.refresh_button)
|
self.operate_layout.addWidget(self.refresh_button)
|
||||||
self.operate_layout.addWidget(self.create_profile)
|
self.operate_layout.addWidget(self.create_profile_button)
|
||||||
|
|
||||||
self.mod_config_fields_layout.addWidget(
|
self.mod_config_fields_layout.addWidget(
|
||||||
self.mod_loader_label
|
self.mod_loader_label
|
||||||
@@ -143,15 +136,38 @@ class CreateProfilePage(QWidget):
|
|||||||
# Signals
|
# Signals
|
||||||
self.signals = self.PageSignal()
|
self.signals = self.PageSignal()
|
||||||
|
|
||||||
self.signals.get_selected_type.connect(self.get_selected_type)
|
self.signals.get_selected_item.connect(self.get_selected_item)
|
||||||
self.signals.get_selected_version.connect(self.get_selected_version)
|
self.signals.update_list.connect(self.update_list)
|
||||||
self.signals.update_type_dropdown.connect(self.update_type_dropdown)
|
self.signals.clean_list.connect(self.clean_list)
|
||||||
self.signals.update_version_dropdown.connect(self.update_version_dropdown)
|
|
||||||
|
|
||||||
# events
|
# Button event
|
||||||
self.type_list_loading = threading.Event()
|
self.create_profile_button.clicked.connect(self.create_profile)
|
||||||
self.version_list_loading = threading.Event()
|
|
||||||
|
|
||||||
|
# Update version list while current type is changed
|
||||||
|
self.version_type_dropdown.currentTextChanged.connect(
|
||||||
|
self.start_load_version_list
|
||||||
|
)
|
||||||
|
|
||||||
|
self.version_list.currentTextChanged.connect(
|
||||||
|
self.start_load_mod_loader_version
|
||||||
|
)
|
||||||
|
|
||||||
|
self.mod_loader_dropdown.currentTextChanged.connect(
|
||||||
|
self.start_load_mod_loader_version
|
||||||
|
)
|
||||||
|
|
||||||
|
self.loader_list = {
|
||||||
|
"Don't Install": {
|
||||||
|
"type": "vanilla",
|
||||||
|
"loadVersionMethod": self.load_vanilla_version_list,
|
||||||
|
"profileCreateHandler": self.handle_vanilla_profile_create,
|
||||||
|
},
|
||||||
|
"Fabric": {
|
||||||
|
"type": "modded",
|
||||||
|
"loadVersionMethod": getattr(self, "load_fabric_version_list", None),
|
||||||
|
"profileCreateHandler": self.handle_fabric_profile_create,
|
||||||
|
}
|
||||||
|
}
|
||||||
self.init_all()
|
self.init_all()
|
||||||
|
|
||||||
class PageSignal(QObject):
|
class PageSignal(QObject):
|
||||||
@@ -159,19 +175,75 @@ class CreateProfilePage(QWidget):
|
|||||||
load_version_list = Signal()
|
load_version_list = Signal()
|
||||||
|
|
||||||
# widget
|
# widget
|
||||||
|
update_list = Signal(object)
|
||||||
clean_list = Signal(object)
|
clean_list = Signal(object)
|
||||||
update_version_dropdown = Signal(list)
|
|
||||||
update_type_dropdown = Signal(list)
|
|
||||||
update_mod_loader_icon_label = Signal(object)
|
update_mod_loader_icon_label = Signal(object)
|
||||||
update_mod_loader_dropdown = Signal(list)
|
|
||||||
update_mod_loader_version_list = Signal(list)
|
|
||||||
|
|
||||||
get_selected_version = Signal(object)
|
get_selected_item = Signal(object)
|
||||||
get_selected_type = Signal(object)
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Signal Handler
|
# Signal Handler
|
||||||
#
|
#
|
||||||
|
class UpdateListRequest(object):
|
||||||
|
def __init__(self, target_list: QListWidget | QComboBox, items: list[str], clean: bool=True,
|
||||||
|
current_index: int | None = None, custom_handler: Callable = None,
|
||||||
|
block_signals: bool = False, complete_callback: Callable = None):
|
||||||
|
self.target_list = target_list
|
||||||
|
self.items: list[str | dict] = items
|
||||||
|
self.clean = clean
|
||||||
|
self.current_index = current_index
|
||||||
|
self.custom_handler = custom_handler
|
||||||
|
self.block_signals = block_signals
|
||||||
|
self.complete_callback = complete_callback
|
||||||
|
|
||||||
|
@Slot(object)
|
||||||
|
def update_list(self, request: UpdateListRequest):
|
||||||
|
target_list = request.target_list
|
||||||
|
items = request.items
|
||||||
|
|
||||||
|
if not isinstance(target_list, QListWidget | QComboBox):
|
||||||
|
raise TypeError(
|
||||||
|
"Target list must be a QListWidget or QComboBox."
|
||||||
|
)
|
||||||
|
|
||||||
|
if request.block_signals:
|
||||||
|
target_list.blockSignals(True)
|
||||||
|
|
||||||
|
# Handle update
|
||||||
|
try:
|
||||||
|
if request.clean:
|
||||||
|
target_list.clear()
|
||||||
|
|
||||||
|
if callable(request.custom_handler):
|
||||||
|
handler = request.custom_handler
|
||||||
|
handler(request)
|
||||||
|
else:
|
||||||
|
target_list.addItems(items)
|
||||||
|
|
||||||
|
index = request.current_index
|
||||||
|
if index is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check and set current index
|
||||||
|
if 0 <= request.current_index < target_list.count():
|
||||||
|
if isinstance(target_list, QComboBox):
|
||||||
|
target_list.setCurrentIndex(index)
|
||||||
|
elif isinstance(target_list, QListWidget):
|
||||||
|
target_list.setCurrentRow(index)
|
||||||
|
else:
|
||||||
|
self.app.logger.warning(
|
||||||
|
"Target list index out of range. Current index is {}.".format(
|
||||||
|
request.current_index
|
||||||
|
)
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
if request.block_signals:
|
||||||
|
target_list.blockSignals(False)
|
||||||
|
|
||||||
|
if callable(request.complete_callback):
|
||||||
|
request.complete_callback()
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def clean_list(target_list: QListWidget | QComboBox):
|
def clean_list(target_list: QListWidget | QComboBox):
|
||||||
if not isinstance(target_list, QListWidget | QComboBox):
|
if not isinstance(target_list, QListWidget | QComboBox):
|
||||||
@@ -179,33 +251,44 @@ class CreateProfilePage(QWidget):
|
|||||||
|
|
||||||
target_list.clear()
|
target_list.clear()
|
||||||
|
|
||||||
@Slot(dict)
|
class GetSelectedItem(SignalTransferItem):
|
||||||
def get_selected_type(self, item: SignalTransferItem):
|
def __init__(self, target_list: QListWidget | QComboBox):
|
||||||
if not isinstance(item, SignalTransferItem):
|
SignalTransferItem.__init__(self)
|
||||||
raise TypeError("Item is not a SignalTransferItem")
|
self.target_list = target_list
|
||||||
|
|
||||||
selected_type = self.version_type_dropdown.currentText()
|
class DisplayListItem(QListWidgetItem):
|
||||||
|
def __init__(self, parent: QListWidget=None, display_text: str=None, raw_value: str=None):
|
||||||
|
QListWidgetItem.__init__(self, parent)
|
||||||
|
|
||||||
if not selected_type:
|
self.display_text = display_text
|
||||||
item.result_queue.put(None)
|
self.raw_value = raw_value
|
||||||
else:
|
|
||||||
item.result_queue.put(selected_type)
|
|
||||||
|
|
||||||
item.wait_event.set()
|
self.setText(self.display_text)
|
||||||
|
|
||||||
@Slot(object)
|
@Slot(object)
|
||||||
def get_selected_version(self, item: SignalTransferItem):
|
def get_selected_item(self, request: GetSelectedItem):
|
||||||
if not isinstance(item, SignalTransferItem):
|
if not isinstance(request, self.GetSelectedItem):
|
||||||
raise TypeError("Item is not a SignalTransferItem")
|
raise TypeError("Item is not a GetSelectedItem")
|
||||||
|
|
||||||
selected_item = self.version_list.currentItem()
|
target = request.target_list
|
||||||
|
|
||||||
if selected_item is None:
|
if isinstance(target, QComboBox):
|
||||||
item.result_queue.put(None)
|
selected_text = target.currentText()
|
||||||
|
elif isinstance(target, QListWidget):
|
||||||
|
item = target.currentItem()
|
||||||
|
if isinstance(item, self.DisplayListItem):
|
||||||
|
selected_text = item.raw_value
|
||||||
|
else:
|
||||||
|
selected_text = None
|
||||||
else:
|
else:
|
||||||
item.result_queue.put(selected_item.text())
|
raise TypeError("Target list is not a QListWidget or QComboBox")
|
||||||
|
|
||||||
item.wait_event.set()
|
if not selected_text:
|
||||||
|
request.result_queue.put(None)
|
||||||
|
else:
|
||||||
|
request.result_queue.put(selected_text)
|
||||||
|
|
||||||
|
request.wait_event.set()
|
||||||
|
|
||||||
def update_type_dropdown(self, types: list):
|
def update_type_dropdown(self, types: list):
|
||||||
# Move release to top
|
# Move release to top
|
||||||
@@ -218,63 +301,328 @@ class CreateProfilePage(QWidget):
|
|||||||
if len(self.version_type_dropdown.currentText()) == 0:
|
if len(self.version_type_dropdown.currentText()) == 0:
|
||||||
self.version_type_dropdown.setCurrentIndex(0)
|
self.version_type_dropdown.setCurrentIndex(0)
|
||||||
|
|
||||||
def update_version_dropdown(self, versions: list):
|
|
||||||
ids = []
|
|
||||||
|
|
||||||
for version in versions:
|
def update_version_list(self, request: UpdateListRequest):
|
||||||
ver_id = version.get("id", None)
|
version_list = request.target_list
|
||||||
if ver_id is not None:
|
|
||||||
ids.append(ver_id)
|
|
||||||
|
|
||||||
self.version_list.addItems(ids)
|
if not isinstance(version_list, QListWidget | QComboBox):
|
||||||
|
raise TypeError("Target list is not a QListWidget or QComboBox")
|
||||||
|
|
||||||
selected_item = self.version_list.currentItem()
|
for version in request.items:
|
||||||
|
ver_id = version.get("id", None) or version.get("version", None)
|
||||||
|
stable = version.get("stable", None)
|
||||||
|
|
||||||
if selected_item is None and len(ids) > 0:
|
if not ver_id:
|
||||||
self.version_list.setCurrentRow(0)
|
self.app.logger.warning("Provided version {} missing ID.".format(version))
|
||||||
|
|
||||||
|
display_text = ver_id
|
||||||
|
|
||||||
|
if stable is not None and isinstance(stable, bool):
|
||||||
|
display_text = f"{ver_id}"+(" (stable)" if stable else "")
|
||||||
|
|
||||||
|
item = self.DisplayListItem(
|
||||||
|
parent=request.target_list,
|
||||||
|
display_text=display_text,
|
||||||
|
raw_value=ver_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
version_list.addItem(item)
|
||||||
|
|
||||||
|
def update_mod_loader_list(self, loaders: dict):
|
||||||
|
for loader_name, value in loaders.items():
|
||||||
|
self.mod_loader_version_list.addItem(loader_name)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Thread Starter
|
# Thread Starter
|
||||||
#
|
#
|
||||||
def init_all(self):
|
def init_all(self):
|
||||||
self.start_load_type_list(callback=self.start_load_version_list)
|
self.start_load_type_list()
|
||||||
|
self.start_load_mod_loader_list()
|
||||||
|
|
||||||
def start_load_type_list(self, callback=None):
|
def start_load_type_list(self):
|
||||||
thread = threading.Thread(target=self.load_type_list, args=(callback,))
|
thread = threading.Thread(target=self.load_type_list)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
def start_load_version_list(self):
|
def start_load_version_list(self):
|
||||||
thread = threading.Thread(target=self.load_version_list)
|
thread = threading.Thread(target=self.load_version_list)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
def start_load_mod_loader_list_and_version(self):
|
def start_load_mod_loader_list(self):
|
||||||
pass
|
thread = threading.Thread(target=self.load_mod_loader_list)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
def start_load_mod_loader_version(self):
|
||||||
|
thread = threading.Thread(target=self.load_mod_loader_version)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Processor
|
||||||
|
#
|
||||||
|
def create_profile(self):
|
||||||
|
request = self.GetSelectedItem(
|
||||||
|
target_list=self.version_list,
|
||||||
|
)
|
||||||
|
self.signals.get_selected_item.emit(request)
|
||||||
|
|
||||||
|
if not request.wait(10):
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
"Timeout while waiting for selected version.",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
minecraft_version = request.result()
|
||||||
|
|
||||||
|
if not minecraft_version:
|
||||||
|
self.widget_mgr.signal.show_warning_message.emit(
|
||||||
|
"Selected a version before creating profile.",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
loader_request = self.GetSelectedItem(
|
||||||
|
target_list=self.mod_loader_dropdown,
|
||||||
|
)
|
||||||
|
self.signals.get_selected_item.emit(loader_request)
|
||||||
|
|
||||||
|
if not loader_request.wait(10):
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
"Timeout while waiting for selected mod loader.",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
loader_name = loader_request.result()
|
||||||
|
|
||||||
|
if loader_name not in self.loader_list:
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
"Loader {} not found.".format(loader_name),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
loader = self.loader_list[loader_name]
|
||||||
|
profile_create_handler: Callable[[str, str, str | None], None] = loader.get("profileCreateHandler", None)
|
||||||
|
|
||||||
|
if profile_create_handler is None:
|
||||||
|
self.widget_mgr.signal.show_warning_message.emit(
|
||||||
|
"Profile creation for loader {} is not implemented.".format(loader_name),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
loader_version_request = self.GetSelectedItem(
|
||||||
|
target_list=self.mod_loader_version_list,
|
||||||
|
)
|
||||||
|
self.signals.get_selected_item.emit(loader_version_request)
|
||||||
|
|
||||||
|
if not loader_version_request.wait(10):
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
"Timeout while waiting for selected mod loader version.",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
loader_version = loader_version_request.result()
|
||||||
|
|
||||||
|
if not loader_version and loader_version is not None:
|
||||||
|
loader_version = None
|
||||||
|
|
||||||
|
profile_name, accepted = QInputDialog.getText(
|
||||||
|
self,
|
||||||
|
"Create Profile",
|
||||||
|
"Profile name:",
|
||||||
|
text=f"Minecraft {minecraft_version}",
|
||||||
|
)
|
||||||
|
|
||||||
|
if not accepted:
|
||||||
|
return
|
||||||
|
|
||||||
|
profile_name = profile_name.strip()
|
||||||
|
if not profile_name:
|
||||||
|
self.widget_mgr.signal.show_warning_message.emit(
|
||||||
|
"Profile name cannot be empty.",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Call handler
|
||||||
|
if callable(profile_create_handler):
|
||||||
|
profile_create_handler(profile_name, minecraft_version, loader_version)
|
||||||
|
|
||||||
|
def handle_vanilla_profile_create(self, profile_name: str, version: str, loader_version: None=None, switch_to_current=False):
|
||||||
|
"""
|
||||||
|
Handle vanilla profile creation.
|
||||||
|
:param profile_name:
|
||||||
|
:param version:
|
||||||
|
:param loader_version:
|
||||||
|
:param switch_to_current: Switch new profile to current profile.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
manifest = self.manifest_mgr.get_cached_version_manifest(version)
|
||||||
|
|
||||||
|
if not manifest:
|
||||||
|
self.widget_mgr.signal.show_warning_message.emit(
|
||||||
|
"Unable to create profile due to manifest fetch failure.",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
version_id: str = manifest.get("id")
|
||||||
|
version_type = manifest.get(
|
||||||
|
"type",
|
||||||
|
self.version_type_dropdown.currentText() or "custom",
|
||||||
|
)
|
||||||
|
|
||||||
|
if version_id is None:
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
"Unable to create profile due to version ID is missing in manifest.",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
request = CreateProfileRequest(
|
||||||
|
name=profile_name,
|
||||||
|
version_id=version_id,
|
||||||
|
version_type=version_type,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Switch to current profile and save
|
||||||
|
profile_id = self.app.profile_manager.create_profile(request)
|
||||||
|
if profile_id is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if switch_to_current:
|
||||||
|
if not self.app.profile_manager.set_current_profile(profile_id):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.app.profile_manager.save():
|
||||||
|
return
|
||||||
|
|
||||||
|
self.widget_mgr.signal.show_info_message.emit(
|
||||||
|
f"Profile {profile_id} successfully created.",
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle_fabric_profile_create(self, profile_name: str, minecraft_version: str, loader_version: str | None, switch_to_current=False):
|
||||||
|
"""
|
||||||
|
Handle fabric profile creation.
|
||||||
|
:param profile_name:
|
||||||
|
:param minecraft_version:
|
||||||
|
:param loader_version:
|
||||||
|
:param switch_to_current:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if not loader_version:
|
||||||
|
self.widget_mgr.signal.show_warning_message.emit(
|
||||||
|
"Selected a fabric loader version before creating profile.",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create vanilla manifest
|
||||||
|
vanilla_manifest = self.manifest_mgr.get_cached_version_manifest(
|
||||||
|
minecraft_version
|
||||||
|
)
|
||||||
|
if not vanilla_manifest:
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
"Unable to create profile due to vanilla manifest fetch failure.",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
version_id = fabric.get_full_version_string(version=minecraft_version, loader_version=loader_version)
|
||||||
|
|
||||||
|
def fetch_loader_manifest(_version_id: str, ) -> tuple[dict, None]:
|
||||||
|
manifest = fabric.get_loader_manifest_from_loader_data(
|
||||||
|
minecraft_version,
|
||||||
|
{"version": loader_version},
|
||||||
|
)
|
||||||
|
return manifest, None
|
||||||
|
|
||||||
|
loader_manifest = self.manifest_mgr.get_cached_custom_version_manifest(
|
||||||
|
version_id, fetch_manifest_handler=fetch_loader_manifest,)
|
||||||
|
|
||||||
|
if not loader_manifest:
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
"Unable to create profile due to mod loader manifest fetch failure.",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
loader_info = find_useful_part_in_mod_version_manifest(
|
||||||
|
loader_manifest
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check mod info
|
||||||
|
if loader_info["id"] != version_id:
|
||||||
|
self.app.logger.error(f"Fabric manifest ID mismatch: expected {version_id}, got {loader_info["id"]}.")
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
"Unable to create profile. Exising loader version ID mismatch.",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if loader_info["inheritsFrom"] != minecraft_version:
|
||||||
|
self.app.logger.error(
|
||||||
|
f"Fabric manifest inherits from "
|
||||||
|
f'{loader_info["inheritsFrom"]}, not {minecraft_version}.'
|
||||||
|
)
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
"Unable to create profile. Inherits version is mismatched as expected.",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
request = CreateProfileRequest(
|
||||||
|
name=profile_name,
|
||||||
|
version_id=version_id,
|
||||||
|
version_type=loader_info["type"] or vanilla_manifest["type"] or "custom",
|
||||||
|
)
|
||||||
|
|
||||||
|
profile_id = self.app.profile_manager.create_profile(
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
if profile_id is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if switch_to_current:
|
||||||
|
if not self.app.profile_manager.set_current_profile(profile_id):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.app.profile_manager.save():
|
||||||
|
return
|
||||||
|
|
||||||
|
self.widget_mgr.signal.show_info_message.emit(
|
||||||
|
f"Profile {profile_id} with fabric loader successfully created.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_forge_profile_create(self, profile_name: str, minecraft_version: str, loader_version: str | None):
|
||||||
|
if not loader_version:
|
||||||
|
self.widget_mgr.signal.show_warning_message.emit(
|
||||||
|
"Selected a forge loader version before creating profile.",
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle_neoforge_profile_create(self, profile_name: str, minecraft_version: str, loader_version: str | None):
|
||||||
|
if not loader_version:
|
||||||
|
self.widget_mgr.signal.show_warning_message.emit(
|
||||||
|
"Selected a neoforge loader version before creating profile.",
|
||||||
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Background Thread
|
# Background Thread
|
||||||
#
|
#
|
||||||
def load_type_list(self, callback: Callable[None]=None):
|
def load_type_list(self):
|
||||||
# Cleanup
|
|
||||||
self.signals.clean_list.emit(self.version_type_dropdown)
|
|
||||||
|
|
||||||
manifest = self.manifest_mgr.get_cached_version_manifest()
|
manifest = self.manifest_mgr.get_cached_version_manifest()
|
||||||
if not manifest:
|
if not manifest:
|
||||||
return
|
return
|
||||||
|
|
||||||
ver_types = get_available_version_types(manifest)
|
ver_types = get_available_version_types(manifest)
|
||||||
|
release_index = ver_types.index("release") if "release" in ver_types else None
|
||||||
|
|
||||||
self.signals.update_type_dropdown.emit(ver_types)
|
request = self.UpdateListRequest(
|
||||||
|
target_list=self.version_type_dropdown,
|
||||||
if callable(callback):
|
items=ver_types,
|
||||||
callback()
|
current_index=release_index,
|
||||||
|
clean=True,
|
||||||
def load_version_list(self, callback: Callable[Callable[None] | None]=None):
|
block_signals=True,
|
||||||
# Cleanup
|
complete_callback=self.start_load_version_list,
|
||||||
self.version_list.clear()
|
)
|
||||||
|
self.signals.update_list.emit(request)
|
||||||
|
|
||||||
|
def load_version_list(self):
|
||||||
# Get selected version type
|
# Get selected version type
|
||||||
request = SignalTransferItem()
|
request = self.GetSelectedItem(
|
||||||
self.signals.get_selected_type.emit(request)
|
target_list=self.version_type_dropdown
|
||||||
|
)
|
||||||
|
self.signals.get_selected_item.emit(request)
|
||||||
|
|
||||||
if not request.wait(10):
|
if not request.wait(10):
|
||||||
self.widget_mgr.signal.show_error_message.emit(
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
@@ -292,13 +640,115 @@ class CreateProfilePage(QWidget):
|
|||||||
|
|
||||||
# Fetch (or read) manifest
|
# Fetch (or read) manifest
|
||||||
manifest = self.manifest_mgr.get_cached_version_manifest()
|
manifest = self.manifest_mgr.get_cached_version_manifest()
|
||||||
|
|
||||||
|
if not manifest:
|
||||||
|
return
|
||||||
|
|
||||||
versions = get_all_versions(
|
versions = get_all_versions(
|
||||||
manifest,
|
manifest,
|
||||||
specified_type=selected_type
|
specified_type=selected_type
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update list
|
# Update list
|
||||||
self.update_version_dropdown(versions)
|
request = self.UpdateListRequest(
|
||||||
|
target_list=self.version_list,
|
||||||
|
items=versions,
|
||||||
|
clean=True,
|
||||||
|
current_index=0,
|
||||||
|
custom_handler=self.update_version_list
|
||||||
|
)
|
||||||
|
self.signals.update_list.emit(request)
|
||||||
|
|
||||||
if callable(callback):
|
def load_vanilla_version_list(self):
|
||||||
callback(None)
|
"""
|
||||||
|
A dummy port for load_mod_loader_version
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.clean_list(self.mod_loader_version_list)
|
||||||
|
|
||||||
|
def load_fabric_version_list(self):
|
||||||
|
self.signals.clean_list.emit(self.mod_loader_version_list)
|
||||||
|
request = self.GetSelectedItem(
|
||||||
|
target_list=self.version_list
|
||||||
|
)
|
||||||
|
self.signals.get_selected_item.emit(request)
|
||||||
|
|
||||||
|
if not request.wait(10):
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
"Timeout while waiting for selected version.",
|
||||||
|
)
|
||||||
|
|
||||||
|
selected_version = request.result()
|
||||||
|
|
||||||
|
if not selected_version:
|
||||||
|
self.widget_mgr.signal.show_warning_message.emit(
|
||||||
|
"No version selected.",
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
loaders = fabric.get_version_support_loader_list(
|
||||||
|
selected_version
|
||||||
|
)
|
||||||
|
except (VersionManifestFetchException, VersionManifestException) as e:
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
"Failed to fetch version manifest: {}".format(e.user_message),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
"An unexpected error occurred while fetching version manifest: {}".format(e),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
request = self.UpdateListRequest(
|
||||||
|
target_list=self.mod_loader_version_list,
|
||||||
|
items=loaders,
|
||||||
|
current_index=0,
|
||||||
|
custom_handler=self.update_version_list,
|
||||||
|
block_signals=True,
|
||||||
|
)
|
||||||
|
self.signals.update_list.emit(request)
|
||||||
|
|
||||||
|
def load_mod_loader_list(self):
|
||||||
|
loaders = [loader for loader in self.loader_list.keys()]
|
||||||
|
|
||||||
|
request = self.UpdateListRequest(
|
||||||
|
target_list=self.mod_loader_dropdown,
|
||||||
|
items=loaders,
|
||||||
|
current_index=0,
|
||||||
|
clean=True
|
||||||
|
)
|
||||||
|
self.signals.update_list.emit(request)
|
||||||
|
|
||||||
|
def load_mod_loader_version(self):
|
||||||
|
request = self.GetSelectedItem(
|
||||||
|
target_list=self.mod_loader_dropdown
|
||||||
|
)
|
||||||
|
self.signals.get_selected_item.emit(request)
|
||||||
|
|
||||||
|
if not request.wait(10):
|
||||||
|
self.widget_mgr.signal.show_error_message.emit(
|
||||||
|
"Timeout while waiting for selected loader.",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
loader_name = request.result()
|
||||||
|
|
||||||
|
if loader_name not in self.loader_list:
|
||||||
|
raise KeyError("Target loader {} not found".format(loader_name))
|
||||||
|
|
||||||
|
loader = self.loader_list[loader_name]
|
||||||
|
version_handler = loader.get("loadVersionMethod", None)
|
||||||
|
|
||||||
|
if not version_handler:
|
||||||
|
self.widget_mgr.signal.show_warning_message.emit(
|
||||||
|
"Selected loader \"{}\" is not implemented".format(loader_name),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if callable(version_handler):
|
||||||
|
version_handler()
|
||||||
|
else:
|
||||||
|
self.app.logger.warning(
|
||||||
|
"Select mod loader \"loadVersionMethod\" is not callable."
|
||||||
|
)
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
# Copyright announce for *Launcher* icon resources
|
# Copyright announce for *Launcher* icon resources
|
||||||
|
|
||||||
## 1. You can do anything you want to these icons. (Except some icons that said maybe have other peoples copyright announce)
|
## 1. You can do anything you want to these icons. (However, you may need other people's accept to use below that maybe have other peoples copyright announce)
|
||||||
|
|
||||||
## 2. Below icons are from other source that may contain copyright:
|
## 2. Below icons are from other source that may contain copyright:
|
||||||
- profile_default.png : [Source](https://www.pixilart.com/art/minecraft-crafting-table-8aa9e1b9aee1a62)
|
- profile_default.png : [Source](https://www.pixilart.com/art/minecraft-crafting-table-8aa9e1b9aee1a62)
|
||||||
|
- modLoader/fabric.png : [Source](https://fabricmc.net/assets/logo.png)
|
||||||
|
- modLoader/neoforged.png : [Source](https://raw.githubusercontent.com/neoforged/.github/refs/heads/main/art/neoforged.ico)
|
||||||
|
|
||||||
## For the icon owner: If you want to take down icon that infringing upon your copyright. Contact us with this [link](https://repo.weispace.net/wei/Launcher)
|
## For the icon owner: If you want to take down icon that infringing upon your copyright. Contact us with this [link](https://repo.weispace.net/wei/Launcher)
|
||||||
Reference in New Issue
Block a user