Update many files.

Add some description about this project, and now it has a simple UI (made with Qt)

If you don't specify the mode of (interface?) the UI. Launcher will show a crude window that lets you
 select a mode to start the (real) launcher.

Test version data fetching functions. Looks great.
This commit is contained in:
2026-07-10 23:27:21 +08:00
parent c1f7967ff0
commit a93641b26d
9 changed files with 274 additions and 19 deletions
+1
View File
@@ -0,0 +1 @@
__version__ = '0.1.0'
+3
View File
@@ -0,0 +1,3 @@
import logging
logger = logging.getLogger("Launcher.CoreLib")
+23
View File
@@ -0,0 +1,23 @@
import logging
import sys
from colorlog import ColoredFormatter
DEFAULT_FORMAT = '%(asctime)s:%(name)s:%(levelname)s: %(message)s'
DEFAULT_DATE_FORMAT = '%H:%M:%S'
def get_colorful_handler():
handler = logging.StreamHandler(sys.stdout)
formatter = ColoredFormatter(
'%(light_white)s%(asctime)s:%(light_green)s%(name)s:%(log_color)s%(levelname)s%(reset)s: %(message)s',
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'light_red',
'CRITICAL': 'red',
},
datefmt=DEFAULT_DATE_FORMAT
)
handler.setFormatter(formatter)
return handler
+10
View File
@@ -0,0 +1,10 @@
from PySide6.QtGui import QScreen
from PySide6.QtWidgets import QMainWindow, QApplication
def move_window_to_center(window: QMainWindow):
main_screen = QApplication.primaryScreen()
center = QScreen.availableGeometry(main_screen).center()
geometry = window.frameGeometry()
geometry.moveCenter(center)
window.move(geometry.topLeft())
+119 -8
View File
@@ -1,10 +1,121 @@
from PySide6.QtGui import QScreen
from PySide6.QtWidgets import QMainWindow, QApplication
import json
import requests
from .core_init import logger
MOJANG_VERSION_MANIFEST_V2 = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json"
def get_version_manifest(manifest_url=MOJANG_VERSION_MANIFEST_V2, timeout=10) -> dict | None:
logger.debug("Getting version manifest from {}".format(manifest_url))
try:
r = requests.get(manifest_url, timeout=timeout)
except requests.exceptions.RequestException as e:
logger.error("Error getting version manifest from {}".format(manifest_url))
logger.error("Exception: {}".format(e))
return None
try:
data = r.json()
logger.debug("VersionManifest: {}".format(json.dumps(data, indent=4)))
return data
except ValueError as e:
logger.error("Unable to deserialize response data into dictionary.")
logger.error("URL: {}".format(manifest_url))
logger.error("Exception: {}".format(e))
return None
def get_latest_version(version_manifest: dict, is_snapshot=False) -> None | str:
latest = version_manifest.get("latest", {})
if len(latest) == 0:
logger.debug("No latest version section available in version manifest.")
logger.debug("Data: {}".format(json.dumps(version_manifest, indent=4)))
return None
latest_ver = latest.get("release" if not is_snapshot else "snapshot", None)
if latest_ver is None:
logger.debug("No latest version available.")
else:
logger.debug("Latest version: {}".format(latest_ver))
return latest_ver
def get_specific_version_manifest_url(version_manifest: dict,
version_id: str, is_snapshot=False) -> str | None:
versions = version_manifest.get("versions", [])
if len(versions) == 0:
logger.debug("No version section available in version manifest.")
return None
ver_type = "release" if not is_snapshot else "snapshot"
versions = [version for version in versions if version.get("type") == ver_type]
for version in versions:
if version.get("id") == version_id:
specific_ver_manifest_url = version.get("url", None)
if specific_ver_manifest_url is None:
logger.debug("Specific version manifest URL not available in version manifest.")
return None
return specific_ver_manifest_url
logger.debug("No version section available in version manifest.")
return None
def fetch_specific_version_manifest(version_manifest: dict, spec_version: str) -> dict | None:
ver_url = get_specific_version_manifest_url(version_manifest, spec_version)
if ver_url is None:
logger.debug("Unable to get specific version manifest url from version manifest.\n"
"Version: {}".format(spec_version))
return None
try:
r = requests.get(ver_url, timeout=10)
except requests.exceptions.RequestException as e:
logger.error("Error getting version manifest from {}".format(ver_url))
logger.error("Exception: {}".format(e))
return None
try:
data = r.json()
logger.debug("SpecificVersionManifest: {}".format(json.dumps(data, indent=4)))
return data
except ValueError as e:
logger.error("Unable to deserialize response data into dictionary.")
def move_window_to_center(window: QMainWindow):
main_screen = QApplication.primaryScreen()
center = QScreen.availableGeometry(main_screen).center()
geometry = window.frameGeometry()
geometry.moveCenter(center)
window.move(geometry.topLeft())
def find_useful_part_in_specific_version_manifest(spec_version_manifest: dict)\
-> tuple[dict | None, dict | None, dict | None, dict | None, list | None, str | None]:
arguments = spec_version_manifest.get("arguments", {})
asset_index = spec_version_manifest.get("assetIndex", {})
downloads = spec_version_manifest.get("downloads", {})
java_version = spec_version_manifest.get("java_version", {})
libraries = spec_version_manifest.get("libraries", [])
main_class = spec_version_manifest.get("mainClass", None)
if len(arguments) == 0:
logger.debug("No arguments section available in version manifest.")
if len(asset_index) == 0:
logger.debug("No asset index section available in version manifest.")
if len(downloads) == 0:
logger.debug("No downloads section available in version manifest.")
if len(java_version) == 0:
logger.debug("No java version section available in version manifest.")
if len(libraries) == 0:
logger.debug("No libraries section available in version manifest.")
if main_class is None:
logger.debug("No main class section available in version manifest.")
return arguments, asset_index, downloads, java_version, libraries, main_class