mirror of
https://github.com/nixietab/picodulce.git
synced 2025-04-03 15:08:58 +01:00
added better user feedback starting a version
This commit is contained in:
parent
ba8072c669
commit
0243aa60a7
175
gamerun.py
Normal file
175
gamerun.py
Normal file
@ -0,0 +1,175 @@
|
||||
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
|
35
picodulce.py
35
picodulce.py
@ -13,10 +13,11 @@ import time
|
||||
|
||||
from authser import MinecraftAuthenticator
|
||||
from healthcheck import HealthCheck
|
||||
from gamerun import MinecraftLauncher
|
||||
|
||||
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.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, QTimer
|
||||
from datetime import datetime
|
||||
|
||||
logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
@ -35,6 +36,10 @@ class PicomcVersionSelector(QWidget):
|
||||
themes_folder = "themes"
|
||||
theme_file = self.config.get("Theme", "Dark.json")
|
||||
|
||||
# Set up the minecraft runner
|
||||
self.gamerun = MinecraftLauncher()
|
||||
self.gamerun.set_parent_widget(self)
|
||||
|
||||
# Ensure the theme file exists in the themes directory
|
||||
theme_file_path = os.path.join(themes_folder, theme_file)
|
||||
|
||||
@ -847,29 +852,33 @@ class PicomcVersionSelector(QWidget):
|
||||
self.current_state = selected_instance
|
||||
|
||||
# Read the config.json to get the "Instance" value
|
||||
with open('config.json', 'r') as config_file:
|
||||
config = json.load(config_file)
|
||||
instance_value = config.get("Instance", "default") # Default to "default" if not found
|
||||
try:
|
||||
with open('config.json', 'r') as config_file:
|
||||
config = json.load(config_file)
|
||||
instance_value = config.get("Instance", "default")
|
||||
except Exception as e:
|
||||
logging.error(f"Error reading config.json: {e}")
|
||||
self.showError("Error", "Could not read configuration file")
|
||||
return
|
||||
|
||||
# Update lastplayed field in config.json on a separate thread
|
||||
update_thread = threading.Thread(target=self.update_last_played, args=(selected_instance,))
|
||||
update_thread.start()
|
||||
|
||||
# Run the game subprocess with the instance_value from config.json
|
||||
subprocess.run(['picomc', 'instance', 'launch', '--version-override', selected_instance, instance_value], check=True)
|
||||
# Prepare the command
|
||||
cmd = ['picomc', 'instance', 'launch', '--version-override', selected_instance, instance_value]
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
# Launch the game using the MinecraftLauncher
|
||||
self.gamerun.launch_game(cmd)
|
||||
|
||||
except Exception as e:
|
||||
error_message = f"Error playing {selected_instance}: {e}"
|
||||
logging.error(error_message)
|
||||
# Use QMetaObject.invokeMethod to call showError safely
|
||||
QMetaObject.invokeMethod(
|
||||
self, "showError", Qt.QueuedConnection,
|
||||
Q_ARG(str, "Error"), Q_ARG(str, error_message)
|
||||
)
|
||||
self.showError("Error", error_message)
|
||||
finally:
|
||||
# Reset current_state to "menu" after the game closes
|
||||
self.current_state = "menu"
|
||||
|
||||
|
||||
def update_last_played(self, selected_instance):
|
||||
config_path = "config.json"
|
||||
self.config["LastPlayed"] = selected_instance
|
||||
|
Loading…
Reference in New Issue
Block a user