mirror of
				https://github.com/nixietab/picodulce.git
				synced 2025-10-30 21:15:11 +00:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			ba8072c669
			...
			32e4783218
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ![github-actions[bot]](/assets/img/avatar_default.png)  | 32e4783218 | ||
|   | 502e64df83 | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | 1cfb6ffcb6 | ||
|   | 77291ad89e | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | 3baf6e0b1d | ||
|   | 0768897706 | 
							
								
								
									
										161
									
								
								authser.py
									
									
									
									
									
								
							
							
						
						
									
										161
									
								
								authser.py
									
									
									
									
									
								
							| @ -1,43 +1,19 @@ | |||||||
| import sys | import sys | ||||||
| import subprocess |  | ||||||
| import re | import re | ||||||
|  | import colorama | ||||||
|  | import requests | ||||||
| from PyQt5.QtWidgets import (QApplication, QDialog, QLabel, QVBoxLayout,  | from PyQt5.QtWidgets import (QApplication, QDialog, QLabel, QVBoxLayout,  | ||||||
|                           QPushButton, QLineEdit, QMessageBox) |                           QPushButton, QLineEdit, QMessageBox) | ||||||
| from PyQt5.QtCore import QThread, pyqtSignal, Qt, QUrl, QObject | from PyQt5.QtCore import QThread, pyqtSignal, Qt, QUrl, QObject, QTimer | ||||||
| from PyQt5.QtGui import QDesktopServices | from PyQt5.QtGui import QDesktopServices | ||||||
|  | from picomc.logging import logger | ||||||
| 
 | 
 | ||||||
| class AuthenticationParser: | # Constants | ||||||
|     @staticmethod | URL_DEVICE_AUTH = "https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode" | ||||||
|     def clean_ansi(text): | URL_TOKEN = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token" | ||||||
|         ansi_clean = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') | CLIENT_ID = "c52aed44-3b4d-4215-99c5-824033d2bc0f" | ||||||
|         printable_clean = re.compile(r'[^\x20-\x7E\n]') | SCOPE = "XboxLive.signin offline_access" | ||||||
|         text = ansi_clean.sub('', text) | GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code" | ||||||
|         text = printable_clean.sub('', text) |  | ||||||
|         return text.strip() |  | ||||||
| 
 |  | ||||||
|     @staticmethod |  | ||||||
|     def is_auth_error(output): |  | ||||||
|         cleaned_output = AuthenticationParser.clean_ansi(output) |  | ||||||
|         return "AADSTS70016" in cleaned_output and "not yet been authorized" in cleaned_output |  | ||||||
| 
 |  | ||||||
|     @staticmethod |  | ||||||
|     def parse_auth_output(output): |  | ||||||
|         cleaned_output = AuthenticationParser.clean_ansi(output) |  | ||||||
|         if AuthenticationParser.is_auth_error(cleaned_output): |  | ||||||
|             return None |  | ||||||
| 
 |  | ||||||
|         pattern = r"https://[^\s]+" |  | ||||||
|         code_pattern = r"code\s+([A-Z0-9]+)" |  | ||||||
|          |  | ||||||
|         url_match = re.search(pattern, cleaned_output) |  | ||||||
|         code_match = re.search(code_pattern, cleaned_output, re.IGNORECASE) |  | ||||||
|          |  | ||||||
|         if url_match and code_match: |  | ||||||
|             return { |  | ||||||
|                 'url': url_match.group(0), |  | ||||||
|                 'code': code_match.group(1) |  | ||||||
|             } |  | ||||||
|         return None |  | ||||||
| 
 | 
 | ||||||
| class AuthDialog(QDialog): | class AuthDialog(QDialog): | ||||||
|     def __init__(self, url, code, parent=None, error_mode=False): |     def __init__(self, url, code, parent=None, error_mode=False): | ||||||
| @ -106,66 +82,81 @@ class AuthenticationThread(QThread): | |||||||
|     error_occurred = pyqtSignal(str) |     error_occurred = pyqtSignal(str) | ||||||
|     auth_error_detected = pyqtSignal(str) |     auth_error_detected = pyqtSignal(str) | ||||||
|     finished = pyqtSignal() |     finished = pyqtSignal() | ||||||
|  |     access_token_received = pyqtSignal(str, str) | ||||||
|      |      | ||||||
|     def __init__(self, account): |     def __init__(self, account): | ||||||
|         super().__init__() |         super().__init__() | ||||||
|         self.account = account |         self.account = account | ||||||
|         self.process = None |         self.device_code = None | ||||||
|         self.is_running = True |         self.is_running = True | ||||||
|         self.current_output = "" |  | ||||||
|         self.waiting_for_auth = False |  | ||||||
| 
 | 
 | ||||||
|     def run(self): |     def run(self): | ||||||
|         try: |         try: | ||||||
|             command = f'picomc account authenticate {self.account}' |             self.authenticate(self.account) | ||||||
|              |  | ||||||
|             self.process = subprocess.Popen( |  | ||||||
|                 command, |  | ||||||
|                 shell=True, |  | ||||||
|                 stdout=subprocess.PIPE, |  | ||||||
|                 stderr=subprocess.STDOUT, |  | ||||||
|                 stdin=subprocess.PIPE, |  | ||||||
|                 text=True, |  | ||||||
|                 bufsize=1, |  | ||||||
|                 universal_newlines=True |  | ||||||
|             ) |  | ||||||
|              |  | ||||||
|             self.current_output = "" |  | ||||||
|             while self.is_running and self.process.poll() is None: |  | ||||||
|                 line = self.process.stdout.readline() |  | ||||||
|                 if line: |  | ||||||
|                     self.current_output += line |  | ||||||
|                      |  | ||||||
|                     if not self.waiting_for_auth: |  | ||||||
|                         parsed_data = AuthenticationParser.parse_auth_output(self.current_output) |  | ||||||
|                         if parsed_data: |  | ||||||
|                             self.auth_data_received.emit(parsed_data) |  | ||||||
|                             self.waiting_for_auth = True |  | ||||||
|                             self.current_output = "" |  | ||||||
|                     elif AuthenticationParser.is_auth_error(self.current_output): |  | ||||||
|                         self.auth_error_detected.emit(self.current_output) |  | ||||||
|                         self.waiting_for_auth = False |  | ||||||
|                         self.current_output = "" |  | ||||||
|              |  | ||||||
|             self.process.wait() |  | ||||||
|             self.finished.emit() |  | ||||||
|              |  | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             self.error_occurred.emit(str(e)) |             self.error_occurred.emit(str(e)) | ||||||
|             self.finished.emit() |             self.finished.emit() | ||||||
| 
 | 
 | ||||||
