From 8f3e95b2a4f4da3cb150d8cbe8525162b2624ac8 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Sun, 23 Feb 2025 02:37:18 -0300
Subject: [PATCH] Update picodulce.py
---
picodulce.py | 234 +++++++++++++++++++++++++++++++--------------------
1 file changed, 141 insertions(+), 93 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index af49300..dd12b67 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -263,7 +263,7 @@ class PicomcVersionSelector(QWidget):
title_label.setFont(QFont("Arial", 24, QFont.Bold))
# Create installed versions section
- installed_versions_label = QLabel(self.tr('Installed Versions:'))
+ installed_versions_label = QLabel('Installed Versions:')
installed_versions_label.setFont(QFont("Arial", 14))
self.installed_version_combo = QComboBox()
self.installed_version_combo.setMinimumWidth(200)
@@ -273,32 +273,32 @@ class PicomcVersionSelector(QWidget):
buttons_layout = QVBoxLayout()
# Create play button for installed versions
- self.play_button = QPushButton(self.tr('Play'))
+ self.play_button = QPushButton('Play')
self.play_button.clicked.connect(self.play_instance)
highlight_color = self.palette().color(QPalette.Highlight)
self.play_button.setStyleSheet(f"background-color: {highlight_color.name()}; color: white;")
buttons_layout.addWidget(self.play_button)
# Version Manager Button
- self.open_menu_button = QPushButton(self.tr('Version Manager'))
+ self.open_menu_button = QPushButton('Version Manager')
self.open_menu_button.clicked.connect(self.open_mod_loader_and_version_menu)
buttons_layout.addWidget(self.open_menu_button)
# Create button to manage accounts
- self.manage_accounts_button = QPushButton(self.tr('Manage Accounts'))
+ self.manage_accounts_button = QPushButton('Manage Accounts')
self.manage_accounts_button.clicked.connect(self.manage_accounts)
buttons_layout.addWidget(self.manage_accounts_button)
# Create a button for the marroc mod loader
- self.open_marroc_button = QPushButton(self.tr('Marroc Mod Manager'))
+ self.open_marroc_button = QPushButton('Marroc Mod Manager')
self.open_marroc_button.clicked.connect(self.open_marroc_script)
buttons_layout.addWidget(self.open_marroc_button)
# Create grid layout for Settings and About buttons
grid_layout = QGridLayout()
- self.settings_button = QPushButton(self.tr('Settings'))
+ self.settings_button = QPushButton('Settings')
self.settings_button.clicked.connect(self.open_settings_dialog)
- self.about_button = QPushButton(self.tr('About'))
+ self.about_button = QPushButton('About')
self.about_button.clicked.connect(self.show_about_dialog)
grid_layout.addWidget(self.settings_button, 0, 0)
@@ -336,9 +336,53 @@ class PicomcVersionSelector(QWidget):
else:
super().keyPressEvent(event)
+ def check_config_file(self):
+ config_path = "config.json"
+ default_config = {
+ "IsRCPenabled": False,
+ "CheckUpdate": False,
+ "IsBleeding": False,
+ "LastPlayed": "",
+ "IsFirstLaunch": True,
+ "Instance": "default",
+ "Theme": "Dark.json",
+ "ThemeBackground": True,
+ "ThemeRepository": "https://raw.githubusercontent.com/nixietab/picodulce-themes/main/repo.json"
+ }
+
+ # Step 1: Check if the file exists; if not, create it with default values
+ if not os.path.exists(config_path):
+ with open(config_path, "w") as config_file:
+ json.dump(default_config, config_file, indent=4)
+ self.config = default_config
+ return
+
+ # Step 2: Try loading the config file, handle invalid JSON
+ try:
+ with open(config_path, "r") as config_file:
+ self.config = json.load(config_file)
+ except (json.JSONDecodeError, ValueError):
+ # File is corrupted, overwrite it with default configuration
+ with open(config_path, "w") as config_file:
+ json.dump(default_config, config_file, indent=4)
+ self.config = default_config
+ return
+
+ # Step 3: Check for missing keys and add defaults if necessary
+ updated = False
+ for key, value in default_config.items():
+ if key not in self.config: # Field is missing
+ self.config[key] = value
+ updated = True
+
+ # Step 4: Save the repaired config back to the file
+ if updated:
+ with open(config_path, "w") as config_file:
+ json.dump(self.config, config_file, indent=4)
+
def open_settings_dialog(self):
dialog = QDialog(self)
- dialog.setWindowTitle(self.tr('Settings'))
+ dialog.setWindowTitle('Settings')
# Make the window resizable
dialog.setMinimumSize(400, 300)
@@ -350,17 +394,17 @@ class PicomcVersionSelector(QWidget):
settings_tab = QWidget()
settings_layout = QVBoxLayout()
- title_label = QLabel(self.tr('Settings'))
+ title_label = QLabel('Settings')
title_label.setFont(QFont("Arial", 14))
# Create checkboxes for settings tab
- discord_rcp_checkbox = QCheckBox(self.tr('Discord Rich Presence'))
+ discord_rcp_checkbox = QCheckBox('Discord Rich Presence')
discord_rcp_checkbox.setChecked(self.config.get("IsRCPenabled", False))
- check_updates_checkbox = QCheckBox(self.tr('Check Updates on Start'))
+ check_updates_checkbox = QCheckBox('Check Updates on Start')
check_updates_checkbox.setChecked(self.config.get("CheckUpdate", False))
- bleeding_edge_checkbox = QCheckBox(self.tr('Bleeding Edge'))
+ bleeding_edge_checkbox = QCheckBox('Bleeding Edge')
bleeding_edge_checkbox.setChecked(self.config.get("IsBleeding", False))
bleeding_edge_checkbox.stateChanged.connect(lambda: self.show_bleeding_edge_popup(bleeding_edge_checkbox))
@@ -370,13 +414,13 @@ class PicomcVersionSelector(QWidget):
settings_layout.addWidget(bleeding_edge_checkbox)
# Add buttons in the settings tab
- update_button = QPushButton(self.tr('Check for updates'))
+ update_button = QPushButton('Check for updates')
update_button.clicked.connect(self.check_for_update)
- open_game_directory_button = QPushButton(self.tr('Open game directory'))
+ open_game_directory_button = QPushButton('Open game directory')
open_game_directory_button.clicked.connect(self.open_game_directory)
- stats_button = QPushButton(self.tr('Stats for Nerds'))
+ stats_button = QPushButton('Stats for Nerds')
stats_button.clicked.connect(self.show_system_info)
settings_layout.addWidget(update_button)
@@ -390,15 +434,15 @@ class PicomcVersionSelector(QWidget):
customization_layout = QVBoxLayout()
# Create theme background checkbox for customization tab
- theme_background_checkbox = QCheckBox(self.tr('Theme Background'))
+ theme_background_checkbox = QCheckBox('Theme Background')
theme_background_checkbox.setChecked(self.config.get("ThemeBackground", False))
# Label to show currently selected theme
theme_filename = self.config.get('Theme', 'Dark.json')
- current_theme_label = QLabel(self.tr(f"Current Theme: {theme_filename}"))
+ current_theme_label = QLabel(f"Current Theme: {theme_filename}")
# QListWidget to display available themes
- json_files_label = QLabel(self.tr('Installed Themes:'))
+ json_files_label = QLabel('Installed Themes:')
self.json_files_list_widget = QListWidget()
# Track selected theme
@@ -422,7 +466,7 @@ class PicomcVersionSelector(QWidget):
customization_layout.addWidget(self.json_files_list_widget)
# Button to download themes
- download_themes_button = QPushButton(self.tr("Download More Themes"))
+ download_themes_button = QPushButton("Download More Themes")
download_themes_button.clicked.connect(self.download_themes_window)
customization_layout.addWidget(download_themes_button)
@@ -430,11 +474,11 @@ class PicomcVersionSelector(QWidget):
customization_tab.setLayout(customization_layout)
# Add the tabs to the TabWidget
- tab_widget.addTab(settings_tab, self.tr("Settings"))
- tab_widget.addTab(customization_tab, self.tr("Customization"))
+ tab_widget.addTab(settings_tab, "Settings")
+ tab_widget.addTab(customization_tab, "Customization")
# Save button
- save_button = QPushButton(self.tr('Save'))
+ save_button = QPushButton('Save')
save_button.clicked.connect(
lambda: self.save_settings(
discord_rcp_checkbox.isChecked(),
@@ -513,13 +557,13 @@ class PicomcVersionSelector(QWidget):
selected_item = json_files_list_widget.currentItem()
if selected_item:
self.selected_theme = selected_item.data(Qt.UserRole)
- current_theme_label.setText(self.tr(f"Current Theme: {self.selected_theme}"))
+ current_theme_label.setText(f"Current Theme: {self.selected_theme}")
## REPOSITORY BLOCK BEGGINS
def download_themes_window(self):
dialog = QDialog(self)
- dialog.setWindowTitle(self.tr("Themes Repository"))
+ dialog.setWindowTitle("Themes Repository")
dialog.setGeometry(100, 100, 800, 600)
main_layout = QHBoxLayout(dialog)
@@ -541,7 +585,7 @@ class PicomcVersionSelector(QWidget):
self.image_label.setStyleSheet("padding: 10px;")
right_layout.addWidget(self.image_label)
- download_button = QPushButton(self.tr("Download Theme"), dialog)
+ download_button = QPushButton("Download Theme", dialog)
download_button.clicked.connect(self.theme_download)
right_layout.addWidget(download_button)
@@ -554,6 +598,7 @@ class PicomcVersionSelector(QWidget):
dialog.finished.connect(lambda: self.update_themes_list())
+
self.load_themes()
dialog.exec_()
@@ -567,18 +612,18 @@ class PicomcVersionSelector(QWidget):
config = json.load(config_file)
url = config.get("ThemeRepository")
if not url:
- raise ValueError(self.tr("ThemeRepository is not defined in config.json"))
+ raise ValueError("ThemeRepository is not defined in config.json")
response = requests.get(url)
response.raise_for_status()
return response.json()
except (FileNotFoundError, json.JSONDecodeError) as config_error:
- self.show_error_popup(self.tr("Error reading configuration"), self.tr(f"An error occurred while reading config.json: {config_error}"))
+ self.show_error_popup("Error reading configuration", f"An error occurred while reading config.json: {config_error}")
return {}
except requests.exceptions.RequestException as fetch_error:
- self.show_error_popup(self.tr("Error fetching themes"), self.tr(f"An error occurred while fetching themes: {fetch_error}"))
+ self.show_error_popup("Error fetching themes", f"An error occurred while fetching themes: {fetch_error}")
return {}
except ValueError as value_error:
- self.show_error_popup(self.tr("Configuration Error"), self.tr(str(value_error)))
+ self.show_error_popup("Configuration Error", str(value_error))
return {}
def download_theme_json(self, theme_url, theme_name):
@@ -590,15 +635,15 @@ class PicomcVersionSelector(QWidget):
theme_filename = os.path.join('themes', f'{theme_name}.json')
with open(theme_filename, 'wb') as f:
f.write(response.content)
- print(self.tr(f"Downloaded {theme_name} theme to {theme_filename}"))
+ print(f"Downloaded {theme_name} theme to {theme_filename}")
except requests.exceptions.RequestException as e:
- self.show_error_popup(self.tr("Error downloading theme"), self.tr(f"An error occurred while downloading {theme_name}: {e}"))
+ self.show_error_popup("Error downloading theme", f"An error occurred while downloading {theme_name}: {e}")
def show_error_popup(self, title, message):
msg = QMessageBox()
msg.setIcon(QMessageBox.Critical)
- msg.setWindowTitle(self.tr(title))
- msg.setText(self.tr(message))
+ msg.setWindowTitle(title)
+ msg.setText(message)
msg.exec_()
def is_theme_installed(self, theme_name):
@@ -612,7 +657,7 @@ class PicomcVersionSelector(QWidget):
for theme in themes:
theme_display_name = f"{theme['name']} by {theme['author']}"
if self.is_theme_installed(theme['name']):
- theme_display_name += self.tr(" [I]")
+ theme_display_name += " [I]"
installed_themes.append(theme_display_name)
else:
uninstalled_themes.append(theme_display_name)
@@ -632,11 +677,11 @@ class PicomcVersionSelector(QWidget):
theme = self.find_theme_by_name(theme_name)
if theme:
self.details_label.setText(
- self.tr(f"Name: {theme['name']}
"
- f"Description: {theme['description']}
"
- f"Author: {theme['author']}
"
- f"License: {theme['license']}
"
- f"Link: {theme['link']}
")
+ f"Name: {theme['name']}
"
+ f"Description: {theme['description']}
"
+ f"Author: {theme['author']}
"
+ f"License: {theme['license']}
"
+ f"Link: {theme['link']}
"
)
self.details_label.setTextFormat(Qt.RichText)
self.details_label.setOpenExternalLinks(True)
@@ -656,7 +701,7 @@ class PicomcVersionSelector(QWidget):
response.raise_for_status()
return response.content
except requests.exceptions.RequestException as e:
- self.show_error_popup(self.tr("Error fetching image"), self.tr(f"An error occurred while fetching the image: {e}"))
+ self.show_error_popup("Error fetching image", f"An error occurred while fetching the image: {e}")
return None
def find_theme_by_name(self, theme_name):
@@ -676,7 +721,7 @@ class PicomcVersionSelector(QWidget):
theme_url = theme["link"]
self.download_theme_json(theme_url, theme_name)
self.load_themes()
-
+
## REPOSITORY BLOCK ENDS
@@ -704,6 +749,20 @@ class PicomcVersionSelector(QWidget):
)
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):
# Get system information
java_version = subprocess.getoutput("java -version 2>&1 | head -n 1")
@@ -915,11 +974,11 @@ class PicomcVersionSelector(QWidget):
# Main account management dialog
dialog = QDialog(self)
self.open_dialogs.append(dialog)
- dialog.setWindowTitle(self.tr('Manage Accounts'))
+ dialog.setWindowTitle('Manage Accounts')
dialog.setFixedSize(400, 250)
# Title
- title_label = QLabel(self.tr('Manage Accounts'))
+ title_label = QLabel('Manage Accounts')
title_label.setFont(QFont("Arial", 14))
title_label.setAlignment(Qt.AlignCenter) # Center the text
# Dropdown for selecting accounts
@@ -927,17 +986,17 @@ class PicomcVersionSelector(QWidget):
self.populate_accounts(account_combo)
# Buttons
- create_account_button = QPushButton(self.tr('Create Account'))
+ create_account_button = QPushButton('Create Account')
create_account_button.clicked.connect(self.open_create_account_dialog)
- authenticate_button = QPushButton(self.tr('Authenticate Account'))
+ authenticate_button = QPushButton('Authenticate Account')
authenticate_button.clicked.connect(lambda: self.authenticate_account(dialog, account_combo.currentText()))
- remove_account_button = QPushButton(self.tr('Remove Account'))
+ remove_account_button = QPushButton('Remove Account')
remove_account_button.clicked.connect(lambda: self.remove_account(dialog, account_combo.currentText()))
# New button to set the account idk
- set_default_button = QPushButton(self.tr('Select'))
+ set_default_button = QPushButton('Select')
set_default_button.setFixedWidth(100) # Set button width to a quarter
set_default_button.clicked.connect(lambda: self.set_default_account(account_combo.currentText(), dialog))
@@ -965,15 +1024,15 @@ class PicomcVersionSelector(QWidget):
# Dialog for creating a new account
dialog = QDialog(self)
self.open_dialogs.append(dialog)
- dialog.setWindowTitle(self.tr('Create Account'))
+ dialog.setWindowTitle('Create Account')
dialog.setFixedSize(300, 150)
username_input = QLineEdit()
- username_input.setPlaceholderText(self.tr('Enter Username'))
+ username_input.setPlaceholderText('Enter Username')
- microsoft_checkbox = QCheckBox(self.tr('Microsoft Account'))
+ microsoft_checkbox = QCheckBox('Microsoft Account')
- create_button = QPushButton(self.tr('Create'))
+ create_button = QPushButton('Create')
create_button.clicked.connect(lambda: self.create_account(dialog, username_input.text(), microsoft_checkbox.isChecked()))
layout = QVBoxLayout()
@@ -990,12 +1049,11 @@ class PicomcVersionSelector(QWidget):
username = username.strip()
if not username:
- QMessageBox.warning(dialog, self.tr("Warning"), self.tr("Username cannot be blank."))
+ QMessageBox.warning(dialog, "Warning", "Username cannot be blank.")
return
if not self.is_valid_username(username):
- QMessageBox.warning(dialog, self.tr("Warning"),
- self.tr("Invalid username. Usernames must be 3-16 characters long and can only contain letters, numbers, and underscores."))
+ QMessageBox.warning(dialog, "Warning", "Invalid username. Usernames must be 3-16 characters long and can only contain letters, numbers, and underscores.")
return
try:
@@ -1004,14 +1062,13 @@ class PicomcVersionSelector(QWidget):
command.append('--ms')
subprocess.run(command, check=True)
- QMessageBox.information(dialog, self.tr("Success"),
- self.tr("Account '{username}' created successfully!").format(username=username))
+ QMessageBox.information(dialog, "Success", f"Account '{username}' created successfully!")
self.populate_accounts_for_all_dialogs()
dialog.accept()
except subprocess.CalledProcessError as e:
- error_message = self.tr("Error creating account: {error}").format(error=e.stderr.decode())
+ error_message = f"Error creating account: {e.stderr.decode()}"
logging.error(error_message)
- QMessageBox.critical(dialog, self.tr("Error"), error_message)
+ QMessageBox.critical(dialog, "Error", error_message)
def is_valid_username(self, username):
# Validate the username according to Minecraft's rules
@@ -1023,8 +1080,7 @@ class PicomcVersionSelector(QWidget):
# Clean up the account name
account_name = account_name.strip().lstrip(" * ")
if not account_name:
- QMessageBox.warning(dialog, self.tr("Warning"),
- self.tr("Please select an account to authenticate."))
+ QMessageBox.warning(dialog, "Warning", "Please select an account to authenticate.")
return
try:
@@ -1037,18 +1093,15 @@ class PicomcVersionSelector(QWidget):
self.authenticator.authenticate(account_name)
except Exception as e:
- error_message = self.tr("Error authenticating account '{account}': {error}").format(
- account=account_name, error=str(e))
+ error_message = f"Error authenticating account '{account_name}': {str(e)}"
logging.error(error_message)
- QMessageBox.critical(dialog, self.tr("Error"), error_message)
+ QMessageBox.critical(dialog, "Error", error_message)
def _on_auth_finished(self, success):
if success:
- QMessageBox.information(self, self.tr("Success"),
- self.tr("Account authenticated successfully!"))
+ QMessageBox.information(self, "Success", "Account authenticated successfully!")
else:
- QMessageBox.critical(self, self.tr("Error"),
- self.tr("Failed to authenticate account"))
+ QMessageBox.critical(self, "Error", "Failed to authenticate account")
# Cleanup
if self.authenticator:
@@ -1059,23 +1112,20 @@ class PicomcVersionSelector(QWidget):
# Remove a selected account
username = username.strip().lstrip(" * ")
if not username:
- QMessageBox.warning(dialog, self.tr("Warning"),
- self.tr("Please select an account to remove."))
+ QMessageBox.warning(dialog, "Warning", "Please select an account to remove.")
return
- confirm_message = self.tr("Are you sure you want to remove the account '{username}'?\nThis action cannot be undone.").format(username=username)
- confirm_dialog = QMessageBox.question(dialog, self.tr("Confirm Removal"),
- confirm_message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
+ confirm_message = f"Are you sure you want to remove the account '{username}'?\nThis action cannot be undone."
+ confirm_dialog = QMessageBox.question(dialog, "Confirm Removal", confirm_message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if confirm_dialog == QMessageBox.Yes:
try:
subprocess.run(['picomc', 'account', 'remove', username], check=True)
- QMessageBox.information(dialog, self.tr("Success"),
- self.tr("Account '{username}' removed successfully!").format(username=username))
+ QMessageBox.information(dialog, "Success", f"Account '{username}' removed successfully!")
self.populate_accounts_for_all_dialogs()
except subprocess.CalledProcessError as e:
- error_message = self.tr("Error removing account: {error}").format(error=e.stderr.decode())
+ error_message = f"Error removing account: {e.stderr.decode()}"
logging.error(error_message)
- QMessageBox.critical(dialog, self.tr("Error"), error_message)
+ QMessageBox.critical(dialog, "Error", error_message)
def populate_accounts(self, account_combo):
@@ -1160,18 +1210,16 @@ class PicomcVersionSelector(QWidget):
if is_bleeding and version_bleeding:
version_number = version_bleeding
- # Create the about message without translation for the version placeholder
- about_message = self.tr(
- "PicoDulce Launcher (v{0})\n\n"
+ about_message = (
+ f"PicoDulce Launcher (v{version_number})\n\n"
"A simple Minecraft launcher built using Qt, based on the picomc project.\n\n"
"Credits:\n"
"Nixietab: Code and UI design\n"
"Wabaano: Graphic design\n"
"Olinad: Christmas!!!!"
- ).format(version_number)
-
- QMessageBox.about(self, self.tr("About"), about_message)
-
+ )
+ QMessageBox.about(self, "About", about_message)
+
def check_for_update(self):
self.update_checker.check_for_update()
@@ -1509,7 +1557,7 @@ class ModLoaderAndVersionMenu(QDialog):
layout = QVBoxLayout(install_mod_tab)
# Create title label
- title_label = QLabel(self.tr('Mod Loader Installer'))
+ title_label = QLabel('Mod Loader Installer')
title_label.setFont(QFont("Arial", 14))
layout.addWidget(title_label)
@@ -1534,7 +1582,7 @@ class ModLoaderAndVersionMenu(QDialog):
self.fabric_checkbox.clicked.connect(update_versions)
# Create install button
- install_button = QPushButton(self.tr('Install'))
+ install_button = QPushButton('Install')
install_button.clicked.connect(lambda: self.install_mod_loader(
self.version_combo_mod.currentText(),
self.forge_checkbox.isChecked(),
@@ -1546,15 +1594,15 @@ class ModLoaderAndVersionMenu(QDialog):
layout = QVBoxLayout(download_version_tab)
# Create title label
- title_label = QLabel(self.tr('Download Version'))
+ title_label = QLabel('Download Version')
title_label.setFont(QFont("Arial", 14))
layout.addWidget(title_label)
# Create checkboxes for different version types
- self.release_checkbox = QCheckBox(self.tr('Releases'))
- self.snapshot_checkbox = QCheckBox(self.tr('Snapshots'))
- self.alpha_checkbox = QCheckBox(self.tr('Alpha'))
- self.beta_checkbox = QCheckBox(self.tr('Beta'))
+ self.release_checkbox = QCheckBox('Releases')
+ self.snapshot_checkbox = QCheckBox('Snapshots')
+ self.alpha_checkbox = QCheckBox('Alpha')
+ self.beta_checkbox = QCheckBox('Beta')
layout.addWidget(self.release_checkbox)
layout.addWidget(self.snapshot_checkbox)
layout.addWidget(self.alpha_checkbox)
@@ -1614,10 +1662,10 @@ class ModLoaderAndVersionMenu(QDialog):
def show_popup(self):
self.popup = QDialog(self)
- self.popup.setWindowTitle(self.tr("Installing Version"))
+ self.popup.setWindowTitle("Installing Version")
layout = QVBoxLayout(self.popup)
- label = QLabel(self.tr("The version is being installed..."))
+ label = QLabel("The version is being installed...")
layout.addWidget(label)
movie = QMovie("drums.gif")
@@ -1671,7 +1719,7 @@ class ModLoaderAndVersionMenu(QDialog):
def install_mod_loader(self, version, install_forge, install_fabric):
if not install_forge and not install_fabric:
- QMessageBox.warning(self, self.tr("Select Mod Loader"), self.tr("Please select at least one mod loader."))
+ QMessageBox.warning(self, "Select Mod Loader", "Please select at least one mod loader.")
return
mod_loader = None