add a wip main.py launcher, update the cli , and the whole system

This commit is contained in:
xgui4
2026-03-17 10:04:16 -04:00
parent 037cd1dc94
commit b96e53d10e
13 changed files with 10322 additions and 46 deletions

1
.gitignore vendored
View File

@@ -294,3 +294,4 @@ MinecraftLCEClient/
src/LCE-Qt-Windows-Launcher.zip
output/
.vscode/settings.json

14
.vscode/settings.json vendored
View File

@@ -1,3 +1,15 @@
{
"python-envs.defaultEnvManager": "ms-python.python:venv"
"python-envs.defaultEnvManager": "ms-python.python:venv",
"python-envs.pythonProjects": [
{
"path": "src",
"envManager": "ms-python.python:venv",
"packageManager": "ms-python.python:pip"
},
{
"path": "src/cli.py",
"envManager": "ms-python.python:venv",
"packageManager": "ms-python.python:pip"
}
]
}

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env python3
# This is just a main.py as a fallback
import src.launcher as launcher
launcher.main()

View File

@@ -3,6 +3,7 @@ name = "LCE-Qt-Launcher"
version = "0.0.0"
description = "This is a custom MCLCE Launcher written in python and Qt with Freedom and GNU/Linux support in mind."
requires-python = "==3.12.*"
dependencies = [
"pyside6",
"requests",
@@ -20,13 +21,15 @@ files = [
"src/instance_manager.py",
"src/launcher.py",
"src/system_manager.py",
"src/user_data",
"src/user_data.py",
"src/form.ui",
"src/system_info.ui",
"src/json_trans.py",
"pkg/LCE-Qt-Launcher.desktop",
"license.md",
"readme.md",
"requirements.txt",
"src/main.py",
"src/cli.py",
"res.qrc",
"CODE-DE-CONDUITE.md",
"code-of-conduct.md",

10075
rc_res.py Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 32 KiB

15
src/cli.py Normal file → Executable file
View File

@@ -1,3 +1,5 @@
#!/usr/bin/env python3
from term_image.image import from_file
from rich import print
@@ -31,9 +33,10 @@ buildInfo = BuildInfo()
defaultInstance = Instance()
instanceManager = InstanceManager(defaultInstance)
if user_output == "1":
instanceManager.play()
if user_output == "2":
instanceManager.install()
else:
print("not implemented yet")
if __name__ == "__main__":
if user_output == "1":
instanceManager.play()
if user_output == "2":
instanceManager.install_instance()
else:
print("not implemented yet")

View File

@@ -3,24 +3,26 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING:
from instance_manager import Instance
from build_info import BuildInfo
from zipfile import ZipFile, BadZipFile, LargeZipFile
from io import BytesIO
import requests
import os
import stat
import platform
SUCCESS_STATUS_CODE = 200
class Downloader:
def download_client(self, instance : Instance):
def __init__(self, build_info: BuildInfo):
self._build_info = build_info
def download_instance(self, instance : Instance):
response = requests.get(instance.get_download_url())
if response.status_code == SUCCESS_STATUS_CODE:
print(f"Download of {instance.name} from {instance.get_download_url} was a success")
try:
with ZipFile(BytesIO(response.content)) as archive:
archive.extractall(instance.installation_path)
archive : ZipFile = self.extract_instance(response, instance)
except BadZipFile as err:
print(f"Error : {err} while extracting {archive.filename}")
except LargeZipFile as err:
@@ -28,8 +30,11 @@ class Downloader:
else:
if os.name == "posix":
exe_abs_path = os.path.join(instance.installation_path, instance.exe_name)
curr_perm = os.stat(exe_abs_path)
new_perm = curr_perm.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH
os.chmod(exe_abs_path, new_perm)
system = self._build_info.system_manager
system.set_file_permission(exe_abs_path)
else:
print(f"Error : {response.status_code} during the dowbloading of the Minecraft LCE Client")
print(f"Error : {response.status_code} during the dowloading of the Minecraft LCE Client")
def extract_instance(self, response, instance) -> ZipFile:
with ZipFile(BytesIO(response.content)) as archive:
return archive.extractall(instance.installation_path)

View File

@@ -1,15 +1,19 @@
from enum import Enum
from downloader import Downloader
from build_info import BuildInfo
_GITHUB_RELEASE_STR = "/releases/download/"
from subprocess import TimeoutExpired
import subprocess
_GITHUB_RELEASE_STR = "/releases/download/"
class InstanceType(Enum):
GITHUB_RELEASE = 0
GIT_SOURCE_CODE = 1
LOCAL_NO_INSTALL = 2
REMOTE_GIT_SOURCE = 1
LOCAL_INSTALLATION = 2
LOCAL_SOURCE_CODE = 3
class Instance:
def __init__(self,
@@ -39,18 +43,32 @@ class Instance:
_GITHUB_RELEASE_STR + \
self.version + "/" + \
self.archive_file
if self.instance_type == InstanceType.GIT_SOURCE_CODE:
if self.instance_type == InstanceType.REMOTE_GIT_SOURCE:
return f"{self.repo_url}.git"
if self.instance_type == InstanceType.LOCAL_NO_INSTALL:
if self.instance_type == InstanceType.LOCAL_INSTALLATION:
return RuntimeError("Error ! Ressource Cannot be downloaded. Reason : Ressource is local")
else:
return RuntimeError("Not implemented yet!")
class InstanceManager:
def __init__(self, instance : Instance):
def __init__(self, instance : Instance, build_info : BuildInfo):
self.instance = instance
self._downloader = Downloader()
def play(self):
subprocess.run(self.instance.installation_path + self.instance.exe_name)
def install(self):
self._downloader.download_client(self.instance);
self._downloader = Downloader(build_info)
self._build_info = build_info
def play(self) -> str:
try:
game_process = subprocess.run(self.instance.installation_path, self.instance.exe_name)
except TimeoutExpired as err:
print(f"Error : process of lauching instance {self.instance.name} Failed. Reason : Timeout Expired.\n traceback : {err.with_traceback}")
return f"Error : process of lauching instance {self.instance.name} Failed. Reason : Timeout Expired.\n traceback : {err.with_traceback}"
except PermissionError as err:
print(f"Error : Cannot launch {self.instance.name}. Reason : Permission Denied.\n traceback : {err.with_traceback}")
return f"Error : Cannot launch {self.instance.name}. Reason : Permission Denied.\n traceback : {err.with_traceback}"
else:
return f"Client closed with code {game_process.returncode}"
def install_instance(self) -> str:
if self.instance in [InstanceType.GITHUB_RELEASE, InstanceType.REMOTE_GIT_SOURCE]:
self._downloader.download_instance(self.instance)
else:
return "Already Installed, skip installation."

View File

@@ -2,6 +2,7 @@
from PySide6.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QDialog, QMessageBox
from PySide6.QtGui import QPalette, QPixmap, QBrush
from PySide6.QtCore import Qt
from user_pref import UserPref
from build_info import BuildInfo
@@ -17,7 +18,7 @@ userPref = UserPref()
buildInfo = BuildInfo()
defaultInstance = Instance()
instanceManager = InstanceManager(defaultInstance)
instanceManager = InstanceManager(defaultInstance, buildInfo)
BACKGROUND_PIXMAP_IMG = ":/assets/background.png"
@@ -46,11 +47,15 @@ class launcher(QMainWindow):
instanceManager.play()
def install(self):
print("Starting the installation!")
instanceManager.install()
instanceManager.install_instance()
def show_aboutQt(self):
QMessageBox.aboutQt("About Qt")
print("Show About Qt popup.")
QMessageBox.aboutQt(self, "About Qt")
def show_about(self):
self.aboutPopupWindow = QDialog()
self.aboutPopupWindow.setWindowModality(Qt,)
self.aboutPopupWindow.setWindowTitle(f"About {buildInfo.app_name} {buildInfo.version}")
imageLabel = QLabel()
@@ -89,7 +94,7 @@ class launcher(QMainWindow):
self.ui.actionAbout.triggered.connect(self.show_about)
self.ui.actionAbout.triggered.connect(QMessageBox.aboutQt)
self.ui.actionAbout_QT.triggered.connect(self.show_aboutQt)
self.versionlabel = QLabel(f"Version {buildInfo.version}")
self.ui.statusbar.addPermanentWidget(self.versionlabel)

16
src/main.py Normal file
View File

@@ -0,0 +1,16 @@
#!/usr/bin/env python3
import launcher
import cli
import sys
argv = sys.argv
if len(argv) >= 1:
if argv[1] == "cli" :
cli.main()
else:
launcher.main()
else:
launcher.main()

128
src/system_info.ui Normal file
View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>sys_info_dialog</class>
<widget class="QDialog" name="sys_info_dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>LCE Qt Launcher - System Info </string>
</property>
<property name="windowIcon">
<iconset resource="../res.qrc">
<normaloff>:/assets/app.ico</normaloff>:/assets/app.ico</iconset>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>30</x>
<y>240</y>
<width>341</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>20</y>
<width>391</width>
<height>211</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="appVersion">
<property name="text">
<string>LCE Qt Launcher V. Unknow</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="qVersionLabel">
<property name="text">
<string>Qt Version Unknow</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="pyVersionLabel">
<property name="text">
<string>Python Version Unknow</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="osInfoLabel">
<property name="text">
<string>Operating System info :</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="pluginsInfoLabel">
<property name="text">
<string>Plugins : </string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="runnersLabel">
<property name="text">
<string>Runners: </string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources>
<include location="../res.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>sys_info_dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>sys_info_dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -1,26 +1,43 @@
from enum import StrEnum
import platform
import os
import stat
class OperatingSystemType(StrEnum):
WINDOWS = "Microsoft Windows",
MACOS = "MacOS",
LINUX = "GNU/Linux",
FREEBSD = "FreeBSD",
ANDROID = "Android"
ANDROID = "Android",
UNKNOWN = "Unknow"
class SystemManager():
def __init__(self):
self.type : OperatingSystemType = None
self.name : str = None
self.version : str = None
self.determine_os_type()
def determine_os_type() -> OperatingSystemType :
def determine_os_type(self):
if (platform.system() == "Linux") :
return OperatingSystemType.LINUX
self.type = OperatingSystemType.LINUX
if (platform.system() == "Darwin") :
return OperatingSystemType.MACOS
self.type = OperatingSystemType.MACOS
if (platform.system() == "Windows") :
return OperatingSystemType.WINDOWS
self.type = OperatingSystemType.WINDOWS
if (platform.system() == "FreeBSD"):
self.type = OperatingSystemType.FREEBSD
if (platform.system() == "Android") :
return OperatingSystemType.WINDOWS
self.type = OperatingSystemType.ANDROID
else:
self.type = OperatingSystemType.UNKNOWN
def set_file_permission(self, file_abs_path : str) -> str:
if self.type in [OperatingSystemType.LINUX, OperatingSystemType.FREEBSD, OperatingSystemType.MACOS]:
curr_perm = os.stat(file_abs_path)
new_perm = curr_perm.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH
os.chmod(file_abs_path, new_perm)
else:
pass