|  |     def authenticate(self, account): | ||||||
|  |         try: | ||||||
|  |             data = {"client_id": CLIENT_ID, "scope": SCOPE} | ||||||
|  | 
 | ||||||
|  |             # Request device code | ||||||
|  |             resp = requests.post(URL_DEVICE_AUTH, data) | ||||||
|  |             resp.raise_for_status() | ||||||
|  | 
 | ||||||
|  |             j = resp.json() | ||||||
|  |             self.device_code = j["device_code"] | ||||||
|  |             user_code = j["user_code"] | ||||||
|  |             link = j["verification_uri"] | ||||||
|  | 
 | ||||||
|  |             # Format message with colorama | ||||||
|  |             msg = j["message"] | ||||||
|  |             msg = msg.replace( | ||||||
|  |                 user_code, colorama.Fore.RED + user_code + colorama.Fore.RESET | ||||||
|  |             ).replace(link, colorama.Style.BRIGHT + link + colorama.Style.NORMAL) | ||||||
|  | 
 | ||||||
|  |             # Emit auth data received signal | ||||||
|  |             self.auth_data_received.emit({'url': link, 'code': user_code}) | ||||||
|  | 
 | ||||||
|  |         except requests.exceptions.RequestException as e: | ||||||
|  |             logger.error(f"Request failed: {e}") | ||||||
|  |             self.error_occurred.emit(str(e)) | ||||||
|  |             self.finished.emit() | ||||||
|  |      | ||||||
|  |     def poll_for_token(self): | ||||||
|  |         try: | ||||||
|  |             data = {"code": self.device_code, "grant_type": GRANT_TYPE, "client_id": CLIENT_ID} | ||||||
|  |             resp = requests.post(URL_TOKEN, data) | ||||||
|  |             if resp.status_code == 400: | ||||||
|  |                 j = resp.json() | ||||||
|  |                 logger.debug(j) | ||||||
|  |                 if j["error"] == "authorization_pending": | ||||||
|  |                     logger.warning(j["error_description"]) | ||||||
|  |                     self.auth_error_detected.emit(j["error_description"]) | ||||||
|  |                     return | ||||||
|  |                 else: | ||||||
|  |                     raise Exception(j["error_description"]) | ||||||
|  |             resp.raise_for_status() | ||||||
|  |             j = resp.json() | ||||||
|  |             access_token = j["access_token"] | ||||||
|  |             refresh_token = j["refresh_token"] | ||||||
|  |             logger.debug("OAuth device code flow successful") | ||||||
|  |             self.access_token_received.emit(access_token, refresh_token) | ||||||
|  |             self.finished.emit() | ||||||
|  |         except requests.exceptions.RequestException as e: | ||||||
|  |             logger.error(f"Request failed: {e}") | ||||||
|  |             self.error_occurred.emit(str(e)) | ||||||
|  |             self.finished.emit() | ||||||
|  |      | ||||||
|     def send_enter(self): |     def send_enter(self): | ||||||
|         if self.process and self.process.poll() is None: |         self.poll_for_token() | ||||||
|             self.process.stdin.write("\n") |  | ||||||
|             self.process.stdin.flush() |  | ||||||
| 
 | 
 | ||||||
|     def stop(self): |     def stop(self): | ||||||
|         self.is_running = False |         self.is_running = False | ||||||
|         if self.process: |  | ||||||
|             self.process.terminate() |  | ||||||
| 
 | 
 | ||||||
| class MinecraftAuthenticator(QObject):  # Changed to inherit from QObject | class MinecraftAuthenticator(QObject): | ||||||
|     auth_finished = pyqtSignal(bool)  # Add signal for completion |     auth_finished = pyqtSignal(bool) | ||||||
| 
 | 
 | ||||||
|     def __init__(self, parent=None): |     def __init__(self, parent=None): | ||||||
|         super().__init__(parent) |         super().__init__(parent) | ||||||
| @ -175,15 +166,12 @@ class MinecraftAuthenticator(QObject):  # Changed to inherit from QObject | |||||||
|         self.success = False |         self.success = False | ||||||
|          |          | ||||||
|     def authenticate(self, username): |     def authenticate(self, username): | ||||||
|         """ |  | ||||||
|         Start the authentication process for the given username |  | ||||||
|         Returns immediately, authentication result will be emitted via auth_finished signal |  | ||||||
|         """ |  | ||||||
|         self.success = False |         self.success = False | ||||||
|         self.auth_thread = AuthenticationThread(username) |         self.auth_thread = AuthenticationThread(username) | ||||||
|         self.auth_thread.auth_data_received.connect(self.show_auth_dialog) |         self.auth_thread.auth_data_received.connect(self.show_auth_dialog) | ||||||
|         self.auth_thread.auth_error_detected.connect(self.handle_auth_error) |         self.auth_thread.auth_error_detected.connect(self.handle_auth_error) | ||||||
|         self.auth_thread.error_occurred.connect(self.show_error) |         self.auth_thread.error_occurred.connect(self.show_error) | ||||||
|  |         self.auth_thread.access_token_received.connect(self.on_access_token_received) | ||||||
|         self.auth_thread.finished.connect(self.on_authentication_finished) |         self.auth_thread.finished.connect(self.on_authentication_finished) | ||||||
|         self.auth_thread.start() |         self.auth_thread.start() | ||||||
| 
 | 
 | ||||||
| @ -217,6 +205,11 @@ class MinecraftAuthenticator(QObject):  # Changed to inherit from QObject | |||||||
|         self.success = False |         self.success = False | ||||||
|         self.auth_finished.emit(False) |         self.auth_finished.emit(False) | ||||||
| 
 | 
 | ||||||
|  |     def on_access_token_received(self, access_token, refresh_token): | ||||||
|  |         QMessageBox.information(None, "Success", "Authentication successful!") | ||||||
|  |         self.success = True | ||||||
|  |         self.auth_finished.emit(True) | ||||||
|  | 
 | ||||||
|     def on_authentication_finished(self): |     def on_authentication_finished(self): | ||||||
|         if self.auth_dialog is not None: |         if self.auth_dialog is not None: | ||||||
|             self.auth_dialog.close() |             self.auth_dialog.close() | ||||||
| @ -226,8 +219,8 @@ class MinecraftAuthenticator(QObject):  # Changed to inherit from QObject | |||||||
|             self.auth_thread.stop() |             self.auth_thread.stop() | ||||||
|             self.auth_thread = None |             self.auth_thread = None | ||||||
|              |              | ||||||
|         self.success = True |         if not self.success: | ||||||
|         self.auth_finished.emit(True) |             self.auth_finished.emit(False) | ||||||
| 
 | 
 | ||||||
|     def cleanup(self): |     def cleanup(self): | ||||||
|         if self.auth_dialog is not None: |         if self.auth_dialog is not None: | ||||||
| @ -240,5 +233,7 @@ class MinecraftAuthenticator(QObject):  # Changed to inherit from QObject | |||||||
| 
 | 
 | ||||||
