mirror of
https://github.com/nixietab/picodulce.git
synced 2025-04-04 07:28:56 +01:00
175 lines
6.7 KiB
Python
175 lines
6.7 KiB
Python
import sys
|
|
import subprocess
|
|
from PyQt5.QtWidgets import (
|
|
QApplication, QMainWindow, QVBoxLayout,
|
|
QWidget, QPushButton, QDialog, QLabel,
|
|
QProgressBar
|
|
)
|
|
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QObject
|
|
|
|
|
|
class LaunchDialog(QDialog):
|
|
"""Dialog displayed during the game launch process."""
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
self.setWindowTitle("Launching Game")
|
|
self.setFixedSize(400, 100)
|
|
|
|
# Remove context help button
|
|
self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
|
|
|
|
# Layout and widgets for status and progress
|
|
layout = QVBoxLayout()
|
|
self.status_label = QLabel("Starting game launcher...")
|
|
self.status_label.setAlignment(Qt.AlignCenter)
|
|
layout.addWidget(self.status_label)
|
|
|
|
self.progress_bar = QProgressBar()
|
|
self.progress_bar.setRange(0, 0) # Indeterminate progress bar, going to change this latter
|
|
layout.addWidget(self.progress_bar)
|
|
|
|
self.setLayout(layout)
|
|
|
|
|
|
class PicomcThread(QThread):
|
|
"""Thread that handles launching the game and reading output."""
|
|
|
|
# Signals to notify parent on various events
|
|
output_received = pyqtSignal(str)
|
|
game_launched = pyqtSignal()
|
|
error_occurred = pyqtSignal(str, str)
|
|
|
|
def __init__(self, cmd):
|
|
super().__init__()
|
|
self.cmd = cmd # Command to run
|
|
self.process = None # Process reference
|
|
self.stop_parsing = False # Flag to stop output parsing
|
|
|
|
def run(self):
|
|
"""Executes the game launch command and processes its output."""
|
|
try:
|
|
# Start the process with subprocess
|
|
cmd_str = ' '.join(self.cmd)
|
|
self.process = subprocess.Popen(
|
|
cmd_str,
|
|
shell=True,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
bufsize=1,
|
|
universal_newlines=True
|
|
)
|
|
|
|
# Parse the output until the game is launched
|
|
while True:
|
|
output = self.process.stdout.readline()
|
|
if not output and self.process.poll() is not None:
|
|
break # Process finished
|
|
|
|
if output:
|
|
line = output.strip()
|
|
if line:
|
|
self.output_received.emit(line)
|
|
|
|
if line == "INFO Launching the game":
|
|
self.game_launched.emit() # Notify that the game is launching
|
|
break # Stop parsing
|
|
|
|
# Print all remaining logs after the game has started
|
|
print("\n[INFO] Game has launched! Showing live logs...\n")
|
|
while True:
|
|
output = self.process.stdout.readline()
|
|
if not output and self.process.poll() is not None:
|
|
break # Process finished
|
|
|
|
if output:
|
|
print(output.strip()) # Display logs in the terminal
|
|
|
|
except Exception as e:
|
|
self.error_occurred.emit("Error", f"Error launching game: {str(e)}")
|
|
|
|
|
|
class MinecraftLauncher(QObject):
|
|
"""Handles the Minecraft game launcher, dialog creation, and error handling."""
|
|
|
|
# Signals for dialog and status updates
|
|
create_dialog_signal = pyqtSignal()
|
|
close_dialog_signal = pyqtSignal()
|
|
update_status_signal = pyqtSignal(str)
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.parent_widget = None # Parent widget reference
|
|
self.launch_dialog = None # Launch dialog instance
|
|
self.picomc_thread = None # Thread instance for game launch
|
|
|
|
# Connect signals to internal methods
|
|
self.create_dialog_signal.connect(self._create_dialog)
|
|
self.close_dialog_signal.connect(self._close_dialog)
|
|
self.update_status_signal.connect(self._update_status)
|
|
|
|
def set_parent_widget(self, parent):
|
|
"""Set the parent widget for the launcher."""
|
|
self.parent_widget = parent
|
|
|
|
def launch_game(self, cmd):
|
|
"""Launches the game by running the picomc command."""
|
|
try:
|
|
print("[INFO] Creating popup...")
|
|
self.create_dialog_signal.emit() # Show the launch dialog
|
|
|
|
print("[INFO] Starting picomc thread...")
|
|
self.picomc_thread = PicomcThread(cmd)
|
|
self.picomc_thread.output_received.connect(self._handle_output)
|
|
self.picomc_thread.game_launched.connect(self._stop_parsing_and_close_popup)
|
|
self.picomc_thread.error_occurred.connect(self.handle_error)
|
|
self.picomc_thread.start() # Start the thread
|
|
|
|
except Exception as e:
|
|
error_message = f"Error initializing launcher: {str(e)}"
|
|
print(f"[ERROR] {error_message}")
|
|
if self.parent_widget and hasattr(self.parent_widget, "showError"):
|
|
self.parent_widget.showError("Error", error_message)
|
|
|
|
def _handle_output(self, text):
|
|
"""Handles output from the picomc thread and updates the UI."""
|
|
if self.picomc_thread and self.picomc_thread.stop_parsing:
|
|
return # Stop if parsing is stopped
|
|
|
|
print(f"[PICOMC] {text}")
|
|
self.update_status_signal.emit(text) # Update status label
|
|
|
|
def _stop_parsing_and_close_popup(self):
|
|
"""Stops parsing and closes the popup dialog."""
|
|
print("[INFO] Stopping parsing, closing popup.")
|
|
if self.picomc_thread:
|
|
self.picomc_thread.stop_parsing = True
|
|
self.close_dialog_signal.emit() # Close the dialog
|
|
|
|
def _create_dialog(self):
|
|
"""Creates and shows the launch dialog."""
|
|
if self.launch_dialog is None:
|
|
print("[INFO] Creating launch dialog...")
|
|
self.launch_dialog = LaunchDialog(self.parent_widget)
|
|
self.launch_dialog.show()
|
|
|
|
def _close_dialog(self):
|
|
"""Closes the launch dialog."""
|
|
if self.launch_dialog:
|
|
print("[INFO] Closing dialog...")
|
|
self.launch_dialog.close()
|
|
self.launch_dialog = None
|
|
print("[INFO] Dialog closed.")
|
|
|
|
def _update_status(self, text):
|
|
"""Updates the status label in the launch dialog."""
|
|
if self.launch_dialog and self.launch_dialog.isVisible():
|
|
print(f"[INFO] Updating status: {text}")
|
|
self.launch_dialog.status_label.setText(text)
|
|
|
|
def handle_error(self, title, message):
|
|
"""Handles any error that occurs during the game launch."""
|
|
print(f"[ERROR] {title} - {message}")
|
|
if self.parent_widget and hasattr(self.parent_widget, "showError"):
|
|
self.parent_widget.showError(title, message)
|
|
self.close_dialog_signal.emit() # Close the dialog on error |