| # Example usage | # Example usage | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|  |     app = QApplication(sys.argv) | ||||||
|     authenticator = MinecraftAuthenticator() |     authenticator = MinecraftAuthenticator() | ||||||
|     authenticator.authenticate("TestUser") |     authenticator.authenticate("TestUser") | ||||||
|  |     sys.exit(app.exec_()) | ||||||
|  | |||||||
							
								
								
									
										30
									
								
								modulecli.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								modulecli.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | import click | ||||||
|  | from picomc.cli.main import picomc_cli | ||||||
|  | from io import StringIO | ||||||
|  | import sys | ||||||
|  | 
 | ||||||
|  | def run_command(command="picomc"): | ||||||
|  |     # Redirect stdout and stderr to capture the command output | ||||||
|  |     old_stdout, old_stderr = sys.stdout, sys.stderr | ||||||
|  |     sys.stdout = mystdout = StringIO() | ||||||
|  |     sys.stderr = mystderr = StringIO() | ||||||
|  |      | ||||||
|  |     try: | ||||||
|  |         picomc_cli.main(args=command.split()) | ||||||
|  |     except SystemExit as e: | ||||||
|  |         if e.code != 0: | ||||||
|  |             print(f"Command exited with code {e.code}", file=sys.stderr) | ||||||
|  |     except Exception as e: | ||||||
|  |         print(f"Unexpected error: {e}", file=sys.stderr) | ||||||
|  |     finally: | ||||||
|  |         # Restore stdout and stderr | ||||||
|  |         sys.stdout = old_stdout | ||||||
|  |         sys.stderr = old_stderr | ||||||
|  | 
 | ||||||
|  |     output = mystdout.getvalue().strip() | ||||||
|  |     error = mystderr.getvalue().strip() | ||||||
|  |      | ||||||
|  |     if not output: | ||||||
|  |         return f"Error: No output from command. Stderr: {error}" | ||||||
|  |      | ||||||
|  |     return output | ||||||
							
								
								
									
										281
									
								
								picodulce.py
									
									
									
									
									
								
							
							
						
						
									
										281
									
								
								picodulce.py
									
									
									
									
									
								
							| @ -13,13 +13,14 @@ import time | |||||||
| 
 | 
 | ||||||
| from authser import MinecraftAuthenticator | from authser import MinecraftAuthenticator | ||||||
| from healthcheck import HealthCheck | from healthcheck import HealthCheck | ||||||
|  | import modulecli | ||||||
| 
 | 
 | ||||||
| from PyQt5.QtWidgets import QApplication, QComboBox, QWidget, QInputDialog, QVBoxLayout, QListWidget, QPushButton, QMessageBox, QDialog, QHBoxLayout, QLabel, QLineEdit, QCheckBox, QTabWidget, QFrame, QSpacerItem, QSizePolicy, QMainWindow, QGridLayout, QTextEdit, QListWidget, QListWidgetItem, QMenu | from PyQt5.QtWidgets import QApplication, QComboBox, QWidget, QInputDialog, QVBoxLayout, QListWidget, QPushButton, QMessageBox, QDialog, QHBoxLayout, QLabel, QLineEdit, QCheckBox, QTabWidget, QFrame, QSpacerItem, QSizePolicy, QMainWindow, QGridLayout, QTextEdit, QListWidget, QListWidgetItem, QMenu | ||||||
| from PyQt5.QtGui import QFont, QIcon, QColor, QPalette, QMovie, QPixmap, QDesktopServices, QBrush | from PyQt5.QtGui import QFont, QIcon, QColor, QPalette, QMovie, QPixmap, QDesktopServices, QBrush | ||||||
| from PyQt5.QtCore import Qt, QObject, pyqtSignal, QThread, QUrl, QMetaObject, Q_ARG, QByteArray, QSize | from PyQt5.QtCore import Qt, QObject, pyqtSignal, QThread, QUrl, QMetaObject, Q_ARG, QByteArray, QSize | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
| 
 | 
 | ||||||
| logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s') | logging.basicConfig(level=logging.ERROR, format='%(levelname)s - %(message)s') | ||||||
| 
 | 
 | ||||||
| class PicomcVersionSelector(QWidget): | class PicomcVersionSelector(QWidget): | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
| @ -126,11 +127,13 @@ class PicomcVersionSelector(QWidget): | |||||||
|         try: |         try: | ||||||
|             self.config_path = "config.json" |             self.config_path = "config.json" | ||||||
|             print("Running picomc instance create default command...") |             print("Running picomc instance create default command...") | ||||||
|             # Run the command using subprocess |              | ||||||
|             result = subprocess.run(["picomc", "instance", "create", "default"], check=True, capture_output=True, text=True) |             # Run the command using modulecli | ||||||
|  |             command = "instance create default" | ||||||
|  |             result = modulecli.run_command(command) | ||||||
|              |              | ||||||
|             # Print the output of the command |             # Print the output of the command | ||||||
|             print("Command output:", result.stdout) |             print("Command output:", result) | ||||||
|              |              | ||||||
|             # Change the value of IsFirstLaunch to False |             # Change the value of IsFirstLaunch to False | ||||||
|             self.config["IsFirstLaunch"] = False |             self.config["IsFirstLaunch"] = False | ||||||
| @ -141,9 +144,9 @@ class PicomcVersionSelector(QWidget): | |||||||
|                 json.dump(self.config, f, indent=4) |                 json.dump(self.config, f, indent=4) | ||||||
|             print("Configuration saved to", self.config_path) |             print("Configuration saved to", self.config_path) | ||||||
| 
 | 
 | ||||||
|         except subprocess.CalledProcessError as e: |         except Exception as e: | ||||||
|             print("An error occurred while creating the instance.") |             print("An error occurred while creating the instance.") | ||||||
|             print("Error output:", e.stderr) |             print("Error output:", str(e)) | ||||||
| 
 | 
 | ||||||
|     def resize_event(self, event): |     def resize_event(self, event): | ||||||
|         if hasattr(self, 'movie_label'): |         if hasattr(self, 'movie_label'): | ||||||
| @ -394,8 +397,8 @@ class PicomcVersionSelector(QWidget): | |||||||
|                 discord_rcp_checkbox.isChecked(), |                 discord_rcp_checkbox.isChecked(), | ||||||
|                 check_updates_checkbox.isChecked(), |                 check_updates_checkbox.isChecked(), | ||||||
|                 theme_background_checkbox.isChecked(), |                 theme_background_checkbox.isChecked(), | ||||||
|                 self.selected_theme,  # Pass the selected theme here |                 self.selected_theme, | ||||||
|                 bleeding_edge_checkbox.isChecked()  # Pass the bleeding edge setting here |                 bleeding_edge_checkbox.isChecked()  | ||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
| @ -659,20 +662,6 @@ class PicomcVersionSelector(QWidget): | |||||||
|         ) |         ) | ||||||
|         self.__init__() |         self.__init__() | ||||||
| 
 | 
 | ||||||
|     def get_palette(self, palette_type): |  | ||||||
|         """Retrieve the corresponding palette based on the palette type.""" |  | ||||||
|         palettes = { |  | ||||||
|             "Dark": self.create_dark_palette, |  | ||||||
|             "Obsidian": self.create_obsidian_palette, |  | ||||||
|             "Redstone": self.create_redstone_palette, |  | ||||||
|             "Alpha": self.create_alpha_palette, |  | ||||||
|             "Strawberry": self.create_strawberry_palette, |  | ||||||
|             "Native": self.create_native_palette, |  | ||||||
|             "Christmas": self.create_christmas_palette, |  | ||||||
|         } |  | ||||||
|         # Default to dark palette if the type is not specified or invalid |  | ||||||
|         return palettes.get(palette_type, self.create_dark_palette)() |  | ||||||
| 
 |  | ||||||
|     def get_system_info(self): |     def get_system_info(self): | ||||||
|         # Get system information |         # Get system information | ||||||
|         java_version = subprocess.getoutput("java -version 2>&1 | head -n 1") |         java_version = subprocess.getoutput("java -version 2>&1 | head -n 1") | ||||||
| @ -712,16 +701,16 @@ class PicomcVersionSelector(QWidget): | |||||||
| 
 | 
 | ||||||
|     def open_game_directory(self): |     def open_game_directory(self): | ||||||
|         try: |         try: | ||||||
|             # Run the command and capture the output |             # Run the command using modulecli | ||||||
|             result = subprocess.run(['picomc', 'instance', 'dir'], capture_output=True, text=True, check=True) |             command = "instance dir" | ||||||
|             game_directory = result.stdout.strip() |             result = modulecli.run_command(command) | ||||||
|  |             game_directory = result.strip() | ||||||
| 
 | 
 | ||||||
|             # Open the directory in the system's file explorer |             # Open the directory in the system's file explorer | ||||||
|             QDesktopServices.openUrl(QUrl.fromLocalFile(game_directory)) |             QDesktopServices.openUrl(QUrl.fromLocalFile(game_directory)) | ||||||
|         except subprocess.CalledProcessError as e: |         except Exception as e: | ||||||
|             print(f"Error running picomc command: {e}") |             print(f"Error running picomc command: {e}") | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def populate_installed_versions(self): |     def populate_installed_versions(self): | ||||||
|         config_path = "config.json" |         config_path = "config.json" | ||||||
|          |          | ||||||
| @ -742,20 +731,17 @@ class PicomcVersionSelector(QWidget): | |||||||
| 
 | 
 | ||||||
|         # Run the command and capture the output |         # Run the command and capture the output | ||||||
|         try: |         try: | ||||||
|             process = subprocess.Popen(['picomc', 'version', 'list'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) |             command = "version list" | ||||||
|             output, error = process.communicate() |             output = modulecli.run_command(command) | ||||||
|              |              | ||||||
|             if process.returncode != 0: |             if not output: | ||||||
|                 raise subprocess.CalledProcessError(process.returncode, process.args, output=output, stderr=error) |                 raise Exception("Failed to get output from modulecli") | ||||||
|         except FileNotFoundError: |         except Exception as e: | ||||||
|             logging.error("'picomc' command not found. Please ensure it's installed and in your PATH.") |             logging.error("Error running 'picomc': %s", e) | ||||||
|             return |  | ||||||
|         except subprocess.CalledProcessError as e: |  | ||||||
|             logging.error("Error running 'picomc': %s", e.stderr) |  | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         # Parse the output and replace '[local]' with a space |         # Parse the output and replace '[local]' with a space | ||||||
|         versions = [version.replace('[local]', ' ').strip() for version in output.splitlines()] |         versions = [version.replace('[local]', ' ').strip() for version in output.splitlines() if version.strip()] | ||||||
| 
 | 
 | ||||||
|         # Get the last played version from the config |         # Get the last played version from the config | ||||||
|         last_played = self.config.get("LastPlayed", "") |         last_played = self.config.get("LastPlayed", "") | ||||||
| @ -768,32 +754,26 @@ class PicomcVersionSelector(QWidget): | |||||||
|         # Populate the installed versions combo box |         # Populate the installed versions combo box | ||||||
|         self.installed_version_combo.clear() |         self.installed_version_combo.clear() | ||||||
|         self.installed_version_combo.addItems(versions) |         self.installed_version_combo.addItems(versions) | ||||||
| 
 |      | ||||||
|     def populate_installed_versions_normal_order(self): |     def populate_installed_versions_normal_order(self): | ||||||
|         # Run the 'picomc instance create default' command at the start |         # Run the 'picomc instance create default' command at the start | ||||||
|         try: |         try: | ||||||
|             process = subprocess.Popen(['picomc', 'instance', 'create', 'default'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) |             command = "instance create default" | ||||||
|             output, error = process.communicate() |             output = modulecli.run_command(command) | ||||||
|             if process.returncode != 0: |             if not output: | ||||||
|                 raise subprocess.CalledProcessError(process.returncode, process.args, error) |                 raise Exception("Failed to get output from modulecli for 'instance create default'") | ||||||
|         except FileNotFoundError: |         except Exception as e: | ||||||
|             logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.") |             logging.error("Error creating default instance: %s", str(e)) | ||||||
|             return |  | ||||||
|         except subprocess.CalledProcessError as e: |  | ||||||
|             logging.error("Error creating default instance: %s", e.stderr) |  | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         # Run the 'picomc version list' command and get the output |         # Run the 'picomc version list' command and get the output | ||||||
|         try: |         try: | ||||||
|             process = subprocess.Popen(['picomc', 'version', 'list'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) |             command = "version list" | ||||||
|             output, error = process.communicate() |             output = modulecli.run_command(command) | ||||||
|             if process.returncode != 0: |             if not output: | ||||||
|                 raise subprocess.CalledProcessError(process.returncode, process.args, error) |                 raise Exception("Failed to get output from modulecli for 'version list'") | ||||||
|         except FileNotFoundError: |         except Exception as e: | ||||||
|             logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.") |             logging.error("Error: %s", str(e)) | ||||||
|             return |  | ||||||
|         except subprocess.CalledProcessError as e: |  | ||||||
|             logging.error("Error: %s", e.stderr) |  | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         # Parse the output and replace '[local]' with a space |         # Parse the output and replace '[local]' with a space | ||||||
| @ -820,7 +800,7 @@ class PicomcVersionSelector(QWidget): | |||||||
| 
 | 
 | ||||||
|         # Check if there are any accounts |         # Check if there are any accounts | ||||||
|         try: |         try: | ||||||
|             account_list_output = subprocess.check_output(["picomc", "account", "list"]).decode("utf-8").strip() |             account_list_output = modulecli.run_command("account list").strip() | ||||||
|             if not account_list_output: |             if not account_list_output: | ||||||
|                 QMessageBox.warning(self, "No Account Available", "Please create an account first.") |                 QMessageBox.warning(self, "No Account Available", "Please create an account first.") | ||||||
|                 return |                 return | ||||||
| @ -829,18 +809,25 @@ class PicomcVersionSelector(QWidget): | |||||||
|             if '*' not in account_list_output: |             if '*' not in account_list_output: | ||||||
|                 QMessageBox.warning(self, "No Account Selected", "Please select an account.") |                 QMessageBox.warning(self, "No Account Selected", "Please select an account.") | ||||||
|                 return |                 return | ||||||
|         except subprocess.CalledProcessError as e: |         except Exception as e: | ||||||
|             error_message = f"Error fetching accounts: {str(e)}" |             error_message = f"Error fetching accounts: {str(e)}" | ||||||
|             logging.error(error_message) |             logging.error(error_message) | ||||||
|             QMessageBox.critical(self, "Error", error_message) |             QMessageBox.critical(self, "Error", error_message) | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         selected_instance = self.installed_version_combo.currentText() |         selected_instance = self.installed_version_combo.currentText() | ||||||
|         logging.info(f"Selected instance: {selected_instance}") |         logging.info(f"Selected instance from dropdown: {selected_instance}") | ||||||
|  | 
 | ||||||
|  |         # Verify the selected instance value before starting the game | ||||||
|  |         if not selected_instance: | ||||||
|  |             logging.error("No instance selected.") | ||||||
|  |             QMessageBox.warning(self, "No Instance Selected", "Please select an instance.") | ||||||
|  |             return | ||||||
| 
 | 
 | ||||||
|         play_thread = threading.Thread(target=self.run_game, args=(selected_instance,)) |         play_thread = threading.Thread(target=self.run_game, args=(selected_instance,)) | ||||||
|         play_thread.start() |         play_thread.start() | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     def run_game(self, selected_instance): |     def run_game(self, selected_instance): | ||||||
|         try: |         try: | ||||||
|             # Set current_state to the selected instance |             # Set current_state to the selected instance | ||||||
| @ -855,10 +842,14 @@ class PicomcVersionSelector(QWidget): | |||||||
|             update_thread = threading.Thread(target=self.update_last_played, args=(selected_instance,)) |             update_thread = threading.Thread(target=self.update_last_played, args=(selected_instance,)) | ||||||
|             update_thread.start() |             update_thread.start() | ||||||
| 
 | 
 | ||||||
|             # Run the game subprocess with the instance_value from config.json |             # Run the game using the modulecli module | ||||||
|             subprocess.run(['picomc', 'instance', 'launch', '--version-override', selected_instance, instance_value], check=True) |             command = f"instance launch --version-override {selected_instance} {instance_value}" | ||||||
|  |             output = modulecli.run_command(command) | ||||||
|  |              | ||||||
|  |             if not output: | ||||||
|  |                 raise Exception("Failed to get output from modulecli") | ||||||
| 
 | 
 | ||||||
|         except subprocess.CalledProcessError as e: |         except Exception as e: | ||||||
|             error_message = f"Error playing {selected_instance}: {e}" |             error_message = f"Error playing {selected_instance}: {e}" | ||||||
|             logging.error(error_message) |             logging.error(error_message) | ||||||
|             # Use QMetaObject.invokeMethod to call showError safely |             # Use QMetaObject.invokeMethod to call showError safely | ||||||
| @ -870,6 +861,7 @@ class PicomcVersionSelector(QWidget): | |||||||
|             # Reset current_state to "menu" after the game closes |             # Reset current_state to "menu" after the game closes | ||||||
|             self.current_state = "menu" |             self.current_state = "menu" | ||||||
|              |              | ||||||
|  |              | ||||||
|     def update_last_played(self, selected_instance): |     def update_last_played(self, selected_instance): | ||||||
|         config_path = "config.json" |         config_path = "config.json" | ||||||
|         self.config["LastPlayed"] = selected_instance |         self.config["LastPlayed"] = selected_instance | ||||||
| @ -967,16 +959,16 @@ class PicomcVersionSelector(QWidget): | |||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         try: |         try: | ||||||
|             command = ['picomc', 'account', 'create', username] |             command = f"account create {username}" | ||||||
|             if is_microsoft: |             if is_microsoft: | ||||||
|                 command.append('--ms') |                 command += " --ms" | ||||||
| 
 | 
 | ||||||
|             subprocess.run(command, check=True) |             modulecli.run_command(command) | ||||||
|             QMessageBox.information(dialog, "Success", f"Account '{username}' created successfully!") |             QMessageBox.information(dialog, "Success", f"Account '{username}' created successfully!") | ||||||
|             self.populate_accounts_for_all_dialogs() |             self.populate_accounts_for_all_dialogs() | ||||||
|             dialog.accept() |             dialog.accept() | ||||||
|         except subprocess.CalledProcessError as e: |         except Exception as e: | ||||||
|             error_message = f"Error creating account: {e.stderr.decode()}" |             error_message = f"Error creating account: {str(e)}" | ||||||
|             logging.error(error_message) |             logging.error(error_message) | ||||||
|             QMessageBox.critical(dialog, "Error", error_message) |             QMessageBox.critical(dialog, "Error", error_message) | ||||||
| 
 | 
 | ||||||
| @ -1029,23 +1021,21 @@ class PicomcVersionSelector(QWidget): | |||||||
|         confirm_dialog = QMessageBox.question(dialog, "Confirm Removal", confirm_message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) |         confirm_dialog = QMessageBox.question(dialog, "Confirm Removal", confirm_message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) | ||||||
|         if confirm_dialog == QMessageBox.Yes: |         if confirm_dialog == QMessageBox.Yes: | ||||||
|             try: |             try: | ||||||
|                 subprocess.run(['picomc', 'account', 'remove', username], check=True) |                 command = f"account remove {username}" | ||||||
|  |                 modulecli.run_command(command) | ||||||
|                 QMessageBox.information(dialog, "Success", f"Account '{username}' removed successfully!") |                 QMessageBox.information(dialog, "Success", f"Account '{username}' removed successfully!") | ||||||
|                 self.populate_accounts_for_all_dialogs() |                 self.populate_accounts_for_all_dialogs() | ||||||
|             except subprocess.CalledProcessError as e: |             except Exception as e: | ||||||
|                 error_message = f"Error removing account: {e.stderr.decode()}" |                 error_message = f"Error removing account: {str(e)}" | ||||||
|                 logging.error(error_message) |                 logging.error(error_message) | ||||||
|                 QMessageBox.critical(dialog, "Error", error_message) |                 QMessageBox.critical(dialog, "Error", error_message) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def populate_accounts(self, account_combo): |     def populate_accounts(self, account_combo): | ||||||
|         # Populate the account dropdown |         # Populate the account dropdown | ||||||
|         try: |         try: | ||||||
|             process = subprocess.Popen(['picomc', 'account', 'list'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) |             command = "account list" | ||||||
|             output, error = process.communicate() |             output = modulecli.run_command(command) | ||||||
|             if process.returncode != 0: |              | ||||||
|                 raise subprocess.CalledProcessError(process.returncode, process.args, error) |  | ||||||
| 
 |  | ||||||
|             # Process accounts, keeping the one with "*" at the top |             # Process accounts, keeping the one with "*" at the top | ||||||
|             accounts = output.splitlines() |             accounts = output.splitlines() | ||||||
|             starred_account = None |             starred_account = None | ||||||
| @ -1069,10 +1059,8 @@ class PicomcVersionSelector(QWidget): | |||||||
|             for account in normal_accounts: |             for account in normal_accounts: | ||||||
|                 account_combo.addItem(account) |                 account_combo.addItem(account) | ||||||
| 
 | 
 | ||||||
|         except FileNotFoundError: |         except Exception as e: | ||||||
|             logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.") |             logging.error(f"Error: {str(e)}") | ||||||
|         except subprocess.CalledProcessError as e: |  | ||||||
|             logging.error(f"Error: {e.stderr}") |  | ||||||
| 
 | 
 | ||||||
|     def populate_accounts_for_all_dialogs(self): |     def populate_accounts_for_all_dialogs(self): | ||||||
|         # Update account dropdowns in all open dialogs |         # Update account dropdowns in all open dialogs | ||||||
| @ -1089,14 +1077,16 @@ class PicomcVersionSelector(QWidget): | |||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         try: |         try: | ||||||
|             subprocess.run(['picomc', 'account', 'setdefault', account_name], check=True) |             command = f"account setdefault {account_name}" | ||||||
|  |             modulecli.run_command(command) | ||||||
|             QMessageBox.information(self, "Success", f"Account '{account_name}' set as default!") |             QMessageBox.information(self, "Success", f"Account '{account_name}' set as default!") | ||||||
|             self.populate_accounts_for_all_dialogs() |             self.populate_accounts_for_all_dialogs() | ||||||
|         except subprocess.CalledProcessError as e: |         except Exception as e: | ||||||
|             error_message = f"Error setting default account '{account_name}': {e.stderr.decode()}" |             error_message = f"Error setting default account '{account_name}': {str(e)}" | ||||||
|             logging.error(error_message) |             logging.error(error_message) | ||||||
|             QMessageBox.critical(self, "Error", error_message) |             QMessageBox.critical(self, "Error", error_message) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     def show_about_dialog(self): |     def show_about_dialog(self): | ||||||
|         # Load the version number from version.json |         # Load the version number from version.json | ||||||
|         try: |         try: | ||||||
| @ -1326,10 +1316,11 @@ class DownloadThread(QThread): | |||||||
| 
 | 
 | ||||||
|     def run(self): |     def run(self): | ||||||
|         try: |         try: | ||||||
|             subprocess.run(['picomc', 'version', 'prepare', self.version], check=True) |             command = f"version prepare {self.version}" | ||||||
|  |             modulecli.run_command(command) | ||||||
|             self.completed.emit(True, f"Version {self.version} prepared successfully!") |             self.completed.emit(True, f"Version {self.version} prepared successfully!") | ||||||
|         except subprocess.CalledProcessError as e: |         except Exception as e: | ||||||
|             error_message = f"Error preparing {self.version}: {e.stderr.decode()}" |             error_message = f"Error preparing {self.version}: {str(e)}" | ||||||
|             self.completed.emit(False, error_message) |             self.completed.emit(False, error_message) | ||||||
| 
 | 
 | ||||||
| class ModLoaderAndVersionMenu(QDialog): | class ModLoaderAndVersionMenu(QDialog): | ||||||
| @ -1346,11 +1337,11 @@ class ModLoaderAndVersionMenu(QDialog): | |||||||
|         # Create tabs |         # Create tabs | ||||||
|         install_mod_tab = QWidget() |         install_mod_tab = QWidget() | ||||||
|         download_version_tab = QWidget() |         download_version_tab = QWidget() | ||||||
|         instances_tab = QWidget()  # New tab for instances |         instances_tab = QWidget() | ||||||
| 
 | 
 | ||||||
|         tab_widget.addTab(download_version_tab, "Download Version") |         tab_widget.addTab(download_version_tab, "Download Version") | ||||||
|         tab_widget.addTab(install_mod_tab, "Install Mod Loader") |         tab_widget.addTab(install_mod_tab, "Install Mod Loader") | ||||||
|         tab_widget.addTab(instances_tab, "Instances")  # Add the new tab |         tab_widget.addTab(instances_tab, "Instances") | ||||||
| 
 | 
 | ||||||
|         # Add content to "Install Mod Loader" tab |         # Add content to "Install Mod Loader" tab | ||||||
|         self.setup_install_mod_loader_tab(install_mod_tab) |         self.setup_install_mod_loader_tab(install_mod_tab) | ||||||
| @ -1402,11 +1393,8 @@ class ModLoaderAndVersionMenu(QDialog): | |||||||
|         if instance_name: |         if instance_name: | ||||||
|             try: |             try: | ||||||
|                 # Run the "picomc instance create" command |                 # Run the "picomc instance create" command | ||||||
|                 process = subprocess.Popen(['picomc', 'instance', 'create', instance_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) |                 command = f"instance create {instance_name}" | ||||||
|                 output, error = process.communicate() |                 modulecli.run_command(command) | ||||||
| 
 |  | ||||||
|                 if process.returncode != 0: |  | ||||||
|                     raise subprocess.CalledProcessError(process.returncode, process.args, error) |  | ||||||
| 
 | 
 | ||||||
|                 # Notify the user that the instance was created |                 # Notify the user that the instance was created | ||||||
|                 QMessageBox.information(self, "Instance Created", f"Instance '{instance_name}' has been created successfully.") |                 QMessageBox.information(self, "Instance Created", f"Instance '{instance_name}' has been created successfully.") | ||||||
| @ -1417,11 +1405,9 @@ class ModLoaderAndVersionMenu(QDialog): | |||||||
|                 # Optionally select the newly created instance |                 # Optionally select the newly created instance | ||||||
|                 self.on_instance_selected(self.instances_list_widget.item(self.instances_list_widget.count() - 1)) |                 self.on_instance_selected(self.instances_list_widget.item(self.instances_list_widget.count() - 1)) | ||||||
| 
 | 
 | ||||||
|             except FileNotFoundError: |             except Exception as e: | ||||||
|                 logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.") |                 logging.error("Error creating instance: %s", str(e)) | ||||||
|             except subprocess.CalledProcessError as e: |                 QMessageBox.critical(self, "Error", f"Failed to create instance: {str(e)}") | ||||||
|                 logging.error("Error creating instance: %s", e.stderr) |  | ||||||
|                 QMessageBox.critical(self, "Error", f"Failed to create instance: {e.stderr}") |  | ||||||
|         else: |         else: | ||||||
|             QMessageBox.warning(self, "Invalid Input", "Please enter a valid instance name.") |             QMessageBox.warning(self, "Invalid Input", "Please enter a valid instance name.") | ||||||
| 
 | 
 | ||||||
| @ -1432,14 +1418,8 @@ class ModLoaderAndVersionMenu(QDialog): | |||||||
| 
 | 
 | ||||||
|         try: |         try: | ||||||
|             # Run the "picomc instance rename" command |             # Run the "picomc instance rename" command | ||||||
|             process = subprocess.Popen( |             command = f"instance rename {old_instance_name} {new_instance_name}" | ||||||
|                 ['picomc', 'instance', 'rename', old_instance_name, new_instance_name], |             modulecli.run_command(command) | ||||||
|                 stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True |  | ||||||
|             ) |  | ||||||
|             output, error = process.communicate() |  | ||||||
| 
 |  | ||||||
|             if process.returncode != 0: |  | ||||||
|                 raise subprocess.CalledProcessError(process.returncode, process.args, error) |  | ||||||
| 
 | 
 | ||||||
|             QMessageBox.information(self, "Instance Renamed", f"Instance '{old_instance_name}' has been renamed to '{new_instance_name}' successfully.") |             QMessageBox.information(self, "Instance Renamed", f"Instance '{old_instance_name}' has been renamed to '{new_instance_name}' successfully.") | ||||||
| 
 | 
 | ||||||
| @ -1451,11 +1431,9 @@ class ModLoaderAndVersionMenu(QDialog): | |||||||
|             if matching_items: |             if matching_items: | ||||||
|                 self.instances_list_widget.setCurrentItem(matching_items[0]) |                 self.instances_list_widget.setCurrentItem(matching_items[0]) | ||||||
| 
 | 
 | ||||||
|         except FileNotFoundError: |         except Exception as e: | ||||||
|             logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.") |             logging.error("Error renaming instance: %s", str(e)) | ||||||
|         except subprocess.CalledProcessError as e: |             QMessageBox.critical(self, "Error", f"Failed to rename instance: {str(e)}") | ||||||
|             logging.error("Error renaming instance: %s", e.stderr) |  | ||||||
|             QMessageBox.critical(self, "Error", f"Failed to rename instance: {e.stderr}") |  | ||||||
| 
 | 
 | ||||||
|     def delete_instance(self, instance_name): |     def delete_instance(self, instance_name): | ||||||
|         if instance_name == "default": |         if instance_name == "default": | ||||||
| @ -1470,11 +1448,8 @@ class ModLoaderAndVersionMenu(QDialog): | |||||||
|         if confirm_delete == QMessageBox.Yes: |         if confirm_delete == QMessageBox.Yes: | ||||||
|             try: |             try: | ||||||
|                 # Run the "picomc instance delete" command |                 # Run the "picomc instance delete" command | ||||||
|                 process = subprocess.Popen(['picomc', 'instance', 'delete', instance_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) |                 command = f"instance delete {instance_name}" | ||||||
|                 output, error = process.communicate() |                 modulecli.run_command(command) | ||||||
| 
 |  | ||||||
|                 if process.returncode != 0: |  | ||||||
|                     raise subprocess.CalledProcessError(process.returncode, process.args, error) |  | ||||||
| 
 | 
 | ||||||
|                 # Notify the user that the instance was deleted |                 # Notify the user that the instance was deleted | ||||||
|                 QMessageBox.information(self, "Instance Deleted", f"Instance '{instance_name}' has been deleted successfully.") |                 QMessageBox.information(self, "Instance Deleted", f"Instance '{instance_name}' has been deleted successfully.") | ||||||
| @ -1482,19 +1457,16 @@ class ModLoaderAndVersionMenu(QDialog): | |||||||
|                 # Reload the instances list |                 # Reload the instances list | ||||||
|                 self.load_instances() |                 self.load_instances() | ||||||
| 
 | 
 | ||||||
|             except FileNotFoundError: |             except Exception as e: | ||||||
|                 logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.") |                 logging.error("Error deleting instance: %s", str(e)) | ||||||
|             except subprocess.CalledProcessError as e: |                 QMessageBox.critical(self, "Error", f"Failed to delete instance: {str(e)}") | ||||||
|                 logging.error("Error deleting instance: %s", e.stderr) |  | ||||||
|                 QMessageBox.critical(self, "Error", f"Failed to delete instance: {e.stderr}") |  | ||||||
| 
 | 
 | ||||||
|     def load_instances(self): |     def load_instances(self): | ||||||
|         try: |         try: | ||||||
|             process = subprocess.Popen(['picomc', 'instance', 'list'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) |             # Run the "picomc instance list" command | ||||||
|             output, error = process.communicate() |             command = "instance list" | ||||||
|             if process.returncode != 0: |             output = modulecli.run_command(command) | ||||||
|                 raise subprocess.CalledProcessError(process.returncode, process.args, error) |              | ||||||
| 
 |  | ||||||
|             # Parse the output and add each instance to the list widget |             # Parse the output and add each instance to the list widget | ||||||
|             instances = output.splitlines() |             instances = output.splitlines() | ||||||
|             self.instances_list_widget.clear()  # Clear the previous list |             self.instances_list_widget.clear()  # Clear the previous list | ||||||
| @ -1503,10 +1475,9 @@ class ModLoaderAndVersionMenu(QDialog): | |||||||
|                 self.instances_list_widget.addItem(item) |                 self.instances_list_widget.addItem(item) | ||||||
|                 self.add_instance_buttons(item, instance) |                 self.add_instance_buttons(item, instance) | ||||||
| 
 | 
 | ||||||
|         except FileNotFoundError: |         except Exception as e: | ||||||
|             logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.") |             logging.error("Error fetching instances: %s", str(e)) | ||||||
|         except subprocess.CalledProcessError as e: | 
 | ||||||
|             logging.error("Error fetching instances: %s", e.stderr) |  | ||||||
| 
 | 
 | ||||||
|     def add_instance_buttons(self, list_item, instance_name): |     def add_instance_buttons(self, list_item, instance_name): | ||||||
|         widget = QWidget() |         widget = QWidget() | ||||||
| @ -1662,21 +1633,19 @@ class ModLoaderAndVersionMenu(QDialog): | |||||||
|                 options.append('--beta') |                 options.append('--beta') | ||||||
|             if options: |             if options: | ||||||
|                 try: |                 try: | ||||||
|                     process = subprocess.Popen(['picomc', 'version', 'list'] + options, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) |                     command = 'version list ' + ' '.join(options) | ||||||
|                     output, error = process.communicate() |                     output = modulecli.run_command(command) | ||||||
|                     if process.returncode != 0: |                     if "Error" in output: | ||||||
|                         raise subprocess.CalledProcessError(process.returncode, process.args, error) |                         logging.error(output) | ||||||
|                 except FileNotFoundError: |                         return | ||||||
|                     logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.") |  | ||||||
|                     return |  | ||||||
|                 except subprocess.CalledProcessError as e: |  | ||||||
|                     logging.error("Error: %s", e.stderr) |  | ||||||
|                     return |  | ||||||
| 
 | 
 | ||||||
|                 # Parse the output and replace '[local]' with a space |                     # Parse the output and replace '[local]' with a space | ||||||
|                 versions = output.splitlines() |                     versions = output.splitlines() | ||||||
|                 versions = [version.replace('[local]', ' ').strip() for version in versions] |                     versions = [version.replace('[local]', ' ').strip() for version in versions] | ||||||
|                 self.version_combo.addItems(versions) |                     self.version_combo.addItems(versions) | ||||||
|  |                 except Exception as e: | ||||||
|  |                     logging.error("Unexpected error: %s", e) | ||||||
|  |                     return | ||||||
|             # Update the download button state whenever versions are updated |             # Update the download button state whenever versions are updated | ||||||
|             self.update_download_button_state() |             self.update_download_button_state() | ||||||
| 
 | 
 | ||||||
| @ -1693,7 +1662,7 @@ class ModLoaderAndVersionMenu(QDialog): | |||||||
| 
 | 
 | ||||||
|         # Connect the combo box signal to the update function |         # Connect the combo box signal to the update function | ||||||
|         self.version_combo.currentIndexChanged.connect(self.update_download_button_state) |         self.version_combo.currentIndexChanged.connect(self.update_download_button_state) | ||||||
| 
 |          | ||||||
|     def update_download_button_state(self): |     def update_download_button_state(self): | ||||||
|         self.download_button.setEnabled(self.version_combo.currentIndex() != -1) |         self.download_button.setEnabled(self.version_combo.currentIndex() != -1) | ||||||
| 
 | 
 | ||||||
| @ -1733,15 +1702,10 @@ class ModLoaderAndVersionMenu(QDialog): | |||||||
| 
 | 
 | ||||||
|     def populate_available_releases(self, version_combo, install_forge, install_fabric): |     def populate_available_releases(self, version_combo, install_forge, install_fabric): | ||||||
|         try: |         try: | ||||||
|             process = subprocess.Popen(['picomc', 'version', 'list', '--release'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) |             command = "version list --release" | ||||||
|             output, error = process.communicate() |             output = modulecli.run_command(command) | ||||||
|             if process.returncode != 0: |         except Exception as e: | ||||||
|                 raise subprocess.CalledProcessError(process.returncode, process.args, error) |             logging.error("Error: %s", str(e)) | ||||||
|         except FileNotFoundError: |  | ||||||
|             logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.") |  | ||||||
|             return |  | ||||||
|         except subprocess.CalledProcessError as e: |  | ||||||
|             logging.error("Error: %s", e.stderr) |  | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         if install_fabric: |         if install_fabric: | ||||||
| @ -1771,12 +1735,13 @@ class ModLoaderAndVersionMenu(QDialog): | |||||||
| 
 | 
 | ||||||
|         try: |         try: | ||||||
|             if mod_loader == 'forge': |             if mod_loader == 'forge': | ||||||
|                 subprocess.run(['picomc', 'mod', 'loader', 'forge', 'install', '--game', version], check=True) |                 command = f"mod loader forge install --game {version}" | ||||||
|             elif mod_loader == 'fabric': |             elif mod_loader == 'fabric': | ||||||
|                 subprocess.run(['picomc', 'mod', 'loader', 'fabric', 'install', version], check=True) |                 command = f"mod loader fabric install {version}" | ||||||
|  |             modulecli.run_command(command) | ||||||
|             QMessageBox.information(self, "Success", f"{mod_loader.capitalize()} installed successfully for version {version}!") |             QMessageBox.information(self, "Success", f"{mod_loader.capitalize()} installed successfully for version {version}!") | ||||||
|         except subprocess.CalledProcessError as e: |         except Exception as e: | ||||||
|             error_message = f"Error installing {mod_loader} for version {version}: {e.stderr.decode()}" |             error_message = f"Error installing {mod_loader} for version {version}: {str(e)}" | ||||||
|             QMessageBox.critical(self, "Error", error_message) |             QMessageBox.critical(self, "Error", error_message) | ||||||
|             logging.error(error_message) |             logging.error(error_message) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| { | { | ||||||
|   "version": "0.12.1", |   "version": "0.13", | ||||||
|   "links": [ |   "links": [ | ||||||
|     "https://raw.githubusercontent.com/nixietab/picodulce/main/version.json", |     "https://raw.githubusercontent.com/nixietab/picodulce/main/version.json", | ||||||
|     "https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py", |     "https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py", | ||||||
| @ -8,7 +8,8 @@ | |||||||
|     "https://raw.githubusercontent.com/nixietab/picodulce/main/marroc.py", |     "https://raw.githubusercontent.com/nixietab/picodulce/main/marroc.py", | ||||||
|     "https://raw.githubusercontent.com/nixietab/picodulce/main/holiday.ico", |     "https://raw.githubusercontent.com/nixietab/picodulce/main/holiday.ico", | ||||||
|     "https://raw.githubusercontent.com/nixietab/picodulce/main/authser.py", |     "https://raw.githubusercontent.com/nixietab/picodulce/main/authser.py", | ||||||
|     "https://raw.githubusercontent.com/nixietab/picodulce/main/healthcheck.py" |     "https://raw.githubusercontent.com/nixietab/picodulce/main/healthcheck.py", | ||||||
|  |     "https://raw.githubusercontent.com/nixietab/picodulce/main/modulecli.py" | ||||||
|   ], |   ], | ||||||
|   "versionBleeding": "0.12.1-188" |   "versionBleeding": "0.13-194" | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user